304 lines
14 KiB
Swift
304 lines
14 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 TIUIKitCore
|
|
import UIKit
|
|
|
|
open class BaseListItemView<LeadingView: UIView,
|
|
MiddleView: UIView,
|
|
TrailingView: UIView>: BaseInitializableView {
|
|
|
|
public let leadingView = LeadingView()
|
|
public let middleView = MiddleView()
|
|
public let trailingView = TrailingView()
|
|
|
|
// MARK: - Constraints
|
|
|
|
public private(set) lazy var leadingViewTrailingToSuperviewConstraint: NSLayoutConstraint = {
|
|
leadingView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
|
}()
|
|
|
|
public private(set) lazy var leadingToTrailingViewConstraint: NSLayoutConstraint = {
|
|
leadingView.trailingAnchor.constraint(equalTo: trailingView.leadingAnchor)
|
|
}()
|
|
|
|
public private(set) lazy var leadingViewConstraints: SubviewConstraints = {
|
|
let edgeConstraints = EdgeConstraints(leadingConstraint: leadingView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
trailingConstraint: middleViewLeadingToLeadingViewConstraint,
|
|
topConstraint: leadingView.topAnchor.constraint(equalTo: topAnchor),
|
|
bottomConstraint: leadingView.bottomAnchor.constraint(equalTo: bottomAnchor))
|
|
|
|
let centerXConstraint = leadingView.centerXAnchor.constraint(equalTo: centerXAnchor)
|
|
let centerYConstraint = leadingView.centerYAnchor.constraint(equalTo: centerYAnchor)
|
|
|
|
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
|
centerYConstraint: centerYConstraint)
|
|
|
|
let sizeConstraints = SizeConstraints(widthConstraint: leadingView.widthAnchor.constraint(equalToConstant: .zero),
|
|
heightConstraint: leadingView.heightAnchor.constraint(equalToConstant: .zero))
|
|
|
|
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
|
centerConstraints: centerConstraints,
|
|
sizeConstraints: sizeConstraints)
|
|
}()
|
|
|
|
public private(set) lazy var middleViewLeadingToLeadingViewConstraint: NSLayoutConstraint = {
|
|
middleView.leadingAnchor.constraint(equalTo: leadingView.trailingAnchor)
|
|
}()
|
|
|
|
public private(set) lazy var middleToTrailingViewConstraint: NSLayoutConstraint = {
|
|
middleView.trailingAnchor.constraint(equalTo: trailingView.leadingAnchor)
|
|
}()
|
|
|
|
public private(set) lazy var middleViewLeadingToSuperViewConstraint: NSLayoutConstraint = {
|
|
middleView.leadingAnchor.constraint(equalTo: leadingAnchor)
|
|
}()
|
|
|
|
public private(set) lazy var middleViewTrailingToSuperViewConstraint: NSLayoutConstraint = {
|
|
middleView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
|
}()
|
|
|
|
public private(set) lazy var middleViewConstraints: SubviewConstraints = {
|
|
let edgeConstraints = EdgeConstraints(leadingConstraint: middleViewLeadingToLeadingViewConstraint,
|
|
trailingConstraint: middleToTrailingViewConstraint,
|
|
topConstraint: middleView.topAnchor.constraint(equalTo: topAnchor),
|
|
bottomConstraint: middleView.bottomAnchor.constraint(equalTo: bottomAnchor))
|
|
|
|
let centerXConstraint = middleView.centerXAnchor.constraint(equalTo: centerXAnchor)
|
|
let centerYConstraint = middleView.centerYAnchor.constraint(equalTo: centerYAnchor)
|
|
|
|
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
|
centerYConstraint: centerYConstraint)
|
|
|
|
let sizeConstraints = SizeConstraints(widthConstraint: middleView.widthAnchor.constraint(equalToConstant: .zero),
|
|
heightConstraint: middleView.heightAnchor.constraint(equalToConstant: .zero))
|
|
|
|
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
|
centerConstraints: centerConstraints,
|
|
sizeConstraints: sizeConstraints)
|
|
}()
|
|
|
|
public private(set) lazy var trailingViewLeadingToSuperviewConstraint: NSLayoutConstraint = {
|
|
trailingView.leadingAnchor.constraint(equalTo: leadingAnchor)
|
|
}()
|
|
|
|
public private(set) lazy var trailingViewConstraints: SubviewConstraints = {
|
|
let trailingConstraint = trailingView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
|
|
|
let edgeConstraints = EdgeConstraints(leadingConstraint: middleToTrailingViewConstraint,
|
|
trailingConstraint: trailingConstraint,
|
|
topConstraint: trailingView.topAnchor.constraint(equalTo: topAnchor),
|
|
bottomConstraint: trailingView.bottomAnchor.constraint(equalTo: bottomAnchor))
|
|
|
|
let centerXConstraint = middleView.centerXAnchor.constraint(equalTo: centerXAnchor)
|
|
let centerYConstraint = trailingView.centerYAnchor.constraint(equalTo: centerYAnchor)
|
|
|
|
let centerConstraints = CenterConstraints(centerXConstraint: centerXConstraint,
|
|
centerYConstraint: centerYConstraint)
|
|
|
|
let sizeConstraints = SizeConstraints(widthConstraint: trailingView.widthAnchor.constraint(equalToConstant: .zero),
|
|
heightConstraint: trailingView.heightAnchor.constraint(equalToConstant: .zero))
|
|
|
|
return SubviewConstraints(edgeConstraints: edgeConstraints,
|
|
centerConstraints: centerConstraints,
|
|
sizeConstraints: sizeConstraints)
|
|
}()
|
|
|
|
// MARK: - Public Properties
|
|
|
|
open var isLeadingViewHidden: Bool {
|
|
leadingView.isHidden
|
|
}
|
|
|
|
open var isMiddleViewHidden: Bool {
|
|
middleView.isHidden
|
|
}
|
|
|
|
open var isTrailingViewHidden: Bool {
|
|
trailingView.isHidden
|
|
}
|
|
|
|
// MARK: - BaseInitializableView
|
|
|
|
open override func addViews() {
|
|
super.addViews()
|
|
|
|
addSubviews(leadingView, middleView, trailingView)
|
|
}
|
|
|
|
open override func configureLayout() {
|
|
super.configureLayout()
|
|
|
|
[leadingView, middleView, trailingView]
|
|
.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
|
|
}
|
|
|
|
// MARK: - Public methods
|
|
|
|
public func configureBaseListItem(appearance: BaseListItemAppearance<some WrappedViewAppearance,
|
|
some WrappedViewAppearance,
|
|
some WrappedViewAppearance>) {
|
|
|
|
configureUIView(appearance: appearance)
|
|
|
|
let leadingViewTrailingSpacing = updateLeadingConstaints(leadingViewLayout: appearance.leadingViewAppearance.layout,
|
|
middleViewLayout: appearance.middleViewAppearance.layout,
|
|
trailingViewLayout: appearance.trailingAppearance.layout)
|
|
|
|
let trailingViewLeadingSpacing = updateTrailingConstraints(trailingViewLayout: appearance.trailingAppearance.layout,
|
|
middleViewLayout: appearance.middleViewAppearance.layout,
|
|
leadingViewLayout: appearance.leadingViewAppearance.layout)
|
|
|
|
updateMiddleConstraints(middleViewLayout: appearance.middleViewAppearance.layout,
|
|
leadingViewTrailingSpacing: leadingViewTrailingSpacing,
|
|
trailingViewLeadingSpacing: trailingViewLeadingSpacing)
|
|
}
|
|
|
|
// MARK: - Private methdos
|
|
|
|
private func updateLeadingConstaints(leadingViewLayout: WrappedViewLayout,
|
|
middleViewLayout: WrappedViewLayout,
|
|
trailingViewLayout: WrappedViewLayout) -> CGFloat {
|
|
|
|
switch (isLeadingViewHidden, isMiddleViewHidden) {
|
|
case (true, true):
|
|
leadingViewConstraints.deactivate()
|
|
|
|
return UIEdgeInsets.nan.right
|
|
|
|
case (false, false):
|
|
leadingViewConstraints.edgeConstraints.trailingConstraint.isActive = false
|
|
leadingViewConstraints.edgeConstraints.trailingConstraint = middleViewLeadingToLeadingViewConstraint
|
|
|
|
leadingViewConstraints.update(from: leadingViewLayout)
|
|
|
|
return middleViewLayout.insets.add(\.left,
|
|
to: \.right,
|
|
of: leadingViewLayout.insets)
|
|
|
|
case (true, false):
|
|
leadingViewConstraints.deactivate()
|
|
|
|
return middleViewLayout.insets.left
|
|
|
|
case (false, true):
|
|
leadingViewConstraints.edgeConstraints.trailingConstraint.isActive = false
|
|
|
|
defer {
|
|
leadingViewConstraints.update(from: leadingViewLayout)
|
|
}
|
|
|
|
if isTrailingViewHidden {
|
|
leadingViewConstraints.edgeConstraints.trailingConstraint = leadingViewTrailingToSuperviewConstraint
|
|
|
|
return -leadingViewLayout.insets.right
|
|
} else {
|
|
leadingViewConstraints.edgeConstraints.trailingConstraint = leadingToTrailingViewConstraint
|
|
|
|
return leadingViewLayout.insets.add(\.right,
|
|
to: \.right,
|
|
of: trailingViewLayout.insets)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func updateMiddleConstraints(middleViewLayout: WrappedViewLayout,
|
|
leadingViewTrailingSpacing: CGFloat,
|
|
trailingViewLeadingSpacing: CGFloat) {
|
|
|
|
guard !isMiddleViewHidden else {
|
|
middleViewConstraints.deactivate()
|
|
return
|
|
}
|
|
|
|
middleViewConstraints.edgeConstraints.leadingConstraint.isActive = false
|
|
|
|
if isLeadingViewHidden {
|
|
middleViewConstraints.edgeConstraints.leadingConstraint = middleViewLeadingToSuperViewConstraint
|
|
} else {
|
|
middleViewConstraints.edgeConstraints.leadingConstraint = middleViewLeadingToLeadingViewConstraint
|
|
}
|
|
|
|
middleViewConstraints.edgeConstraints.trailingConstraint.isActive = false
|
|
|
|
if isTrailingViewHidden {
|
|
middleViewConstraints.edgeConstraints.trailingConstraint = middleViewTrailingToSuperViewConstraint
|
|
} else {
|
|
middleViewConstraints.edgeConstraints.trailingConstraint = middleToTrailingViewConstraint
|
|
}
|
|
|
|
middleViewConstraints.update(from: middleViewLayout)
|
|
|
|
middleViewConstraints.edgeConstraints.leadingConstraint
|
|
.setActiveConstantOrDeactivate(constant: leadingViewTrailingSpacing)
|
|
|
|
middleViewConstraints.edgeConstraints.trailingConstraint
|
|
.setActiveConstantOrDeactivate(constant: trailingViewLeadingSpacing)
|
|
}
|
|
|
|
private func updateTrailingConstraints(trailingViewLayout: WrappedViewLayout,
|
|
middleViewLayout: WrappedViewLayout,
|
|
leadingViewLayout: WrappedViewLayout) -> CGFloat {
|
|
|
|
switch (isMiddleViewHidden, isTrailingViewHidden) {
|
|
case (true, true):
|
|
trailingViewConstraints.deactivate()
|
|
|
|
return UIEdgeInsets.nan.left
|
|
|
|
case (true, false):
|
|
trailingViewConstraints.edgeConstraints.leadingConstraint.isActive = false
|
|
|
|
defer {
|
|
trailingViewConstraints.update(from: trailingViewLayout)
|
|
}
|
|
|
|
if isLeadingViewHidden {
|
|
trailingViewConstraints.edgeConstraints.leadingConstraint = trailingViewLeadingToSuperviewConstraint
|
|
|
|
return trailingViewLayout.insets.left
|
|
} else {
|
|
trailingViewConstraints.edgeConstraints.leadingConstraint = middleToTrailingViewConstraint
|
|
|
|
return trailingViewLayout.insets.add(\.left,
|
|
to: \.right,
|
|
of: middleViewLayout.insets)
|
|
}
|
|
|
|
case (false, true):
|
|
trailingViewConstraints.deactivate()
|
|
|
|
return -middleViewLayout.insets.right
|
|
|
|
case (false, false):
|
|
trailingViewConstraints.edgeConstraints.leadingConstraint.isActive = false
|
|
trailingViewConstraints.edgeConstraints.leadingConstraint = middleToTrailingViewConstraint
|
|
|
|
trailingViewConstraints.update(from: trailingViewLayout)
|
|
|
|
return trailingViewLayout.insets.add(\.right,
|
|
to: \.left,
|
|
of: middleViewLayout.insets)
|
|
}
|
|
}
|
|
}
|