diff --git a/TIBottomSheet/PlaygroundPodfile b/TIBottomSheet/PlaygroundPodfile index ed76571e..e44c2e85 100644 --- a/TIBottomSheet/PlaygroundPodfile +++ b/TIBottomSheet/PlaygroundPodfile @@ -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 diff --git a/TIBottomSheet/Sources/BottomSheet/BaseModalViewController.swift b/TIBottomSheet/Sources/BottomSheet/BaseModalViewController.swift index aa71b815..838229cc 100644 --- a/TIBottomSheet/Sources/BottomSheet/BaseModalViewController.swift +++ b/TIBottomSheet/Sources/BottomSheet/BaseModalViewController.swift @@ -35,10 +35,120 @@ open class BaseModalViewController CGFloat { @@ -336,7 +452,7 @@ open class BaseModalViewController CGSize { diff --git a/TIBottomSheet/Sources/BottomSheet/Models/ModalViewPresentationDetent.swift b/TIBottomSheet/Sources/BottomSheet/Models/ModalViewPresentationDetent.swift index c01ad4b9..5f0d568b 100644 --- a/TIBottomSheet/Sources/BottomSheet/Models/ModalViewPresentationDetent.swift +++ b/TIBottomSheet/Sources/BottomSheet/Models/ModalViewPresentationDetent.swift @@ -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 diff --git a/TIBottomSheet/Sources/BottomSheet/Views/ModalFooterView.swift b/TIBottomSheet/Sources/BottomSheet/Views/ModalFooterView.swift index d1f00499..20aae228 100644 --- a/TIBottomSheet/Sources/BottomSheet/Views/ModalFooterView.swift +++ b/TIBottomSheet/Sources/BottomSheet/Views/ModalFooterView.swift @@ -32,12 +32,6 @@ public extension ModalFooterView { enum State { case hidden - case presented(UIView.BaseWrappedViewHolderAppearance) - } - - func configureAppearance(appearance: UIView.BaseWrappedViewHolderAppearance) { - wrappedView.configureUIView(appearance: appearance.subviewAppearance) - configureUIView(appearance: appearance) - contentInsets = appearance.subviewAppearance.layout.insets + case presented(BaseWrappedViewHolderAppearance) } } diff --git a/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift b/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift index 9cbcc555..79aa5d41 100644 --- a/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift +++ b/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift @@ -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, WrappedViewAppearance { + final class Appearance: BaseWrappedAppearance, 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 diff --git a/TIBottomSheet/TIBottomSheet.app/Contents/MacOS/Podfile b/TIBottomSheet/TIBottomSheet.app/Contents/MacOS/Podfile index ed76571e..e44c2e85 100644 --- a/TIBottomSheet/TIBottomSheet.app/Contents/MacOS/Podfile +++ b/TIBottomSheet/TIBottomSheet.app/Contents/MacOS/Podfile @@ -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 diff --git a/TIBottomSheet/TIBottomSheet.app/Contents/MacOS/TIBottomSheet.playground/Pages/TIBottomSheet.xcplaygroundpage/Contents.swift b/TIBottomSheet/TIBottomSheet.app/Contents/MacOS/TIBottomSheet.playground/Pages/TIBottomSheet.xcplaygroundpage/Contents.swift index ee0a5ab1..05a69f72 100644 --- a/TIBottomSheet/TIBottomSheet.app/Contents/MacOS/TIBottomSheet.playground/Pages/TIBottomSheet.xcplaygroundpage/Contents.swift +++ b/TIBottomSheet/TIBottomSheet.app/Contents/MacOS/TIBottomSheet.playground/Pages/TIBottomSheet.xcplaygroundpage/Contents.swift @@ -56,7 +56,9 @@ class CustomViewController: BaseModalViewController { $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 diff --git a/TIBottomSheet/TIBottomSheet.podspec b/TIBottomSheet/TIBottomSheet.podspec index 2ba117a7..3659dafc 100644 --- a/TIBottomSheet/TIBottomSheet.podspec +++ b/TIBottomSheet/TIBottomSheet.podspec @@ -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 diff --git a/TIEcommerce/Sources/Filters/FiltersViews/ListFilters/FiltersTableViewCell/DefaultFilterTableViewCell.swift b/TIEcommerce/Sources/Filters/FiltersViews/ListFilters/FiltersTableViewCell/DefaultFilterTableViewCell.swift index 759c9270..cd38ae79 100644 --- a/TIEcommerce/Sources/Filters/FiltersViews/ListFilters/FiltersTableViewCell/DefaultFilterTableViewCell.swift +++ b/TIEcommerce/Sources/Filters/FiltersViews/ListFilters/FiltersTableViewCell/DefaultFilterTableViewCell.swift @@ -60,7 +60,7 @@ open class DefaultFilterTableViewCell: ContainerTableViewCell // MARK: - Open methods open func updateAppearance(with appearance: FilterCellStateAppearance) { - contentInsets = appearance.contentInsets + wrappedContentInsets = appearance.contentInsets wrappedView.textColor = appearance.fontColor wrappedView.images = appearance.stateImages ?? [:] diff --git a/TIEcommerce/Sources/Filters/FiltersViews/TagsFilters/FiltersCollectionCell/DefaultFilterCollectionCell.swift b/TIEcommerce/Sources/Filters/FiltersViews/TagsFilters/FiltersCollectionCell/DefaultFilterCollectionCell.swift index 19895540..605d9217 100644 --- a/TIEcommerce/Sources/Filters/FiltersViews/TagsFilters/FiltersCollectionCell/DefaultFilterCollectionCell.swift +++ b/TIEcommerce/Sources/Filters/FiltersViews/TagsFilters/FiltersCollectionCell/DefaultFilterCollectionCell.swift @@ -56,7 +56,7 @@ open class DefaultFilterCollectionCell: ContainerCollectionViewCell, Co // MARK: - Open methdos open func updateAppearance(with appearance: FilterCellStateAppearance) { - contentInsets = appearance.contentInsets + wrappedContentInsets = appearance.contentInsets wrappedView.textColor = appearance.fontColor backgroundColor = appearance.backgroundColor diff --git a/TIUIElements/Sources/Wrappers/Containers/ScrollViewWrapper.swift b/TIUIElements/Sources/Views/BaseInitializeableViews/BaseInitializeableScrollView.swift similarity index 57% rename from TIUIElements/Sources/Wrappers/Containers/ScrollViewWrapper.swift rename to TIUIElements/Sources/Views/BaseInitializeableViews/BaseInitializeableScrollView.swift index 3edaf125..084e48fc 100644 --- a/TIUIElements/Sources/Wrappers/Containers/ScrollViewWrapper.swift +++ b/TIUIElements/Sources/Views/BaseInitializeableViews/BaseInitializeableScrollView.swift @@ -20,32 +20,51 @@ // THE SOFTWARE. // +import TIUIKitCore import UIKit -public final class ScrollViewWrapper: 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 } } diff --git a/TIUIElements/Sources/Views/ListItemView/BaseListItemView.swift b/TIUIElements/Sources/Views/ListItemView/BaseListItemView.swift index 3c56c8dd..1fb43419 100644 --- a/TIUIElements/Sources/Views/ListItemView/BaseListItemView.swift +++ b/TIUIElements/Sources/Views/ListItemView/BaseListItemView.swift @@ -38,29 +38,47 @@ open class BaseListItemView: 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: UIImageView, // MARK: - Open methods - open func configureBasePlaceholder(appearance: BaseWrappedViewHolderAppearance) { + open func configureBasePlaceholder(appearance: BaseWrappedViewHolderAppearance< + some BaseWrappedAppearance, + some WrappedViewLayout>) { configureUIView(appearance: appearance) wrappedView.configureUIView(appearance: appearance.subviewAppearance) diff --git a/TIUIElements/Sources/Views/Placeholder/Views/BasePlaceholderView.swift b/TIUIElements/Sources/Views/Placeholder/Views/BasePlaceholderView.swift index f101df37..49b6cca7 100644 --- a/TIUIElements/Sources/Views/Placeholder/Views/BasePlaceholderView.swift +++ b/TIUIElements/Sources/Views/Placeholder/Views/BasePlaceholderView.swift @@ -31,44 +31,74 @@ open class BasePlaceholderView: 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: 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: BaseInitializableView { // MARK: - Open methods - open func applyBaseStyle(style: BasePlaceholderStyle & ViewAppearance>) { + open func applyBase(style: BasePlaceholderStyle & ViewAppearance>) { textView.configure(with: style.titleSubtitle) controlsStackView.axis = style.controlsViewAxis @@ -158,7 +176,7 @@ open class BasePlaceholderView: 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: BaseInitializableView { button?.addTarget(action.target, action: action.action, for: action.event) } - open func configureAppearance(appearance: BaseAppearance) { - configureImageViewLayout(layout: appearance.imageViewAppearance.layout) - configureTextViewLayout(layout: appearance.textViewAppearance.layout) - configureControlsViewLayout(layout: appearance.controlsViewAppearance.layout) + open func configureBase(appearance: BaseAppearance) { + 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: 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: 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 diff --git a/TIUIElements/Sources/Views/Placeholder/Views/DefaultPlaceholderView.swift b/TIUIElements/Sources/Views/Placeholder/Views/DefaultPlaceholderView.swift index d86a2793..7cebb901 100644 --- a/TIUIElements/Sources/Views/Placeholder/Views/DefaultPlaceholderView.swift +++ b/TIUIElements/Sources/Views/Placeholder/Views/DefaultPlaceholderView.swift @@ -32,7 +32,7 @@ public final class DefaultPlaceholderView: BasePlaceholderView, 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, 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, ViewAppearance { +public extension DefaultPlaceholderView { + final class Appearance: BaseAppearance, ViewAppearance { public static var defaultAppearance: Self { .init() } diff --git a/TIUIElements/Sources/Views/StatefulButton/StatefulButton+ApplyStyle.swift b/TIUIElements/Sources/Views/StatefulButton/StatefulButton+ApplyStyle.swift index da7b1f67..29effcbf 100644 --- a/TIUIElements/Sources/Views/StatefulButton/StatefulButton+ApplyStyle.swift +++ b/TIUIElements/Sources/Views/StatefulButton/StatefulButton+ApplyStyle.swift @@ -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) diff --git a/TIUIElements/Sources/Wrappers/Constraints/CenterConstraints.swift b/TIUIElements/Sources/Wrappers/Constraints/CenterConstraints.swift index 7b107417..74afdeae 100644 --- a/TIUIElements/Sources/Wrappers/Constraints/CenterConstraints.swift +++ b/TIUIElements/Sources/Wrappers/Constraints/CenterConstraints.swift @@ -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] { diff --git a/TIUIElements/Sources/Wrappers/Constraints/EdgeConstraints.swift b/TIUIElements/Sources/Wrappers/Constraints/EdgeConstraints.swift index b25ea645..06afa29a 100644 --- a/TIUIElements/Sources/Wrappers/Constraints/EdgeConstraints.swift +++ b/TIUIElements/Sources/Wrappers/Constraints/EdgeConstraints.swift @@ -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) } } diff --git a/TIUIElements/Sources/Wrappers/Constraints/NSLayoutConstraint+SetActiveOrDeactivate.swift b/TIUIElements/Sources/Wrappers/Constraints/NSLayoutConstraint+SetActiveOrDeactivate.swift index 4929b2c3..3b5058c0 100644 --- a/TIUIElements/Sources/Wrappers/Constraints/NSLayoutConstraint+SetActiveOrDeactivate.swift +++ b/TIUIElements/Sources/Wrappers/Constraints/NSLayoutConstraint+SetActiveOrDeactivate.swift @@ -22,7 +22,7 @@ import UIKit.NSLayoutConstraint -extension NSLayoutConstraint { +public extension NSLayoutConstraint { func setActiveConstantOrDeactivate(constant: CGFloat) { if constant.isFinite { self.constant = constant diff --git a/TIUIElements/Sources/Wrappers/Constraints/SizeConstraints.swift b/TIUIElements/Sources/Wrappers/Constraints/SizeConstraints.swift index b26d9288..178b42d4 100644 --- a/TIUIElements/Sources/Wrappers/Constraints/SizeConstraints.swift +++ b/TIUIElements/Sources/Wrappers/Constraints/SizeConstraints.swift @@ -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) } } diff --git a/TIUIElements/Sources/Wrappers/Constraints/SubviewConstraints.swift b/TIUIElements/Sources/Wrappers/Constraints/SubviewConstraints.swift index 64372a50..99ab271a 100644 --- a/TIUIElements/Sources/Wrappers/Constraints/SubviewConstraints.swift +++ b/TIUIElements/Sources/Wrappers/Constraints/SubviewConstraints.swift @@ -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 } } diff --git a/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift b/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift index 43c2aed1..d753f46f 100644 --- a/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift +++ b/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift @@ -31,7 +31,7 @@ open class CollectionTableViewCell: 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: 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 diff --git a/TIUIElements/Sources/Wrappers/Containers/ContainerCollectionViewCell.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerCollectionViewCell.swift index 983a88da..85b484fb 100644 --- a/TIUIElements/Sources/Wrappers/Containers/ContainerCollectionViewCell.swift +++ b/TIUIElements/Sources/Wrappers/Containers/ContainerCollectionViewCell.swift @@ -26,24 +26,24 @@ import TIUIKitCore open class ContainerCollectionViewCell: 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: UICollectionViewCell, Init } open func createView() -> View { - return View() + View() } } diff --git a/TIUIElements/Sources/Wrappers/Containers/ContainerScrollView.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerScrollView.swift new file mode 100644 index 00000000..fda07019 --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Containers/ContainerScrollView.swift @@ -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: 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) { + configureWrappedViewHolder(appearance: appearance) + } +} diff --git a/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift index fc2a93f9..233911c2 100644 --- a/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift +++ b/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift @@ -24,24 +24,24 @@ import UIKit import TIUIKitCore open class ContainerTableViewCell: 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: BaseInitializableCell, WrappedV } open func createView() -> View { - return View() - } - - // MARK: - Open methods - - public func configureContainerTableViewCell(appearance: BaseWrappedViewHolderAppearance) { - updateContentLayout(from: appearance.subviewAppearance.layout) - configureUIView(appearance: appearance) + View() } } @@ -82,8 +75,7 @@ open class ContainerTableViewCell: BaseInitializableCell, WrappedV extension ContainerTableViewCell: AppearanceConfigurable where View: AppearanceConfigurable, View.Appearance: WrappedViewAppearance { - public func configure(appearance: DefaultWrappedViewHolderAppearance) { - configureContainerTableViewCell(appearance: appearance) - wrappedView.configure(appearance: appearance.subviewAppearance) + public func configure(appearance: DefaultWrappedViewHolderAppearance) { + configureWrappedViewHolder(appearance: appearance) } } diff --git a/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift index e3366378..9549c06b 100644 --- a/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift +++ b/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift @@ -27,19 +27,19 @@ public final class ContainerView: 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 public func configure(appearance: Appearance) { - wrappedView.configure(appearance: appearance.subviewAppearance) - configureUIView(appearance: appearance) - updateContentLayout(from: appearance.subviewAppearance.layout) + configureWrappedViewHolder(appearance: appearance) } } diff --git a/TIUIElements/Sources/Wrappers/Containers/ReusableCollectionContainerView.swift b/TIUIElements/Sources/Wrappers/Containers/ReusableCollectionContainerView.swift index 9f62832c..af087046 100644 --- a/TIUIElements/Sources/Wrappers/Containers/ReusableCollectionContainerView.swift +++ b/TIUIElements/Sources/Wrappers/Containers/ReusableCollectionContainerView.swift @@ -23,26 +23,28 @@ import UIKit import TIUIKitCore -open class ReusableCollectionContainerView: UICollectionReusableView, InitializableViewProtocol, WrappedViewHolder { +open class ReusableCollectionContainerView: 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: UICollectionReusableVi } open func createView() -> View { - return View() + View() } } diff --git a/TIUIElements/Sources/Wrappers/Extensions/WrappedViewLayout+Constraints.swift b/TIUIElements/Sources/Wrappers/Extensions/WrappedViewHolder+AppearanceConfigurable.swift similarity index 57% rename from TIUIElements/Sources/Wrappers/Extensions/WrappedViewLayout+Constraints.swift rename to TIUIElements/Sources/Wrappers/Extensions/WrappedViewHolder+AppearanceConfigurable.swift index dfd1d949..5fcd2186 100644 --- a/TIUIElements/Sources/Wrappers/Extensions/WrappedViewLayout+Constraints.swift +++ b/TIUIElements/Sources/Wrappers/Extensions/WrappedViewHolder+AppearanceConfigurable.swift @@ -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) } } diff --git a/TIUIElements/Sources/Wrappers/Protocols/WrappedViewHolder.swift b/TIUIElements/Sources/Wrappers/Protocols/WrappedViewHolder.swift index 54b6dd50..01ce6d97 100644 --- a/TIUIElements/Sources/Wrappers/Protocols/WrappedViewHolder.swift +++ b/TIUIElements/Sources/Wrappers/Protocols/WrappedViewHolder.swift @@ -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 } } diff --git a/TIUIElements/TIUIElements.app/Contents/MacOS/TIUIElements.playground/Pages/Placeholder.xcplaygroundpage/Contents.swift b/TIUIElements/TIUIElements.app/Contents/MacOS/TIUIElements.playground/Pages/Placeholder.xcplaygroundpage/Contents.swift index 46138048..b29ba3f6 100644 --- a/TIUIElements/TIUIElements.app/Contents/MacOS/TIUIElements.playground/Pages/Placeholder.xcplaygroundpage/Contents.swift +++ b/TIUIElements/TIUIElements.app/Contents/MacOS/TIUIElements.playground/Pages/Placeholder.xcplaygroundpage/Contents.swift @@ -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)) ])) ]) diff --git a/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift b/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift index 41d9af39..c27fb07d 100644 --- a/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift +++ b/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift @@ -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, + to rhsKeyPath: KeyPath, + 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 + } + } } diff --git a/docs/tibottomsheet/tibottomsheet.md b/docs/tibottomsheet/tibottomsheet.md index d8114635..5086aa85 100644 --- a/docs/tibottomsheet/tibottomsheet.md +++ b/docs/tibottomsheet/tibottomsheet.md @@ -58,7 +58,9 @@ class CustomViewController: BaseModalViewController { $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 diff --git a/docs/tiuielements/placeholder.md b/docs/tiuielements/placeholder.md index 293fb70e..74074b2d 100644 --- a/docs/tiuielements/placeholder.md +++ b/docs/tiuielements/placeholder.md @@ -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)) ])) ])