// // 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 TIUIElements import TIUIKitCore import UIKit open class ModalHeaderView: BaseInitializableView, AppearanceConfigurable { // MARK: - Nested Types public enum State { case hidden case presented(Appearance) } public enum ContentViewState { case leadingButton(BaseButtonStyle) case trailingButton(BaseButtonStyle) case buttons(leading: BaseButtonStyle, trailing: BaseButtonStyle) case custom(view: UIView, appearance: UIView.BaseWrappedAppearance) } // MARK: - Public properties public let leadingButton = StatefulButton() public let trailingButton = StatefulButton() public private(set) lazy var leftTrailingToRightLeadingConstraint: NSLayoutConstraint = { leadingButton.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor) }() public private(set) lazy var leftTrailingToSuperviewTrailing: NSLayoutConstraint = { leadingButton.trailingAnchor.constraint(equalTo: trailingAnchor) }() public private(set) lazy var leadingButtonConstraints: SubviewConstraints = { let edgeConstraints = EdgeConstraints(leadingConstraint: leadingButton.leadingAnchor.constraint(equalTo: leadingAnchor), trailingConstraint: leftTrailingToRightLeadingConstraint, topConstraint: leadingButton.topAnchor.constraint(equalTo: topAnchor), bottomConstraint: leadingButton.bottomAnchor.constraint(equalTo: bottomAnchor)) let centerXConstraint = leadingButton.centerXAnchor.constraint(equalTo: centerXAnchor) let centerYConstraint = leadingButton.centerYAnchor.constraint(equalTo: centerYAnchor) let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint, centerYConstraint: centerYConstraint) let sizeConstraints = SizeConstraints(widthConstraint: leadingButton.widthAnchor.constraint(equalToConstant: .zero), heightConstraint: leadingButton.heightAnchor.constraint(equalToConstant: .zero)) return SubviewConstraints(edgeConstraints: edgeConstraints, centerConstraints: centerConstraints, sizeConstraints: sizeConstraints) }() public private(set) lazy var rightLeadingToSuperviewLeadingConstraint: NSLayoutConstraint = { trailingButton.leadingAnchor.constraint(equalTo: leadingAnchor) }() public private(set) lazy var trailingButtonConstraints: SubviewConstraints = { let trailingConstraint = trailingButton.trailingAnchor.constraint(equalTo: trailingAnchor) let edgeConstraints = EdgeConstraints(leadingConstraint: leftTrailingToRightLeadingConstraint, trailingConstraint: trailingConstraint, topConstraint: trailingButton.topAnchor.constraint(equalTo: topAnchor), bottomConstraint: trailingButton.bottomAnchor.constraint(equalTo: bottomAnchor)) let centerXConstraint = trailingButton.centerXAnchor.constraint(equalTo: centerXAnchor) let centerYConstraint = trailingButton.centerYAnchor.constraint(equalTo: centerYAnchor) let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint, centerYConstraint: centerYConstraint) let sizeConstraints = SizeConstraints(widthConstraint: trailingButton.widthAnchor.constraint(equalToConstant: .zero), heightConstraint: trailingButton.heightAnchor.constraint(equalToConstant: .zero)) return SubviewConstraints(edgeConstraints: edgeConstraints, centerConstraints: centerConstraints, sizeConstraints: sizeConstraints) }() public var customViewConstraints: SubviewConstraints? // MARK: - BaseInitializableView open override func addViews() { super.addViews() addSubviews(leadingButton, trailingButton) } open override func configureLayout() { super.configureLayout() for view in [leadingButton, trailingButton] { view.translatesAutoresizingMaskIntoConstraints = false } } // MARK: - AppearanceConfigurable open func configure(appearance: Appearance) { configureUIView(appearance: appearance) configureContentView(state: appearance.contentViewState) } open func configureContentView(state: ContentViewState) { var leadingButtonStyle: BaseButtonStyle? var trailingButtonStyle: BaseButtonStyle? switch state { case let .leadingButton(style): leadingButtonStyle = style leadingButtonConstraints.edgeConstraints.trailingConstraint = leftTrailingToSuperviewTrailing case let .trailingButton(style): trailingButtonStyle = style trailingButtonConstraints.edgeConstraints.leadingConstraint = rightLeadingToSuperviewLeadingConstraint case let .buttons(leading, trailing): leadingButtonStyle = leading trailingButtonStyle = trailing case let .custom(view, appearance): configureCustomView(view, withLayout: appearance.layout) view.configureUIView(appearance: appearance) } 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 private func configureCustomView(_ view: UIView, withLayout layout: WrappedViewLayout) { addSubview(view) view.translatesAutoresizingMaskIntoConstraints = false 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 customViewConstraints.update(from: layout) } private func configure(buttonStyle: BaseButtonStyle?, forButton button: StatefulButton, constraints: SubviewConstraints?) { guard let buttonStyle else { button.isHidden = true return } button.isHidden = false constraints?.update(from: buttonStyle.appearance.layout) button.apply(style: buttonStyle) } } // MARK: - Appearance public extension ModalHeaderView { final class Appearance: BaseWrappedAppearance, WrappedViewAppearance { public static var defaultAppearance: Self { Self(layout: DefaultWrappedLayout(insets: .horizontal(.zero).vertical(top: .zero), size: .fixedHeight(44))) } public var contentViewState: ContentViewState public init(layout: DefaultWrappedLayout = .defaultLayout, background: UIViewBackground = UIViewColorBackground(color: .clear), border: UIViewBorder = BaseUIViewBorder(), shadow: UIViewShadow? = nil, contentViewState: ContentViewState = .leadingButton(.init())) { self.contentViewState = contentViewState super.init(layout: layout, background: background, border: border, shadow: shadow) } } }