fix: fix code review notes

This commit is contained in:
Ivan Smolin 2023-07-24 20:15:06 +03:00
parent 4e8f60543d
commit ba4cdc6e0e
33 changed files with 694 additions and 367 deletions

View File

@ -7,4 +7,5 @@ target 'TIBottomSheet' do
pod 'TIUIElements', :path => '../../../../TIUIElements/TIUIElements.podspec'
pod 'TIUIKitCore', :path => '../../../../TIUIKitCore/TIUIKitCore.podspec'
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
pod 'TIBottomSheet', :path => '../../../../TIBottomSheet/TIBottomSheet.podspec'
end

View File

@ -35,10 +35,120 @@ open class BaseModalViewController<ContentView: UIView,
private(set) public lazy var contentView = createContentView()
private(set) public lazy var footerView = createFooterView()
public var dragViewConstraints: SubviewConstraints?
public var headerViewConstraints: SubviewConstraints?
public var contentViewConstraints: SubviewConstraints?
public var footerViewConstraints: SubviewConstraints?
public private(set) lazy var dragViewBottomToHeaderViewTopConstraint: NSLayoutConstraint = {
dragView.bottomAnchor.constraint(equalTo: headerView.topAnchor)
}()
public private(set) lazy var dragViewBottomToContentViewTopConstraint: NSLayoutConstraint = {
dragView.bottomAnchor.constraint(equalTo: contentView.topAnchor)
}()
public private(set) lazy var dragViewConstraints: SubviewConstraints = {
let trailingConstraint = dragView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: dragView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
trailingConstraint: trailingConstraint,
topConstraint: dragView.topAnchor.constraint(equalTo: view.topAnchor),
bottomConstraint: dragViewBottomToHeaderViewTopConstraint)
let centerXConstraint = dragView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let centerYConstraint = dragView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: dragView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: dragView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public private(set) lazy var headerViewToSuperviewTopConstraint: NSLayoutConstraint = {
headerView.topAnchor.constraint(equalTo: view.topAnchor)
}()
public private(set) lazy var headerBottomToContentTopConstraint: NSLayoutConstraint = {
headerView.bottomAnchor.constraint(equalTo: contentView.topAnchor)
}()
public private(set) lazy var headerViewConstraints: SubviewConstraints = {
let leadingConstraint = headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
let trailingConstraint = headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint,
trailingConstraint: trailingConstraint,
topConstraint: dragViewBottomToHeaderViewTopConstraint,
bottomConstraint: headerBottomToContentTopConstraint)
let centerXConstraint = headerView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let centerYConstraint = headerView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: headerView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: headerView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public private(set) lazy var contentViewTopToSuperviewConstraint: NSLayoutConstraint = {
contentView.topAnchor.constraint(equalTo: view.topAnchor)
}()
public private(set) lazy var contentViewBottomToSuperviewConstraint: NSLayoutConstraint = {
contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
}()
public private(set) lazy var contentViewConstraints: SubviewConstraints = {
let leadingConstraint = contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
let trailingConstraint = contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint,
trailingConstraint: trailingConstraint,
topConstraint: headerBottomToContentTopConstraint,
bottomConstraint: contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor))
let centerXConstraint = contentView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let centerYConstraint = contentView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: contentView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: contentView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public private(set) lazy var footerViewConstraints: SubviewConstraints = {
let leadingConstraint = footerView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
let trailingConstraint = footerView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint,
trailingConstraint: trailingConstraint,
topConstraint: footerView.topAnchor.constraint(equalTo: contentView.bottomAnchor),
bottomConstraint: footerView.bottomAnchor.constraint(equalTo: view.bottomAnchor))
let centerXConstraint = footerView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let centerYConstraint = footerView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: footerView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: footerView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public var keyboardDidShownObserver: NSObjectProtocol?
public var keyboardDidHiddenObserver: NSObjectProtocol?
@ -117,6 +227,10 @@ open class BaseModalViewController<ContentView: UIView,
open override func configureLayout() {
super.configureLayout()
for view in [dragView, headerView, contentView, footerView] {
view.translatesAutoresizingMaskIntoConstraints = false
}
configureDragViewLayout()
configureHeaderViewLayout()
configureContentViewLayout()
@ -161,7 +275,7 @@ open class BaseModalViewController<ContentView: UIView,
}
open func configureLayoutForKeyboard(_ notification: Notification, isKeyboardHidden: Bool) {
guard let height = getKeyboardHeight(notification) else {
guard let keyboardHeight = getKeyboardHeight(notification) else {
return
}
@ -169,11 +283,14 @@ open class BaseModalViewController<ContentView: UIView,
if isKeyboardHidden {
adjustScrollViewIfNeeded()
} else {
panScrollable.contentInset = .vertical(bottom: height)
panScrollable.contentInset = .vertical(bottom: keyboardHeight)
}
} else if case let .presented(footerViewAppearance) = viewControllerAppearance.footerViewState {
let bottomInset = footerViewAppearance.layout.insets.add(\.bottom,
to: \.bottom,
of: .vertical(bottom: keyboardHeight))
} else {
contentViewConstraints?.edgeConstraints.update(from: isKeyboardHidden ? .zero : .vertical(height))
footerViewConstraints.edgeConstraints.bottomConstraint.constant = bottomInset
}
}
@ -182,8 +299,8 @@ open class BaseModalViewController<ContentView: UIView,
return
}
let verticalInsets = appearance.layout.insets.vertical
let subviewVerticalInsets = appearance.subviewAppearance.layout.insets.vertical
let verticalInsets = appearance.layout.insets.vertical(onNan: .zero)
let subviewVerticalInsets = appearance.subviewAppearance.layout.insets.vertical(onNan: .zero)
let totalHeight = getHeight(of: footerView) + verticalInsets + subviewVerticalInsets
panScrollable.contentInset = .vertical(bottom: totalHeight)
@ -192,93 +309,92 @@ open class BaseModalViewController<ContentView: UIView,
// MARK: - Private Methods
private func configureDragViewLayout() {
guard case let .presented(appearance) = viewControllerAppearance.dragViewState else {
guard case let .presented(dragViewAppearance) = viewControllerAppearance.dragViewState else {
return
}
dragView.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint: NSLayoutConstraint
let bottomConstant: CGFloat
let dragViewConstraints = SubviewConstraints(
edgeConstraints: .init(topConstraint: dragView.topAnchor.constraint(equalTo: view.topAnchor)),
centerConstraints: .init(centerXConstraint: dragView.centerXAnchor.constraint(equalTo: view.centerXAnchor)),
sizeConstraints: .init(widthConstraint: dragView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: dragView.heightAnchor.constraint(equalToConstant: .zero)))
if case let .presented(headerViewAppearance) = viewControllerAppearance.headerViewState {
dragViewBottomToContentViewTopConstraint.isActive = false
bottomConstraint = dragViewBottomToHeaderViewTopConstraint
bottomConstant = dragViewAppearance.layout.insets.add(\.bottom,
to: \.top,
of: headerViewAppearance.layout.insets)
} else {
dragViewBottomToHeaderViewTopConstraint.isActive = false
bottomConstraint = dragViewBottomToContentViewTopConstraint
bottomConstant = dragViewAppearance.layout.insets.bottom
}
self.dragViewConstraints = dragViewConstraints
dragViewConstraints.edgeConstraints.bottomConstraint = bottomConstraint
dragViewConstraints.update(from: dragViewAppearance.layout)
appearance.layout.update(constraints: dragViewConstraints)
NSLayoutConstraint.activate(dragViewConstraints.centerConstraints.allConstraints)
bottomConstraint.setActiveConstantOrDeactivate(constant: bottomConstant)
}
private func configureHeaderViewLayout() {
guard case let .presented(appearance) = viewControllerAppearance.headerViewState else {
guard case let .presented(headerViewAppearance) = viewControllerAppearance.headerViewState else {
return
}
headerView.translatesAutoresizingMaskIntoConstraints = false
let topConstraint: NSLayoutConstraint
let topConstant: CGFloat
let isTopView = dragViewConstraints == nil
let headerViewConstraints = SubviewConstraints(
edgeConstraints: .init(
leadingConstraint: headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
trailingConstraint: headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
topConstraint: headerView.topAnchor.constraint(equalTo: isTopView ? view.topAnchor : dragView.bottomAnchor)),
centerConstraints: .init(),
sizeConstraints: .init(widthConstraint: headerView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: headerView.heightAnchor.constraint(equalToConstant: .zero)))
self.headerViewConstraints = headerViewConstraints
if case let .presented(dragViewAppearance) = viewControllerAppearance.dragViewState {
dragViewBottomToContentViewTopConstraint.isActive = false
topConstraint = dragViewBottomToHeaderViewTopConstraint
appearance.layout.update(constraints: headerViewConstraints)
topConstant = dragViewAppearance.layout.insets.add(\.bottom,
to: \.top,
of: headerViewAppearance.layout.insets)
} else {
dragViewBottomToHeaderViewTopConstraint.isActive = false
topConstraint = dragViewBottomToContentViewTopConstraint
topConstant = headerViewAppearance.layout.insets.top
}
headerViewConstraints.edgeConstraints.topConstraint = topConstraint
headerViewConstraints.update(from: headerViewAppearance.layout)
topConstraint.setActiveConstantOrDeactivate(constant: topConstant)
}
private func configureContentViewLayout() {
contentView.translatesAutoresizingMaskIntoConstraints = false
let topConstraint: NSLayoutConstraint
let topConstant: CGFloat
var topView: UIView
if case let .presented(headerViewAppearance) = viewControllerAppearance.headerViewState {
dragViewBottomToContentViewTopConstraint.isActive = false
contentViewTopToSuperviewConstraint.isActive = false
if headerViewConstraints == nil {
if dragViewConstraints == nil {
topView = view
} else {
topView = dragView
}
topConstraint = headerBottomToContentTopConstraint
topConstant = headerViewAppearance.layout.insets.bottom
} else if case let .presented(dragViewAppearance) = viewControllerAppearance.dragViewState {
contentViewTopToSuperviewConstraint.isActive = false
headerBottomToContentTopConstraint.isActive = false
topConstraint = dragViewBottomToContentViewTopConstraint
topConstant = dragViewAppearance.layout.insets.bottom
} else {
topView = headerView
headerBottomToContentTopConstraint.isActive = false
dragViewBottomToContentViewTopConstraint.isActive = false
topConstraint = contentViewTopToSuperviewConstraint
topConstant = .zero
}
let contentViewConstraints = SubviewConstraints(
edgeConstraints: .init(
leadingConstraint: contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
trailingConstraint: contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
topConstraint: contentView.topAnchor.constraint(equalTo: topView.bottomAnchor),
bottomConstraint: contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor)),
centerConstraints: .init(),
sizeConstraints: .init())
self.contentViewConstraints = contentViewConstraints
NSLayoutConstraint.activate(contentViewConstraints.allConstraints)
topConstraint.setActiveConstantOrDeactivate(constant: topConstant)
contentViewConstraints.activate()
}
private func configureFooterViewLayout() {
guard case let .presented(appearance) = viewControllerAppearance.footerViewState else {
guard case let .presented(footerViewAppearance) = viewControllerAppearance.footerViewState else {
return
}
footerView.translatesAutoresizingMaskIntoConstraints = false
let footerViewConstraints = SubviewConstraints(
edgeConstraints: .init(
leadingConstraint: footerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
trailingConstraint: footerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
bottomConstraint: footerView.bottomAnchor.constraint(equalTo: view.bottomAnchor)),
centerConstraints: .init(),
sizeConstraints: .init(widthConstraint: footerView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: footerView.heightAnchor.constraint(equalToConstant: .zero)))
self.footerViewConstraints = footerViewConstraints
appearance.layout.update(constraints: footerViewConstraints)
footerViewConstraints.update(from: footerViewAppearance.layout)
}
private func configureDragViewAppearance() {
@ -307,7 +423,7 @@ open class BaseModalViewController<ContentView: UIView,
footerView.isHidden = true
case let .presented(appearance):
footerView.configureAppearance(appearance: appearance)
footerView.configureBaseWrappedViewHolder(appearance: appearance)
}
}
@ -328,7 +444,7 @@ open class BaseModalViewController<ContentView: UIView,
return .zero
}
return appearance.layout.insets.vertical
return appearance.layout.insets.vertical(onNan: .zero)
}
private func getHeaderViewVerticalInsets() -> CGFloat {
@ -336,7 +452,7 @@ open class BaseModalViewController<ContentView: UIView,
return .zero
}
return appearance.layout.insets.vertical
return appearance.layout.insets.vertical(onNan: .zero)
}
private func getFittingSize(forView view: UIView) -> CGSize {

View File

@ -26,16 +26,16 @@ public struct ModalViewPresentationDetent: Hashable {
// MARK: - Default Values
public static var headerOnly: ModalViewPresentationDetent {
ModalViewPresentationDetent(height: CGFloat(Int.min))
public static var headerOnly: Self {
Self(height: -.greatestFiniteMagnitude)
}
public static func height(_ height: CGFloat) -> ModalViewPresentationDetent {
ModalViewPresentationDetent(height: height)
public static func height(_ height: CGFloat) -> Self {
Self(height: height)
}
public static var maxHeight: ModalViewPresentationDetent {
ModalViewPresentationDetent(height: CGFloat(Int.max))
public static var maxHeight: Self {
Self(height: .greatestFiniteMagnitude)
}
// MARK: - Public Properties

View File

@ -32,12 +32,6 @@ public extension ModalFooterView {
enum State {
case hidden
case presented(UIView.BaseWrappedViewHolderAppearance<UIView.DefaultWrappedAppearance, UIView.DefaultWrappedLayout>)
}
func configureAppearance(appearance: UIView.BaseWrappedViewHolderAppearance<UIView.DefaultWrappedAppearance, UIView.DefaultWrappedLayout>) {
wrappedView.configureUIView(appearance: appearance.subviewAppearance)
configureUIView(appearance: appearance)
contentInsets = appearance.subviewAppearance.layout.insets
case presented(BaseWrappedViewHolderAppearance<DefaultWrappedAppearance, DefaultWrappedLayout>)
}
}

View File

@ -34,7 +34,6 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
}
public enum ContentViewState {
case none
case buttonLeft(BaseButtonStyle)
case buttonRight(BaseButtonStyle)
case buttons(left: BaseButtonStyle, right: BaseButtonStyle)
@ -46,24 +45,60 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
public let leftButton = StatefulButton()
public let rightButton = StatefulButton()
public private(set) lazy var leftTrailingToRightLeadingConstraint: NSLayoutConstraint = {
leftButton.trailingAnchor.constraint(equalTo: rightButton.leadingAnchor)
}()
public private(set) lazy var leftTrailingToSuperviewTrailing: NSLayoutConstraint = {
leftButton.trailingAnchor.constraint(equalTo: rightButton.leadingAnchor)
}()
public private(set) lazy var leftButtonConstraints: SubviewConstraints = {
SubviewConstraints(
edgeConstraints: .init(
leadingConstraint: leftButton.leadingAnchor.constraint(equalTo: leadingAnchor),
topConstraint: leftButton.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: leftButton.bottomAnchor.constraint(equalTo: bottomAnchor)),
centerConstraints: .init(centerYConstraint: leftButton.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: .init())
let edgeConstraints = EdgeConstraints(leadingConstraint: leftButton.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: leftTrailingToRightLeadingConstraint,
topConstraint: leftButton.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: leftButton.bottomAnchor.constraint(equalTo: bottomAnchor))
let centerXConstraint = leftButton.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = leftButton.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: leftButton.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: leftButton.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public private(set) lazy var rightLeadingToSuperviewLeadingConstraint: NSLayoutConstraint = {
rightButton.leadingAnchor.constraint(equalTo: leadingAnchor)
}()
public private(set) lazy var rightButtonConstraints: SubviewConstraints = {
SubviewConstraints(
edgeConstraints: .init(
trailingConstraint: rightButton.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: rightButton.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: rightButton.bottomAnchor.constraint(equalTo: bottomAnchor)),
centerConstraints: .init(centerYConstraint: rightButton.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: .init())
let trailingConstraint = rightButton.trailingAnchor.constraint(equalTo: trailingAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: rightButton.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: trailingConstraint,
topConstraint: rightButton.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: rightButton.bottomAnchor.constraint(equalTo: bottomAnchor))
let centerXConstraint = rightButton.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = rightButton.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: rightButton.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: rightButton.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public var customViewConstraints: SubviewConstraints?
// MARK: - BaseInitializableView
@ -79,8 +114,6 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
[leftButton, rightButton]
.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
NSLayoutConstraint.activate(leftButtonConstraints.allConstraints + rightButtonConstraints.allConstraints)
}
// MARK: - AppearanceConfigurable
@ -98,20 +131,27 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
switch state {
case let .buttonLeft(style):
leftButtonStyle = style
leftButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToSuperviewTrailing
case let .buttonRight(style):
rightButtonStyle = style
rightButtonConstraints.edgeConstraints.leadingConstraint = rightLeadingToSuperviewLeadingConstraint
case let .buttons(left, right):
leftButtonStyle = left
rightButtonStyle = right
leftButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToRightLeadingConstraint
rightButtonConstraints.edgeConstraints.leadingConstraint = leftTrailingToRightLeadingConstraint
let spacing = left.appearance.layout.insets.add(\.right,
to: \.left,
of: right.appearance.layout.insets)
leftTrailingToRightLeadingConstraint.setActiveConstantOrDeactivate(constant: spacing)
case let .custom(view, appearance):
configureCustomView(view, withLayout: appearance.layout)
view.configureUIView(appearance: appearance)
default:
break
}
configure(buttonStyle: leftButtonStyle, forButton: leftButton, constraints: leftButtonConstraints)
@ -125,21 +165,24 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
view.translatesAutoresizingMaskIntoConstraints = false
let customViewConstraints = SubviewConstraints(
edgeConstraints: .init(
leadingConstraint: view.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: view.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: view.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: view.bottomAnchor.constraint(equalTo: bottomAnchor)),
centerConstraints: .init(centerXConstraint: view.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: view.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: .init())
let edgeConstraints = EdgeConstraints(leadingConstraint: view.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: view.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: view.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: view.bottomAnchor.constraint(equalTo: bottomAnchor))
let centerConstraints = CenterConstraints(centerXConstraint: view.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: view.centerYAnchor.constraint(equalTo: centerYAnchor))
let sizeConstraints = SizeConstraints(widthConstraint: view.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: view.heightAnchor.constraint(equalToConstant: .zero))
let customViewConstraints = SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
self.customViewConstraints = customViewConstraints
NSLayoutConstraint.deactivate(customViewConstraints.centerConstraints.allConstraints)
layout.update(constraints: customViewConstraints)
customViewConstraints.update(from: layout)
}
private func configure(buttonStyle: BaseButtonStyle?,
@ -153,9 +196,7 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
button.isHidden = false
if let layout = buttonStyle.appearance[.normal]?.layout, let constraints {
layout.update(constraints: constraints)
}
constraints?.update(from: buttonStyle.appearance.layout)
button.apply(style: buttonStyle)
}
@ -163,20 +204,20 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable {
// MARK: - Appearance
extension ModalHeaderView {
public extension ModalHeaderView {
public final class Appearance: UIView.BaseWrappedAppearance<UIView.DefaultWrappedLayout>, WrappedViewAppearance {
final class Appearance: BaseWrappedAppearance<DefaultWrappedLayout>, WrappedViewAppearance {
public static var defaultAppearance: Self {
.init()
Self()
}
public var contentViewState: ContentViewState
public init(layout: UIView.DefaultWrappedLayout = .defaultLayout,
public init(layout: DefaultWrappedLayout = .defaultLayout,
backgroundColor: UIColor = .clear,
border: UIViewBorder = .init(),
shadow: UIViewShadow? = nil,
contentViewState: ContentViewState = .none) {
contentViewState: ContentViewState = .buttonLeft(.init())) {
self.contentViewState = contentViewState

View File

@ -7,4 +7,5 @@ target 'TIBottomSheet' do
pod 'TIUIElements', :path => '../../../../TIUIElements/TIUIElements.podspec'
pod 'TIUIKitCore', :path => '../../../../TIUIKitCore/TIUIKitCore.podspec'
pod 'TISwiftUtils', :path => '../../../../TISwiftUtils/TISwiftUtils.podspec'
pod 'TIBottomSheet', :path => '../../../../TIBottomSheet/TIBottomSheet.podspec'
end

View File

@ -56,7 +56,9 @@ class CustomViewController: BaseModalViewController<UIView, UIView> {
$0.layout.size = .fixedHeight(52)
$0.backgroundColor = .white
$0.contentViewState = .buttonLeft(.init(titles: [.normal: "Close"],
appearance: [.normal: .init(backgroundColor: .blue)]))
appearance: .init(stateAppearances: [
.normal: .init(backgroundColor: .blue)
])))
})
return appearance

View File

@ -11,12 +11,12 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '11.0'
s.swift_versions = ['5.7']
sources = '/Sources/**/*'
sources = 'Sources/**/*'
if ENV["DEVELOPMENT_INSTALL"] # installing using :path =>
s.source_files = sources
s.exclude_files = s.name + '.app'
else
s.source_files = s.name + sources
s.source_files = s.name + '/' + sources
s.exclude_files = s.name + '/*.app'
end

View File

@ -60,7 +60,7 @@ open class DefaultFilterTableViewCell: ContainerTableViewCell<DefaultPickerView>
// MARK: - Open methods
open func updateAppearance(with appearance: FilterCellStateAppearance) {
contentInsets = appearance.contentInsets
wrappedContentInsets = appearance.contentInsets
wrappedView.textColor = appearance.fontColor
wrappedView.images = appearance.stateImages ?? [:]

View File

@ -56,7 +56,7 @@ open class DefaultFilterCollectionCell: ContainerCollectionViewCell<UILabel>, Co
// MARK: - Open methdos
open func updateAppearance(with appearance: FilterCellStateAppearance) {
contentInsets = appearance.contentInsets
wrappedContentInsets = appearance.contentInsets
wrappedView.textColor = appearance.fontColor
backgroundColor = appearance.backgroundColor

View File

@ -20,32 +20,51 @@
// THE SOFTWARE.
//
import TIUIKitCore
import UIKit
public final class ScrollViewWrapper<ContentView: UIView>: UIScrollView {
open class BaseInitializeableScrollView: UIScrollView, InitializableViewProtocol {
public var callbacks: [ViewCallbacks] = []
private let contentView: ContentView
override public init(frame: CGRect) {
super.init(frame: frame)
public override init(frame: CGRect) {
self.contentView = ContentView()
super.init(frame: .zero)
addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
contentView.topAnchor.constraint(equalTo: topAnchor),
contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
contentView.widthAnchor.constraint(equalTo: widthAnchor)
])
initializeView()
}
required init?(coder: NSCoder) {
contentView = ContentView()
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.init(coder: coder)
initializeView()
}
override open func layoutSubviews() {
super.layoutSubviews()
for callback in callbacks {
callback.onDidLayoutSubviews()
}
}
// MARK: - InitializableView
open func addViews() {
// override in subclass
}
open func configureLayout() {
// override in subclass
}
open func bindViews() {
// override in subclass
}
open func configureAppearance() {
// override in subclass
}
open func localize() {
// override in subclass
}
}

View File

@ -38,29 +38,47 @@ open class BaseListItemView<LeadingView: UIView,
}()
public private(set) lazy var leadingViewConstraints: SubviewConstraints = {
SubviewConstraints(edgeConstraints: EdgeConstraints(leadingConstraint: leadingView.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: middleViewLeadingConstraint,
topConstraint: leadingView.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: leadingView.bottomAnchor.constraint(equalTo: bottomAnchor)),
centerConstraints: CenterConstraints(centerXConstraint: leadingView.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: leadingView.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: SizeConstraints(widthConstraint: leadingView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: leadingView.heightAnchor.constraint(equalToConstant: .zero)))
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingView.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: middleViewLeadingConstraint,
topConstraint: leadingView.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: leadingView.bottomAnchor.constraint(equalTo: bottomAnchor))
let centerXConstraint = leadingView.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = leadingView.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: leadingView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: leadingView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public private(set) lazy var trailingViewLeadingToMiddleViewConstraint: NSLayoutConstraint = {
public private(set) lazy var trailingViewLeadingToMiddleConstraint: NSLayoutConstraint = {
trailingView.leadingAnchor.constraint(equalTo: middleView.trailingAnchor)
}()
public private(set) lazy var middleViewConstraints: SubviewConstraints = {
SubviewConstraints(edgeConstraints: EdgeConstraints(leadingConstraint: middleViewLeadingConstraint,
trailingConstraint: trailingViewLeadingToMiddleViewConstraint,
topConstraint: middleView.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: middleView.bottomAnchor.constraint(equalTo: bottomAnchor)),
centerConstraints: CenterConstraints(centerXConstraint: middleView.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: middleView.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: SizeConstraints(widthConstraint: middleView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: middleView.heightAnchor.constraint(equalToConstant: .zero)))
let edgeConstraints = EdgeConstraints(leadingConstraint: middleViewLeadingConstraint,
trailingConstraint: trailingViewLeadingToMiddleConstraint,
topConstraint: middleView.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: middleView.bottomAnchor.constraint(equalTo: bottomAnchor))
let centerXConstraint = middleView.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = middleView.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: middleView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: middleView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public private(set) lazy var middleViewLeadingToSuperViewConstraint: NSLayoutConstraint = {
@ -72,14 +90,25 @@ open class BaseListItemView<LeadingView: UIView,
}()
public private(set) lazy var trailingViewConstraints: SubviewConstraints = {
SubviewConstraints(edgeConstraints: EdgeConstraints(leadingConstraint: trailingViewLeadingToMiddleViewConstraint,
trailingConstraint: trailingView.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: trailingView.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: trailingView.bottomAnchor.constraint(equalTo: bottomAnchor)),
centerConstraints: CenterConstraints(centerXConstraint: middleView.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: trailingView.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: SizeConstraints(widthConstraint: trailingView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: trailingView.heightAnchor.constraint(equalToConstant: .zero)))
let trailingConstraint = trailingView.trailingAnchor.constraint(equalTo: trailingAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: trailingViewLeadingToMiddleConstraint,
trailingConstraint: trailingConstraint,
topConstraint: trailingView.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: trailingView.bottomAnchor.constraint(equalTo: bottomAnchor))
let centerXConstraint = middleView.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = trailingView.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let sizeConstraints = SizeConstraints(widthConstraint: trailingView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: trailingView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
// MARK: - Public Properties
@ -105,9 +134,6 @@ open class BaseListItemView<LeadingView: UIView,
[leadingView, middleView, trailingView]
.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
[leadingViewConstraints, middleViewConstraints, trailingViewConstraints]
.forEach { $0.update(insets: .zero, size: .infinity, centerOffset: .nan) }
}
// MARK: - Public methods
@ -118,21 +144,21 @@ open class BaseListItemView<LeadingView: UIView,
configureUIView(appearance: appearance)
updateLeadingViewLayout(leadingViewLayout: appearance.leadingViewAppearance.layout,
middleViewLayout: appearance.middleViewAppearance.layout)
update(leadingViewLayout: appearance.leadingViewAppearance.layout,
middleViewLayout: appearance.middleViewAppearance.layout)
updateMiddleViewLayout(middleViewLayout: appearance.middleViewAppearance.layout)
update(middleViewLayout: appearance.middleViewAppearance.layout)
updateTrailingViewLayout(trailingViewLayout: appearance.trailingAppearance.layout,
middleViewLayout: appearance.middleViewAppearance.layout)
update(trailingViewLayout: appearance.trailingAppearance.layout,
middleViewLayout: appearance.middleViewAppearance.layout)
}
// MARK: - Private methdos
private func updateLeadingViewLayout(leadingViewLayout: WrappedViewLayout,
middleViewLayout: WrappedViewLayout) {
private func update(leadingViewLayout: WrappedViewLayout,
middleViewLayout: WrappedViewLayout) {
let leadingToSuperviewContraint: NSLayoutConstraint?
let leadingToSuperviewContraint: NSLayoutConstraint
let leadingViewLeftInset: CGFloat
if isLeadingViewHidden {
@ -141,11 +167,15 @@ open class BaseListItemView<LeadingView: UIView,
leadingToSuperviewContraint = middleViewLeadingToSuperViewConstraint
leadingViewLeftInset = middleViewLayout.insets.left
} else {
middleViewLeadingConstraint.constant = leadingViewLayout.insets.right + middleViewLayout.insets.left
leadingViewConstraints.edgeConstraints.leadingConstraint?.isActive = true
middleViewConstraints.edgeConstraints.leadingConstraint?.isActive = true
let middleViewLeadingConstant = leadingViewLayout.insets.add(\.left,
to: \.right,
of: middleViewLayout.insets,
onNan: .zero)
middleViewLeadingConstraint.setActiveConstantOrDeactivate(constant: middleViewLeadingConstant)
leadingViewConstraints.edgeConstraints.leadingConstraint.isActive = true
middleViewConstraints.edgeConstraints.leadingConstraint.isActive = true
middleViewLeadingToSuperViewConstraint.isActive = false
leadingViewConstraints.update(from: leadingViewLayout)
@ -154,17 +184,17 @@ open class BaseListItemView<LeadingView: UIView,
leadingViewLeftInset = leadingViewLayout.insets.left
}
leadingToSuperviewContraint?.constant = leadingViewLeftInset
leadingToSuperviewContraint.setActiveConstantOrDeactivate(constant: leadingViewLeftInset)
}
private func updateMiddleViewLayout(middleViewLayout: WrappedViewLayout) {
private func update(middleViewLayout: WrappedViewLayout) {
middleViewConstraints.update(from: middleViewLayout)
}
private func updateTrailingViewLayout(trailingViewLayout: WrappedViewLayout,
middleViewLayout: WrappedViewLayout) {
private func update(trailingViewLayout: WrappedViewLayout,
middleViewLayout: WrappedViewLayout) {
let trailingToSuperviewConstraint: NSLayoutConstraint?
let trailingToSuperviewConstraint: NSLayoutConstraint
let trailingViewRightInset: CGFloat
if isTrailingViewHidden {
@ -173,10 +203,14 @@ open class BaseListItemView<LeadingView: UIView,
trailingToSuperviewConstraint = middleViewTrailingToSuperViewConstraint
trailingViewRightInset = middleViewLayout.insets.right
} else {
trailingViewLeadingToMiddleViewConstraint.constant = middleViewLayout.insets.right + trailingViewLayout.insets.left
trailingViewLeadingToMiddleViewConstraint.isActive = true
let trailingViewLeadingToMiddleConstant = middleViewLayout.insets.add(\.right,
to: \.left,
of: trailingViewLayout.insets,
onNan: .zero)
trailingViewLeadingToMiddleConstraint.setActiveConstantOrDeactivate(constant: trailingViewLeadingToMiddleConstant)
trailingViewLeadingToMiddleConstraint.isActive = true
middleViewTrailingToSuperViewConstraint.isActive = false
trailingViewConstraints.update(from: trailingViewLayout)
@ -185,6 +219,6 @@ open class BaseListItemView<LeadingView: UIView,
trailingViewRightInset = trailingViewLayout.insets.right
}
trailingToSuperviewConstraint?.constant = -trailingViewRightInset
trailingToSuperviewConstraint.setActiveConstantOrDeactivate(constant: -trailingViewRightInset)
}
}

View File

@ -33,19 +33,19 @@ open class BasePlaceholderImageView<Placeholder: UIView>: UIImageView,
public private(set) lazy var wrappedView = Placeholder()
public var contentInsets: UIEdgeInsets = .zero {
public var wrappedContentInsets: UIEdgeInsets = .zero {
didSet {
update(subviewConstraints: placeholderConstraints)
}
}
public var contentSize: CGSize = .infinity {
public var wrappedContentSize: CGSize = .infinity {
didSet {
update(subviewConstraints: placeholderConstraints)
}
}
public var contentCenterOffset: UIOffset = .nan {
public var wrappedContentCenterOffset: UIOffset = .nan {
didSet {
update(subviewConstraints: placeholderConstraints)
}
@ -118,7 +118,9 @@ open class BasePlaceholderImageView<Placeholder: UIView>: UIImageView,
// MARK: - Open methods
open func configureBasePlaceholder(appearance: BaseWrappedViewHolderAppearance<UIView.DefaultWrappedAppearance, some WrappedViewLayout>) {
open func configureBasePlaceholder(appearance: BaseWrappedViewHolderAppearance<
some BaseWrappedAppearance<some ViewLayout>,
some WrappedViewLayout>) {
configureUIView(appearance: appearance)
wrappedView.configureUIView(appearance: appearance.subviewAppearance)

View File

@ -31,44 +31,74 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
public let controlsStackView = UIStackView()
public private(set) lazy var imageViewConstraints: SubviewConstraints = {
SubviewConstraints(edgeConstraints: EdgeConstraints(leadingConstraint: imageView.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: imageView.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: imageView.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: imageView.bottomAnchor.constraint(equalTo: textView.topAnchor)),
centerConstraints: CenterConstraints(centerXConstraint: imageView.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: imageView.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: SizeConstraints(widthConstraint: imageView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: imageView.heightAnchor.constraint(equalToConstant: .zero)))
let edgeConstraints = EdgeConstraints(leadingConstraint: imageView.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: imageView.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: imageView.topAnchor.constraint(equalTo: topAnchor),
bottomConstraint: imageView.bottomAnchor.constraint(equalTo: textView.topAnchor))
let centerConstraints = CenterConstraints(centerXConstraint: imageView.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: imageView.centerYAnchor.constraint(equalTo: centerYAnchor))
let sizeConstraints = SizeConstraints(widthConstraint: imageView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: imageView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public private(set) lazy var textViewTopToSuperviewTopConstraint: NSLayoutConstraint = {
textView.topAnchor.constraint(equalTo: topAnchor)
}()
public private(set) lazy var textViewBottomToSuperviewBottomConstraint: NSLayoutConstraint = {
public private(set) lazy var textViewToSuperviewBottomConstraint: NSLayoutConstraint = {
textView.bottomAnchor.constraint(equalTo: bottomAnchor)
}()
public private(set) lazy var textViewConstraints: SubviewConstraints = {
SubviewConstraints(edgeConstraints: EdgeConstraints(leadingConstraint: textView.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: textView.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: textView.topAnchor.constraint(equalTo: imageView.bottomAnchor),
bottomConstraint: textView.bottomAnchor.constraint(lessThanOrEqualTo: controlsStackView.topAnchor)),
centerConstraints: CenterConstraints(centerXConstraint: textView.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: textView.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: SizeConstraints(widthConstraint: textView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: textView.heightAnchor.constraint(equalToConstant: .zero)))
let bottomConstraint = textView.bottomAnchor.constraint(lessThanOrEqualTo: controlsStackView.topAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: textView.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: textView.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: textView.topAnchor.constraint(equalTo: imageView.bottomAnchor),
bottomConstraint: bottomConstraint)
let centerConstraints = CenterConstraints(centerXConstraint: textView.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: textView.centerYAnchor.constraint(equalTo: centerYAnchor))
let sizeConstraints = SizeConstraints(widthConstraint: textView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: textView.heightAnchor.constraint(equalToConstant: .zero))
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public private(set) lazy var controlsViewConstraints: SubviewConstraints = {
SubviewConstraints(edgeConstraints: EdgeConstraints(leadingConstraint: controlsStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingConstraint: controlsStackView.trailingAnchor.constraint(equalTo: trailingAnchor),
topConstraint: controlsStackView.topAnchor.constraint(equalTo: textView.bottomAnchor),
bottomConstraint: controlsStackView.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor)),
centerConstraints: CenterConstraints(centerXConstraint: controlsStackView.centerXAnchor.constraint(equalTo: centerXAnchor),
centerYConstraint: controlsStackView.centerYAnchor.constraint(equalTo: centerYAnchor)),
sizeConstraints: SizeConstraints(widthConstraint: controlsStackView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: controlsStackView.heightAnchor.constraint(equalToConstant: .zero)))
let leadingConstraint = controlsStackView.leadingAnchor.constraint(equalTo: leadingAnchor)
let trailingConstraint = controlsStackView.trailingAnchor.constraint(equalTo: trailingAnchor)
let topConstraint = controlsStackView.topAnchor.constraint(equalTo: textView.bottomAnchor)
let bottomConstraint = controlsStackView.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor)
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint,
trailingConstraint: trailingConstraint,
topConstraint: topConstraint,
bottomConstraint: bottomConstraint)
let centerXConstraint = controlsStackView.centerXAnchor.constraint(equalTo: centerXAnchor)
let centerYConstraint = controlsStackView.centerYAnchor.constraint(equalTo: centerYAnchor)
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
centerYConstraint: centerYConstraint)
let heightConstraint = controlsStackView.heightAnchor.constraint(equalToConstant: .zero)
let sizeConstraints = SizeConstraints(widthConstraint: controlsStackView.widthAnchor.constraint(equalToConstant: .zero),
heightConstraint: heightConstraint)
return SubviewConstraints(edgeConstraints: edgeConstraints,
centerConstraints: centerConstraints,
sizeConstraints: sizeConstraints)
}()
public var keyboardDidShownObserver: NSObjectProtocol?
@ -82,18 +112,6 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
controlsStackView.isHidden || controlsStackView.arrangedSubviews.isEmpty
}
// MARK: - Deinit
deinit {
if let keyboardDidShownObserver = keyboardDidShownObserver {
NotificationCenter.default.removeObserver(keyboardDidShownObserver)
}
if let keyboardDidHiddenObserver = keyboardDidHiddenObserver {
NotificationCenter.default.removeObserver(keyboardDidHiddenObserver)
}
}
// MARK: - BaseInitializableView
open override func addViews() {
@ -146,7 +164,7 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
// MARK: - Open methods
open func applyBaseStyle(style: BasePlaceholderStyle<some BaseAppearance<some WrappedViewAppearance> & ViewAppearance>) {
open func applyBase(style: BasePlaceholderStyle<some BaseAppearance<some WrappedViewAppearance> & ViewAppearance>) {
textView.configure(with: style.titleSubtitle)
controlsStackView.axis = style.controlsViewAxis
@ -158,7 +176,7 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
controlsStackView.addArrangedSubview(button)
}
configureAppearance(appearance: style.appearance)
configureBase(appearance: style.appearance)
}
open func addAction(_ action: UIButton.Action,
@ -169,10 +187,10 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
button?.addTarget(action.target, action: action.action, for: action.event)
}
open func configureAppearance(appearance: BaseAppearance<some WrappedViewAppearance>) {
configureImageViewLayout(layout: appearance.imageViewAppearance.layout)
configureTextViewLayout(layout: appearance.textViewAppearance.layout)
configureControlsViewLayout(layout: appearance.controlsViewAppearance.layout)
open func configureBase(appearance: BaseAppearance<some WrappedViewAppearance>) {
configureImageView(layout: appearance.imageViewAppearance.layout)
configureTextView(layout: appearance.textViewAppearance.layout)
configureControlsView(layout: appearance.controlsViewAppearance.layout)
configureUIView(appearance: appearance)
textView.configure(appearance: appearance.textViewAppearance)
@ -183,13 +201,13 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
let multiplier = isKeyboardHidden ? 1.0 : -1.0
if let height = getKeyboardHeight(notification) {
controlsViewConstraints.edgeConstraints.bottomConstraint?.constant = multiplier * height / 2
controlsViewConstraints.edgeConstraints.bottomConstraint.constant = multiplier * height / 2
}
}
// MARK: - Private methods
private func configureImageViewLayout(layout: WrappedViewLayout) {
private func configureImageView(layout: WrappedViewLayout) {
guard !isImageViewHidden else {
imageViewConstraints.deactivate()
return
@ -198,17 +216,17 @@ open class BasePlaceholderView<ImageView: UIView>: BaseInitializableView {
imageViewConstraints.update(from: layout)
}
private func configureTextViewLayout(layout: WrappedViewLayout) {
textViewConstraints.edgeConstraints.topConstraint?.isActive = !isImageViewHidden
private func configureTextView(layout: WrappedViewLayout) {
textViewConstraints.edgeConstraints.topConstraint.isActive = !isImageViewHidden
textViewTopToSuperviewTopConstraint.isActive = isImageViewHidden
textViewConstraints.edgeConstraints.bottomConstraint?.isActive = !isControlsViewHidden
textViewBottomToSuperviewBottomConstraint.isActive = isControlsViewHidden
textViewConstraints.edgeConstraints.bottomConstraint.isActive = !isControlsViewHidden
textViewToSuperviewBottomConstraint.isActive = isControlsViewHidden
textViewConstraints.update(from: layout)
}
private func configureControlsViewLayout(layout: SpacedWrappedViewLayout) {
private func configureControlsView(layout: SpacedWrappedViewLayout) {
guard !isControlsViewHidden else {
controlsViewConstraints.deactivate()
return

View File

@ -32,7 +32,7 @@ public final class DefaultPlaceholderView: BasePlaceholderView<UIImageView>, App
public func apply(style: DefaultPlaceholderStyle) {
imageView.image = style.image
super.applyBaseStyle(style: style)
super.applyBase(style: style)
guard let image = imageView.image else {
return
@ -44,14 +44,14 @@ public final class DefaultPlaceholderView: BasePlaceholderView<UIImageView>, App
// MARK: - AppearanceConfigurable
public func configure(appearance: Appearance) {
configureAppearance(appearance: appearance)
configureBase(appearance: appearance)
}
}
// MARK: - Default appearance model
extension DefaultPlaceholderView {
public final class Appearance: BaseAppearance<UIView.DefaultWrappedAppearance>, ViewAppearance {
public extension DefaultPlaceholderView {
final class Appearance: BaseAppearance<UIView.DefaultWrappedAppearance>, ViewAppearance {
public static var defaultAppearance: Self {
.init()
}

View File

@ -20,11 +20,11 @@
// THE SOFTWARE.
//
extension StatefulButton {
public func apply(style: BaseButtonStyle) {
public extension StatefulButton {
func apply(style: BaseButtonStyle) {
set(titles: style.titles)
set(images: style.images)
set(appearance: style.appearance)
configureStatefulButton(appearance: style.appearance)
if let action = style.action {
addTarget(action.target, action: action.action, for: action.event)

View File

@ -23,8 +23,8 @@
import UIKit
public struct CenterConstraints: ConstraintsSet {
public let centerXConstraint: NSLayoutConstraint?
public let centerYConstraint: NSLayoutConstraint?
public var centerXConstraint: NSLayoutConstraint
public var centerYConstraint: NSLayoutConstraint
// MARK: - ConstraintsSet
@ -32,19 +32,19 @@ public struct CenterConstraints: ConstraintsSet {
[
centerXConstraint,
centerYConstraint
].compactMap { $0 }
]
}
public init(centerXConstraint: NSLayoutConstraint? = nil,
centerYConstraint: NSLayoutConstraint? = nil) {
public init(centerXConstraint: NSLayoutConstraint,
centerYConstraint: NSLayoutConstraint) {
self.centerXConstraint = centerXConstraint
self.centerYConstraint = centerYConstraint
}
public func update(offset: UIOffset) {
centerXConstraint?.setActiveConstantOrDeactivate(constant: offset.horizontal)
centerYConstraint?.setActiveConstantOrDeactivate(constant: offset.vertical)
centerXConstraint.setActiveConstantOrDeactivate(constant: offset.horizontal)
centerYConstraint.setActiveConstantOrDeactivate(constant: offset.vertical)
}
public var centerConstraints: [NSLayoutConstraint] {

View File

@ -23,10 +23,10 @@
import UIKit
public struct EdgeConstraints: ConstraintsSet {
public let leadingConstraint: NSLayoutConstraint?
public let trailingConstraint: NSLayoutConstraint?
public let topConstraint: NSLayoutConstraint?
public let bottomConstraint: NSLayoutConstraint?
public var leadingConstraint: NSLayoutConstraint
public var trailingConstraint: NSLayoutConstraint
public var topConstraint: NSLayoutConstraint
public var bottomConstraint: NSLayoutConstraint
// MARK: - ConstraintsSet
@ -36,27 +36,27 @@ public struct EdgeConstraints: ConstraintsSet {
trailingConstraint,
topConstraint,
bottomConstraint
].compactMap { $0 }
]
}
public var horizontal: [NSLayoutConstraint] {
[
leadingConstraint,
trailingConstraint
].compactMap { $0 }
]
}
public var vertical: [NSLayoutConstraint] {
[
topConstraint,
bottomConstraint
].compactMap { $0 }
]
}
public init(leadingConstraint: NSLayoutConstraint? = nil,
trailingConstraint: NSLayoutConstraint? = nil,
topConstraint: NSLayoutConstraint? = nil,
bottomConstraint: NSLayoutConstraint? = nil) {
public init(leadingConstraint: NSLayoutConstraint,
trailingConstraint: NSLayoutConstraint,
topConstraint: NSLayoutConstraint,
bottomConstraint: NSLayoutConstraint) {
self.leadingConstraint = leadingConstraint
self.trailingConstraint = trailingConstraint
@ -65,9 +65,9 @@ public struct EdgeConstraints: ConstraintsSet {
}
public func update(from insets: UIEdgeInsets) {
leadingConstraint?.setActiveConstantOrDeactivate(constant: insets.left)
trailingConstraint?.setActiveConstantOrDeactivate(constant: -insets.right)
topConstraint?.setActiveConstantOrDeactivate(constant: insets.top)
bottomConstraint?.setActiveConstantOrDeactivate(constant: -insets.bottom)
leadingConstraint.setActiveConstantOrDeactivate(constant: insets.left)
trailingConstraint.setActiveConstantOrDeactivate(constant: -insets.right)
topConstraint.setActiveConstantOrDeactivate(constant: insets.top)
bottomConstraint.setActiveConstantOrDeactivate(constant: -insets.bottom)
}
}

View File

@ -22,7 +22,7 @@
import UIKit.NSLayoutConstraint
extension NSLayoutConstraint {
public extension NSLayoutConstraint {
func setActiveConstantOrDeactivate(constant: CGFloat) {
if constant.isFinite {
self.constant = constant

View File

@ -23,8 +23,8 @@
import UIKit
public struct SizeConstraints: ConstraintsSet {
public let widthConstraint: NSLayoutConstraint?
public let heightConstraint: NSLayoutConstraint?
public var widthConstraint: NSLayoutConstraint
public var heightConstraint: NSLayoutConstraint
// MARK: - ConstraintsSet
@ -32,18 +32,18 @@ public struct SizeConstraints: ConstraintsSet {
[
widthConstraint,
heightConstraint
].compactMap { $0 }
]
}
public init(widthConstraint: NSLayoutConstraint? = nil,
heightConstraint: NSLayoutConstraint? = nil) {
public init(widthConstraint: NSLayoutConstraint,
heightConstraint: NSLayoutConstraint) {
self.widthConstraint = widthConstraint
self.heightConstraint = heightConstraint
}
public func update(from size: CGSize) {
widthConstraint?.setActiveConstantOrDeactivate(constant: size.width)
heightConstraint?.setActiveConstantOrDeactivate(constant: size.height)
widthConstraint.setActiveConstantOrDeactivate(constant: size.width)
heightConstraint.setActiveConstantOrDeactivate(constant: size.height)
}
}

View File

@ -35,8 +35,8 @@ public struct SubviewConstraints: ConstraintsSet {
}
public init(edgeConstraints: EdgeConstraints,
centerConstraints: CenterConstraints,
sizeConstraints: SizeConstraints) {
centerConstraints: CenterConstraints,
sizeConstraints: SizeConstraints) {
self.edgeConstraints = edgeConstraints
self.centerConstraints = centerConstraints
@ -59,9 +59,12 @@ public struct SubviewConstraints: ConstraintsSet {
// MARK: - WrappedViewLayout shortcut
func update(from layout: some WrappedViewLayout) {
@discardableResult
public func update(from layout: some WrappedViewLayout) -> Self {
update(insets: layout.insets,
size: layout.size,
centerOffset: layout.centerOffset)
return self
}
}

View File

@ -31,7 +31,7 @@ open class CollectionTableViewCell<CollectionView: UICollectionView>: ContainerT
if #available(iOS 16, *) {
contentSizeObservation = wrappedView.observe(\.contentSize,
options: [.new]) { collectionView, change in
options: [.new]) { collectionView, change in
if let contentSize = change.newValue, !contentSize.equalTo(collectionView.bounds.size) {
self.invalidateIntrinsicContentSize()
@ -52,12 +52,12 @@ open class CollectionTableViewCell<CollectionView: UICollectionView>: ContainerT
}
let cachedCollectionFrame = wrappedView.frame
wrappedView.frame.size.width = targetSize.width - contentInsets.left - contentInsets.right
wrappedView.frame.size.width = targetSize.width - wrappedContentInsets.left - wrappedContentInsets.right
let collectionContentHeight = wrappedView.collectionViewLayout.collectionViewContentSize.height
wrappedView.frame = cachedCollectionFrame
return CGSize(width: targetSize.width,
height: collectionContentHeight + contentInsets.top + contentInsets.bottom)
height: collectionContentHeight + wrappedContentInsets.top + wrappedContentInsets.bottom)
}
// MARK: - Open methods

View File

@ -26,24 +26,24 @@ import TIUIKitCore
open class ContainerCollectionViewCell<View: UIView>: UICollectionViewCell, InitializableViewProtocol, WrappedViewHolder {
public var callbacks: [ViewCallbacks] = []
// MARK: - WrappedViewHolder
public private(set) lazy var wrappedView = createView()
public var contentInsets: UIEdgeInsets = .zero {
public var wrappedContentInsets: UIEdgeInsets = .zero {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var contentSize: CGSize = .infinity {
public var wrappedContentSize: CGSize = .infinity {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var contentCenterOffset: UIOffset = .nan {
public var wrappedContentCenterOffset: UIOffset = .nan {
didSet {
update(subviewConstraints: subviewContraints)
}
@ -98,6 +98,6 @@ open class ContainerCollectionViewCell<View: UIView>: UICollectionViewCell, Init
}
open func createView() -> View {
return View()
View()
}
}

View File

@ -0,0 +1,73 @@
//
// Copyright (c) 2023 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import TIUIKitCore
import UIKit
open class ContainerScrollView<ContentView: UIView>: BaseInitializeableScrollView, WrappedViewHolder {
// MARK: - WrappedViewHolder
public private(set) lazy var wrappedView = createView()
public var wrappedContentInsets: UIEdgeInsets = .zero {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var wrappedContentSize: CGSize = .infinity {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var wrappedContentCenterOffset: UIOffset = .nan {
didSet {
update(subviewConstraints: subviewContraints)
}
}
private lazy var subviewContraints: SubviewConstraints = {
configureWrappedViewLayout()
}()
open func createView() -> ContentView {
ContentView()
}
// MARK: - InitializableViewProtocol
open override func addViews() {
super.addViews()
addSubview(wrappedView)
}
}
extension ContainerScrollView: AppearanceConfigurable where View: AppearanceConfigurable,
View.Appearance: WrappedViewAppearance {
public func configure(appearance: DefaultWrappedViewHolderAppearance<View.Appearance, NoLayout>) {
configureWrappedViewHolder(appearance: appearance)
}
}

View File

@ -24,24 +24,24 @@ import UIKit
import TIUIKitCore
open class ContainerTableViewCell<View: UIView>: BaseInitializableCell, WrappedViewHolder {
// MARK: - WrappedViewHolder
public private(set) lazy var wrappedView = createView()
public var contentInsets: UIEdgeInsets = .zero {
public var wrappedContentInsets: UIEdgeInsets = .zero {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var contentSize: CGSize = .infinity {
public var wrappedContentSize: CGSize = .infinity {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var contentCenterOffset: UIOffset = .nan {
public var wrappedContentCenterOffset: UIOffset = .nan {
didSet {
update(subviewConstraints: subviewContraints)
}
@ -66,14 +66,7 @@ open class ContainerTableViewCell<View: UIView>: BaseInitializableCell, WrappedV
}
open func createView() -> View {
return View()
}
// MARK: - Open methods
public func configureContainerTableViewCell(appearance: BaseWrappedViewHolderAppearance<some WrappedViewAppearance, some ViewLayout>) {
updateContentLayout(from: appearance.subviewAppearance.layout)
configureUIView(appearance: appearance)
View()
}
}
@ -82,8 +75,7 @@ open class ContainerTableViewCell<View: UIView>: BaseInitializableCell, WrappedV
extension ContainerTableViewCell: AppearanceConfigurable where View: AppearanceConfigurable,
View.Appearance: WrappedViewAppearance {
public func configure(appearance: DefaultWrappedViewHolderAppearance<View.Appearance, UIView.NoLayout>) {
configureContainerTableViewCell(appearance: appearance)
wrappedView.configure(appearance: appearance.subviewAppearance)
public func configure(appearance: DefaultWrappedViewHolderAppearance<View.Appearance, NoLayout>) {
configureWrappedViewHolder(appearance: appearance)
}
}

View File

@ -27,19 +27,19 @@ public final class ContainerView<View: UIView>: BaseInitializableView, WrappedVi
public private(set) lazy var wrappedView = View()
public var contentInsets: UIEdgeInsets = .zero {
public var wrappedContentInsets: UIEdgeInsets = .zero {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var contentSize: CGSize = .infinity {
public var wrappedContentSize: CGSize = .infinity {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var contentCenterOffset: UIOffset = .nan {
public var wrappedContentCenterOffset: UIOffset = .nan {
didSet {
update(subviewConstraints: subviewContraints)
}
@ -80,8 +80,6 @@ extension ContainerView: AppearanceConfigurable where View: AppearanceConfigurab
public typealias Appearance = UIView.DefaultWrappedViewHolderAppearance<View.Appearance, UIView.DefaultWrappedLayout>
public func configure(appearance: Appearance) {
wrappedView.configure(appearance: appearance.subviewAppearance)
configureUIView(appearance: appearance)
updateContentLayout(from: appearance.subviewAppearance.layout)
configureWrappedViewHolder(appearance: appearance)
}
}

View File

@ -23,26 +23,28 @@
import UIKit
import TIUIKitCore
open class ReusableCollectionContainerView<View: UIView>: UICollectionReusableView, InitializableViewProtocol, WrappedViewHolder {
open class ReusableCollectionContainerView<View: UIView>: UICollectionReusableView,
InitializableViewProtocol,
WrappedViewHolder {
public var callbacks: [ViewCallbacks] = []
// MARK: - WrappedViewHolder
public private(set) lazy var wrappedView = createView()
public var contentInsets: UIEdgeInsets = .zero {
public var wrappedContentInsets: UIEdgeInsets = .zero {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var contentSize: CGSize = .infinity {
public var wrappedContentSize: CGSize = .infinity {
didSet {
update(subviewConstraints: subviewContraints)
}
}
public var contentCenterOffset: UIOffset = .nan {
public var wrappedContentCenterOffset: UIOffset = .nan {
didSet {
update(subviewConstraints: subviewContraints)
}
@ -97,6 +99,6 @@ open class ReusableCollectionContainerView<View: UIView>: UICollectionReusableVi
}
open func createView() -> View {
return View()
View()
}
}

View File

@ -21,12 +21,22 @@
//
import TIUIKitCore
import UIKit
import UIKit.UIView
public extension WrappedViewLayout {
func update(constraints: SubviewConstraints) {
constraints.update(insets: insets,
size: size,
centerOffset: centerOffset)
public extension WrappedViewHolder {
func configureBaseWrappedViewHolder(appearance: some UIView.BaseWrappedViewHolderAppearance<
some WrappedViewAppearance,
some ViewLayout>) {
updateContentLayout(from: appearance.subviewAppearance.layout)
contentView.configureUIView(appearance: appearance)
}
}
public extension WrappedViewHolder where View: AppearanceConfigurable, View.Appearance: WrappedViewAppearance {
func configureWrappedViewHolder(appearance: some UIView.BaseWrappedViewHolderAppearance<
View.Appearance,
some ViewLayout>) {
configureBaseWrappedViewHolder(appearance: appearance)
wrappedView.configure(appearance: appearance.subviewAppearance)
}
}

View File

@ -28,9 +28,10 @@ public protocol WrappedViewHolder: AnyObject {
var wrappedView: View { get }
var contentView: UIView { get }
var contentInsets: UIEdgeInsets { get set }
var contentSize: CGSize { get set }
var contentCenterOffset: UIOffset { get set }
var wrappedContentInsets: UIEdgeInsets { get set }
var wrappedContentSize: CGSize { get set }
var wrappedContentCenterOffset: UIOffset { get set }
}
public extension WrappedViewHolder {
@ -65,17 +66,17 @@ public extension WrappedViewHolder {
// MARK: - SubviewConstraints shortcut
func update(subviewConstraints: SubviewConstraints) {
subviewConstraints.update(insets: contentInsets,
size: contentSize,
centerOffset: contentCenterOffset)
subviewConstraints.update(insets: wrappedContentInsets,
size: wrappedContentSize,
centerOffset: wrappedContentCenterOffset)
}
// MARK: - WrappedViewLayout shortcut
func updateContentLayout(from layout: some WrappedViewLayout) {
contentInsets = layout.insets
contentSize = layout.size
contentCenterOffset = layout.centerOffset
wrappedContentInsets = layout.insets
wrappedContentSize = layout.size
wrappedContentCenterOffset = layout.centerOffset
}
}

View File

@ -57,10 +57,10 @@ let customStyle = DefaultPlaceholderStyle(
}, buttonsStyles: [
.init(titles: [.normal: "Reload"],
appearance: .init(stateAppearances: [
.normal: UIButton.DefaultAppearance(textAttributes: .init(font: .systemFont(ofSize: 20),
color: .black,
alignment: .natural,
isMultiline: false))
.normal: UIButton.DefaultStateAppearance(textAttributes: .init(font: .systemFont(ofSize: 20),
color: .black,
alignment: .natural,
isMultiline: false))
]))
])

View File

@ -31,33 +31,31 @@ public extension UIEdgeInsets {
}
static func horizontal(_ insets: CGFloat) -> UIEdgeInsets {
.init(top: .zero, left: insets, bottom: .zero, right: insets)
.init(top: .nan, left: insets, bottom: .nan, right: insets)
}
static func vertical(_ insets: CGFloat) -> UIEdgeInsets {
.init(top: insets, left: .zero, bottom: insets, right: .zero)
.init(top: insets, left: .nan, bottom: insets, right: .nan)
}
static func horizontal(left: CGFloat = .zero, right: CGFloat = .zero) -> UIEdgeInsets {
.init(top: .zero, left: left, bottom: .zero, right: right)
static func horizontal(left: CGFloat = .nan, right: CGFloat = .nan) -> UIEdgeInsets {
.init(top: .nan, left: left, bottom: .nan, right: right)
}
static func vertical(top: CGFloat = .zero, bottom: CGFloat = .zero) -> UIEdgeInsets {
.init(top: top, left: .zero, bottom: bottom, right: .zero)
}
// MARK: - Computed Properties
var vertical: CGFloat {
top + bottom
}
var horizontal: CGFloat {
left + right
static func vertical(top: CGFloat = .nan, bottom: CGFloat = .nan) -> UIEdgeInsets {
.init(top: top, left: .nan, bottom: bottom, right: .nan)
}
// MARK: - Instance methods
func vertical(onNan defaultValue: CGFloat = .nan) -> CGFloat {
add(\.top, to: \.bottom, of: self, onNan: defaultValue)
}
func horizontal(onNan defaultValue: CGFloat = .nan) -> CGFloat {
add(\.left, to: \.right, of: self, onNan: defaultValue)
}
func horizontal(_ insets: CGFloat) -> UIEdgeInsets {
.init(top: top, left: insets, bottom: bottom, right: insets)
}
@ -73,4 +71,24 @@ public extension UIEdgeInsets {
func vertical(top: CGFloat = .zero, bottom: CGFloat = .zero) -> UIEdgeInsets {
.init(top: top, left: left, bottom: bottom, right: right)
}
func add(_ lhsKeyPath: KeyPath<Self, CGFloat>,
to rhsKeyPath: KeyPath<Self, CGFloat>,
of rhs: Self,
onNan defaultValue: CGFloat = .nan) -> CGFloat {
let lhsValue = self[keyPath: lhsKeyPath]
let rhsValue = rhs[keyPath: rhsKeyPath]
switch (lhsValue.isFinite, rhsValue.isFinite) {
case (true, true):
return lhsValue + rhsValue
case (true, false):
return lhsValue
case (false, true):
return rhsValue
case (false, false):
return defaultValue
}
}
}

View File

@ -58,7 +58,9 @@ class CustomViewController: BaseModalViewController<UIView, UIView> {
$0.layout.size = .fixedHeight(52)
$0.backgroundColor = .white
$0.contentViewState = .buttonLeft(.init(titles: [.normal: "Close"],
appearance: [.normal: .init(backgroundColor: .blue)]))
appearance: .init(stateAppearances: [
.normal: .init(backgroundColor: .blue)
])))
})
return appearance

View File

@ -60,10 +60,10 @@ let customStyle = DefaultPlaceholderStyle(
}, buttonsStyles: [
.init(titles: [.normal: "Reload"],
appearance: .init(stateAppearances: [
.normal: UIButton.DefaultAppearance(textAttributes: .init(font: .systemFont(ofSize: 20),
color: .black,
alignment: .natural,
isMultiline: false))
.normal: UIButton.DefaultStateAppearance(textAttributes: .init(font: .systemFont(ofSize: 20),
color: .black,
alignment: .natural,
isMultiline: false))
]))
])