diff --git a/Package.resolved b/Package.resolved index 14b78560..ae273492 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Alamofire/Alamofire.git", "state" : { - "revision" : "f96b619bcb2383b43d898402283924b80e2c4bae", - "version" : "5.4.3" + "revision" : "bc268c28fb170f494de9e9927c371b8342979ece", + "version" : "5.7.1" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/petropavel13/Cursors", "state" : { - "revision" : "a1561869135e72832eff3b1e729075c56c2eebf6", - "version" : "0.5.1" + "revision" : "52f27b82cb1cbbc2b5fd09514c48b9c75e3b0300", + "version" : "0.6.0" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Moya/Moya.git", "state" : { - "revision" : "9b906860e3c3c09032879465c471e6375829593f", - "version" : "15.0.0" + "revision" : "c263811c1f3dbf002be9bd83107f7cdc38992b26", + "version" : "15.0.3" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://git.svc.touchin.ru/TouchInstinct/PanModal", "state" : { - "revision" : "be82eddb529faa2bc668230906ec007c53e7b635", - "version" : "1.3.0" + "revision" : "ced7c1703f90746df0224b6e0d33c146d9ae4284", + "version" : "1.3.1" } }, { @@ -77,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ReactiveX/RxSwift.git", "state" : { - "revision" : "b4307ba0b6425c0ba4178e138799946c3da594f8", - "version" : "6.5.0" + "revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4", + "version" : "6.6.0" } }, { diff --git a/TIBottomSheet/Sources/BottomSheet/BaseModalViewController+Appearance.swift b/TIBottomSheet/Sources/BottomSheet/BaseModalViewController+Appearance.swift index 53d66d0e..53a4002e 100644 --- a/TIBottomSheet/Sources/BottomSheet/BaseModalViewController+Appearance.swift +++ b/TIBottomSheet/Sources/BottomSheet/BaseModalViewController+Appearance.swift @@ -28,6 +28,7 @@ extension BaseModalViewController { open class BaseAppearance: UIView.BaseAppearance { + public var presentationDetents: [ModalViewPresentationDetent] public var dragViewState: DragView.State public var headerViewState: ModalHeaderView.State public var footerViewState: ModalFooterView.State @@ -36,15 +37,26 @@ extension BaseModalViewController { backgroundColor: UIColor = .clear, border: UIViewBorder = .init(), shadow: UIViewShadow? = nil, + presentationDetents: [ModalViewPresentationDetent] = [.maxHeight], dragViewState: DragView.State = .hidden, headerViewState: ModalHeaderView.State = .hidden, footerViewState: ModalFooterView.State = .hidden) { + self.presentationDetents = presentationDetents self.dragViewState = dragViewState self.headerViewState = headerViewState self.footerViewState = footerViewState - super.init(layout: layout, backgroundColor: backgroundColor, border: border, shadow: shadow) + super.init(layout: layout, + backgroundColor: backgroundColor, + border: border, + shadow: shadow) + } + } + + public final class DefaultAppearance: BaseAppearance, ViewAppearance { + public static var defaultAppearance: Self { + Self() } } } diff --git a/TIBottomSheet/Sources/BottomSheet/BaseModalViewController.swift b/TIBottomSheet/Sources/BottomSheet/BaseModalViewController.swift index 92f1b5c3..8f340340 100644 --- a/TIBottomSheet/Sources/BottomSheet/BaseModalViewController.swift +++ b/TIBottomSheet/Sources/BottomSheet/BaseModalViewController.swift @@ -112,7 +112,7 @@ open class BaseModalViewController [ModalViewPresentationDetent] { - presentationDetents.uniqued().sorted() + viewControllerAppearance.presentationDetents.uniqued().sorted() } private func getHeight(of view: UIView) -> CGFloat { diff --git a/TIBottomSheet/Sources/BottomSheet/DefaultModalWrapperViewController.swift b/TIBottomSheet/Sources/BottomSheet/DefaultModalWrapperViewController.swift index 342de463..b70b056a 100644 --- a/TIBottomSheet/Sources/BottomSheet/DefaultModalWrapperViewController.swift +++ b/TIBottomSheet/Sources/BottomSheet/DefaultModalWrapperViewController.swift @@ -30,6 +30,9 @@ open class DefaultModalWrapperViewController UIView { + contentViewController.view + } } public extension UIViewController { diff --git a/TIBottomSheet/Sources/BottomSheet/Views/DragView.swift b/TIBottomSheet/Sources/BottomSheet/Views/DragView.swift index 22edd8ea..f66b4d3a 100644 --- a/TIBottomSheet/Sources/BottomSheet/Views/DragView.swift +++ b/TIBottomSheet/Sources/BottomSheet/Views/DragView.swift @@ -53,16 +53,12 @@ public final class DragView: BaseInitializableView, AppearanceConfigurable { public final class Appearance: UIView.BaseWrappedAppearance, WrappedViewAppearance { - public static var defaultAppearance: Appearance { - .make { dragView in - dragView.backgroundColor = Constants.dragViewBackgroundColor - dragView.border = Constants.dragViewBorder - - dragView.layout { layout in - layout.size = Constants.dragViewSize - layout.insets = .vertical(top: Constants.dragViewTopInset) - } - } + public static var defaultAppearance: Self { + Self(layout: DefaultWrappedLayout(insets: .vertical(top: Constants.dragViewTopInset), + size: Constants.dragViewSize, + centerOffset: .centerHorizontal()), + backgroundColor: Constants.dragViewBackgroundColor, + border: Constants.dragViewBorder) } } diff --git a/TIBottomSheet/Sources/BottomSheet/Views/ModalFooterView.swift b/TIBottomSheet/Sources/BottomSheet/Views/ModalFooterView.swift index ab83ddfb..51e84e39 100644 --- a/TIBottomSheet/Sources/BottomSheet/Views/ModalFooterView.swift +++ b/TIBottomSheet/Sources/BottomSheet/Views/ModalFooterView.swift @@ -29,6 +29,17 @@ open class ModalFooterView: ContainerView { public enum State { case hidden - case presented(BaseWrappedViewHolderAppearance) + case presented(Appearance) + } +} + +public extension ModalFooterView { + + final class Appearance: BaseWrappedViewHolderAppearance, + WrappedViewHolderAppearance { + public static var defaultAppearance: Self { + Self(layout: DefaultWrappedLayout(insets: .horizontal(.zero).vertical(bottom: .zero), + size: .fixedHeight(44))) + } } } diff --git a/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift b/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift index dc6ec762..7baf317c 100644 --- a/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift +++ b/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift @@ -53,7 +53,7 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable { leadingButton.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor) }() - public private(set) lazy var leftButtonConstraints: SubviewConstraints = { + public private(set) lazy var leadingButtonConstraints: SubviewConstraints = { let edgeConstraints = EdgeConstraints(leadingConstraint: leadingButton.leadingAnchor.constraint(equalTo: leadingAnchor), trailingConstraint: leftTrailingToRightLeadingConstraint, topConstraint: leadingButton.topAnchor.constraint(equalTo: topAnchor), @@ -77,11 +77,10 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable { trailingButton.leadingAnchor.constraint(equalTo: leadingAnchor) }() - public private(set) lazy var rightButtonConstraints: SubviewConstraints = { - let leadingConstraint = trailingButton.leadingAnchor.constraint(equalTo: leadingAnchor) + public private(set) lazy var trailingButtonConstraints: SubviewConstraints = { let trailingConstraint = trailingButton.trailingAnchor.constraint(equalTo: trailingAnchor) - let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint, + let edgeConstraints = EdgeConstraints(leadingConstraint: leftTrailingToRightLeadingConstraint, trailingConstraint: trailingConstraint, topConstraint: trailingButton.topAnchor.constraint(equalTo: topAnchor), bottomConstraint: trailingButton.bottomAnchor.constraint(equalTo: bottomAnchor)) @@ -133,31 +132,34 @@ open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable { switch state { case let .leadingButton(style): leadingButtonStyle = style - leftButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToSuperviewTrailing + leadingButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToSuperviewTrailing case let .trailingButton(style): trailingButtonStyle = style - rightButtonConstraints.edgeConstraints.leadingConstraint = rightLeadingToSuperviewLeadingConstraint + trailingButtonConstraints.edgeConstraints.leadingConstraint = rightLeadingToSuperviewLeadingConstraint case let .buttons(leading, trailing): leadingButtonStyle = leading trailingButtonStyle = trailing - leftButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToRightLeadingConstraint - rightButtonConstraints.edgeConstraints.leadingConstraint = leftTrailingToRightLeadingConstraint - - let spacing = leading.appearance.layout.insets.add(\.right, - to: \.left, - of: trailing.appearance.layout.insets) - - leftTrailingToRightLeadingConstraint.setActiveConstantOrDeactivate(constant: spacing) case let .custom(view, appearance): configureCustomView(view, withLayout: appearance.layout) view.configureUIView(appearance: appearance) } - configure(buttonStyle: leadingButtonStyle, forButton: leadingButton, constraints: leftButtonConstraints) - configure(buttonStyle: trailingButtonStyle, forButton: trailingButton, constraints: rightButtonConstraints) + configure(buttonStyle: leadingButtonStyle, forButton: leadingButton, constraints: leadingButtonConstraints) + configure(buttonStyle: trailingButtonStyle, forButton: trailingButton, constraints: trailingButtonConstraints) + + if let leadingButtonStyle, let trailingButtonStyle { + leadingButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToRightLeadingConstraint + trailingButtonConstraints.edgeConstraints.leadingConstraint = leftTrailingToRightLeadingConstraint + + let spacing = leadingButtonStyle.appearance.layout.insets.add(\.right, + to: \.left, + of: trailingButtonStyle.appearance.layout.insets) + + leftTrailingToRightLeadingConstraint.setActiveConstantOrDeactivate(constant: spacing) + } } // MARK: - Private methods @@ -210,7 +212,8 @@ public extension ModalHeaderView { final class Appearance: BaseWrappedAppearance, WrappedViewAppearance { public static var defaultAppearance: Self { - Self() + Self(layout: DefaultWrappedLayout(insets: .horizontal(.zero).vertical(top: .zero), + size: .fixedHeight(44))) } public var contentViewState: ContentViewState diff --git a/TIBottomSheet/Sources/BottomSheet/Views/PassthroughDimmedView.swift b/TIBottomSheet/Sources/BottomSheet/Views/PassthroughDimmedView.swift index 70056aed..96ec02d4 100644 --- a/TIBottomSheet/Sources/BottomSheet/Views/PassthroughDimmedView.swift +++ b/TIBottomSheet/Sources/BottomSheet/Views/PassthroughDimmedView.swift @@ -24,7 +24,7 @@ import UIKit import PanModal open class PassthroughDimmedView: DimmedView { - weak var hitTestHandlerView: UIView? + public weak var hitTestHandlerView: UIView? public var isTransparent = false diff --git a/TIUIElements/Sources/Appearance/UIButton+AppearanceConfigurable.swift b/TIUIElements/Sources/Appearance/UIButton+AppearanceConfigurable.swift index 6c7b56a5..00afb6a9 100644 --- a/TIUIElements/Sources/Appearance/UIButton+AppearanceConfigurable.swift +++ b/TIUIElements/Sources/Appearance/UIButton+AppearanceConfigurable.swift @@ -30,21 +30,21 @@ extension UIButton { appearance.textAttributes? .configure(button: self, with: title(for: state), - for: state) + for: state) if #available(iOS 15, *) { var config = configuration ?? .plain() configureInsets(in: &config, - contentInsets: appearance.contentLayout.contentInsets, - titleInsets: appearance.contentLayout.titleInsets, - imageInsets: appearance.contentLayout.imageInsets) + contentInsets: appearance.contentLayout.contentInsets.replacingNan(with: .zero), + titleInsets: appearance.contentLayout.titleInsets.replacingNan(with: .zero), + imageInsets: appearance.contentLayout.imageInsets.replacingNan(with: .zero)) configuration = config } else { - contentEdgeInsets = appearance.contentLayout.contentInsets - titleEdgeInsets = appearance.contentLayout.titleInsets - imageEdgeInsets = appearance.contentLayout.imageInsets + contentEdgeInsets = appearance.contentLayout.contentInsets.replacingNan(with: .zero) + titleEdgeInsets = appearance.contentLayout.titleInsets.replacingNan(with: .zero) + imageEdgeInsets = appearance.contentLayout.imageInsets.replacingNan(with: .zero) } super.configureUIView(appearance: appearance) @@ -67,7 +67,7 @@ extension UIButton { currentImage ] .compactMap { $0 } - .first { !($0.size.width.isZero || $0.size.height.isZero) } != nil + .contains { !($0.size.width.isZero || $0.size.height.isZero) } if hasNonEmptyImage { let leadingContentInset: CGFloat diff --git a/TIUIElements/Sources/Appearance/UIView+ViewLayout.swift b/TIUIElements/Sources/Appearance/UIView+ViewLayout.swift index 94904d82..4a7118a3 100644 --- a/TIUIElements/Sources/Appearance/UIView+ViewLayout.swift +++ b/TIUIElements/Sources/Appearance/UIView+ViewLayout.swift @@ -53,7 +53,7 @@ extension UIView { public var centerOffset: UIOffset public var insets: UIEdgeInsets - public init(insets: UIEdgeInsets = .zero, + public init(insets: UIEdgeInsets = .nan, size: CGSize = .infinity, centerOffset: UIOffset = .nan) { @@ -73,7 +73,7 @@ extension UIView { open class BaseSpecedWrappedLayout: BaseWrappedLayout { public var spacing: CGFloat - public init(insets: UIEdgeInsets = .zero, + public init(insets: UIEdgeInsets = .nan, size: CGSize = .infinity, centerOffset: UIOffset = .nan, spacing: CGFloat = .zero) { @@ -98,7 +98,7 @@ extension UIView { public var distribution: UIStackView.Distribution public var alignment: UIStackView.Alignment - public init(insets: UIEdgeInsets = .zero, + public init(insets: UIEdgeInsets = .nan, size: CGSize = .infinity, centerOffset: UIOffset = .nan, spacing: CGFloat = .zero, diff --git a/TIUIElements/Sources/Separators/ContainerSeparatorTableViewCell.swift b/TIUIElements/Sources/Separators/ContainerSeparatorTableViewCell.swift index 1f34f887..17a54326 100644 --- a/TIUIElements/Sources/Separators/ContainerSeparatorTableViewCell.swift +++ b/TIUIElements/Sources/Separators/ContainerSeparatorTableViewCell.swift @@ -27,15 +27,13 @@ open class ContainerSeparatorTableViewCell: ContainerTableViewCell private lazy var topSeparatorView = createTopSeparator() private lazy var bottomSeparatorView = createBottomSeparator() - private var topViewLeftConstraint: NSLayoutConstraint? - private var topViewRightConstraint: NSLayoutConstraint? - private var topViewTopConstraint: NSLayoutConstraint? - private var topViewHeightConstraint: NSLayoutConstraint? + private lazy var topSeparatorConstraints: SubviewConstraints = { + subviewConstraints(for: topSeparatorView) + }() - private var bottomViewLeftConstraint: NSLayoutConstraint? - private var bottomViewRightConstraint: NSLayoutConstraint? - private var bottomViewBottomConstraint: NSLayoutConstraint? - private var bottomViewHeightConstraint: NSLayoutConstraint? + private lazy var bottomSeparatorConstraints: SubviewConstraints = { + subviewConstraints(for: bottomSeparatorView) + }() open func createTopSeparator() -> UIView { .init() @@ -93,41 +91,17 @@ open class ContainerSeparatorTableViewCell: ContainerTableViewCell open override func configureLayout() { super.configureLayout() - if let separatorSuperview = topSeparatorView.superview { - topViewTopConstraint = topSeparatorView.topAnchor.constraint(equalTo: separatorSuperview.topAnchor) - topViewRightConstraint = separatorSuperview.rightAnchor.constraint(equalTo: topSeparatorView.rightAnchor) - topViewLeftConstraint = topSeparatorView.leftAnchor.constraint(equalTo: separatorSuperview.leftAnchor) + for view in [topSeparatorView, bottomSeparatorView] { + view.translatesAutoresizingMaskIntoConstraints = false } - - topViewHeightConstraint = topSeparatorView.heightAnchor.constraint(equalToConstant: 1) - - if let separatorSuperview = topSeparatorView.superview { - bottomViewRightConstraint = separatorSuperview.rightAnchor.constraint(equalTo: bottomSeparatorView.rightAnchor) - bottomViewLeftConstraint = bottomSeparatorView.leftAnchor.constraint(equalTo: separatorSuperview.leftAnchor) - bottomViewBottomConstraint = bottomSeparatorView.bottomAnchor.constraint(equalTo: separatorSuperview.bottomAnchor) - } - - bottomViewHeightConstraint = bottomSeparatorView.heightAnchor.constraint(equalToConstant: 1) - - NSLayoutConstraint.activate([ - topViewTopConstraint, - topViewRightConstraint, - topViewLeftConstraint, - topViewHeightConstraint, - bottomViewRightConstraint, - bottomViewLeftConstraint, - bottomViewBottomConstraint, - bottomViewHeightConstraint - ].compactMap { $0 }) } open override func configureAppearance() { super.configureAppearance() - [topSeparatorView, bottomSeparatorView].forEach { - $0.isHidden = true - $0.backgroundColor = .black - $0.translatesAutoresizingMaskIntoConstraints = false + for view in [topSeparatorView, bottomSeparatorView] { + view.isHidden = true + view.backgroundColor = .black } } @@ -136,20 +110,37 @@ open class ContainerSeparatorTableViewCell: ContainerTableViewCell private func updateTopSeparator(with appearance: SeparatorAppearance) { topSeparatorView.configureUIView(appearance: appearance) - topViewHeightConstraint?.constant = appearance.layout.size.height - - topViewTopConstraint?.constant = appearance.layout.insets.top - topViewLeftConstraint?.constant = appearance.layout.insets.left - topViewRightConstraint?.constant = appearance.layout.insets.right + topSeparatorConstraints.update(from: appearance.layout) } private func updateBottomSeparator(with appearance: SeparatorAppearance) { bottomSeparatorView.configureUIView(appearance: appearance) - bottomViewHeightConstraint?.constant = appearance.layout.size.height + bottomSeparatorConstraints.update(from: appearance.layout) + } - bottomViewBottomConstraint?.constant = appearance.layout.insets.bottom - bottomViewLeftConstraint?.constant = appearance.layout.insets.left - bottomViewRightConstraint?.constant = appearance.layout.insets.right + private func subviewConstraints(for seperatorView: UIView) -> SubviewConstraints { + let leadingConstraint = seperatorView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor) + let trailingConstraint = seperatorView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor) + let topConstraint = seperatorView.topAnchor.constraint(equalTo: contentView.topAnchor) + let bottomConstraint = seperatorView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + + let edgeConstraints = EdgeConstraints(leadingConstraint: leadingConstraint, + trailingConstraint: trailingConstraint, + topConstraint: topConstraint, + bottomConstraint: bottomConstraint) + + let centerXConstraint = seperatorView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor) + let centerYConstraint = seperatorView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) + + let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint, + centerYConstraint: centerYConstraint) + + let sizeConstraints = SizeConstraints(widthConstraint: seperatorView.widthAnchor.constraint(equalToConstant: .zero), + heightConstraint: seperatorView.heightAnchor.constraint(equalToConstant: .zero)) + + return SubviewConstraints(edgeConstraints: edgeConstraints, + centerConstraints: centerConstraints, + sizeConstraints: sizeConstraints) } } diff --git a/TIUIElements/Sources/Views/ListItemView/BaseListItemView.swift b/TIUIElements/Sources/Views/ListItemView/BaseListItemView.swift index 1fb43419..f1c9bbf7 100644 --- a/TIUIElements/Sources/Views/ListItemView/BaseListItemView.swift +++ b/TIUIElements/Sources/Views/ListItemView/BaseListItemView.swift @@ -184,7 +184,8 @@ open class BaseListItemView: ContainerT } let cachedCollectionFrame = wrappedView.frame - wrappedView.frame.size.width = targetSize.width - wrappedContentInsets.horizontal(onNan: .zero) + wrappedView.frame.size.width = max(targetSize.width - wrappedContentInsets.horizontal(onNan: .zero), .zero) let collectionContentHeight = wrappedView.collectionViewLayout.collectionViewContentSize.height wrappedView.frame = cachedCollectionFrame diff --git a/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift b/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift index c27fb07d..4e14ff55 100644 --- a/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift +++ b/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift @@ -26,6 +26,10 @@ public extension UIEdgeInsets { // MARK: - Factory methods + static var nan: Self { + .edges(.nan) + } + static func edges(_ insets: CGFloat) -> UIEdgeInsets { .init(top: insets, left: insets, bottom: insets, right: insets) } @@ -64,14 +68,21 @@ public extension UIEdgeInsets { .init(top: insets, left: left, bottom: insets, right: right) } - func horizontal(left: CGFloat = .zero, right: CGFloat = .zero) -> UIEdgeInsets { + func horizontal(left: CGFloat = .nan, right: CGFloat = .nan) -> UIEdgeInsets { .init(top: top, left: left, bottom: bottom, right: right) } - func vertical(top: CGFloat = .zero, bottom: CGFloat = .zero) -> UIEdgeInsets { + func vertical(top: CGFloat = .nan, bottom: CGFloat = .nan) -> UIEdgeInsets { .init(top: top, left: left, bottom: bottom, right: right) } + func replacingNan(with defaultValue: CGFloat) -> Self { + .init(top: top.isFinite ? top : defaultValue, + left: left.isFinite ? left : defaultValue, + bottom: bottom.isFinite ? bottom : defaultValue, + right: right.isFinite ? right : defaultValue) + } + func add(_ lhsKeyPath: KeyPath, to rhsKeyPath: KeyPath, of rhs: Self,