LeadKit/TIBottomSheet/Sources/BottomSheet/Views/ModalHeaderView.swift

228 lines
9.9 KiB
Swift

//
// 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 buttonLeft(BaseButtonStyle)
case buttonRight(BaseButtonStyle)
case buttons(left: BaseButtonStyle, right: BaseButtonStyle)
case custom(view: UIView, appearance: UIView.BaseWrappedAppearance<UIView.DefaultWrappedLayout>)
}
// MARK: - Public properties
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 = {
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 = {
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
open override func addViews() {
super.addViews()
addSubviews(leftButton, rightButton)
}
open override func configureLayout() {
super.configureLayout()
[leftButton, rightButton]
.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
}
// MARK: - AppearanceConfigurable
open func configure(appearance: Appearance) {
configureUIView(appearance: appearance)
configureContentView(state: appearance.contentViewState)
}
open func configureContentView(state: ContentViewState) {
var leftButtonStyle: BaseButtonStyle?
var rightButtonStyle: BaseButtonStyle?
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)
}
configure(buttonStyle: leftButtonStyle, forButton: leftButton, constraints: leftButtonConstraints)
configure(buttonStyle: rightButtonStyle, forButton: rightButton, constraints: rightButtonConstraints)
}
// 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<DefaultWrappedLayout>, WrappedViewAppearance {
public static var defaultAppearance: Self {
Self()
}
public var contentViewState: ContentViewState
public init(layout: DefaultWrappedLayout = .defaultLayout,
backgroundColor: UIColor = .clear,
border: UIViewBorder = .init(),
shadow: UIViewShadow? = nil,
contentViewState: ContentViewState = .buttonLeft(.init())) {
self.contentViewState = contentViewState
super.init(layout: layout, backgroundColor: backgroundColor, border: border, shadow: shadow)
}
}
}