// // 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 UIKit import TIUIKitCore open class BaseStackView: UIStackView { public var customArrangedSubviews: [View] = [] @available(*, unavailable, message: "Use strongly-typed version of this function") open override func addArrangedSubview(_ view: UIView) { super.addArrangedSubview(view) } @available(*, unavailable, message: "Use strongly-typed version of this function") open override func removeArrangedSubview(_ view: UIView) { super.removeArrangedSubview(view) } @available(*, unavailable, message: "Use strongly-typed version of this function") open override func insertArrangedSubview(_ view: UIView, at stackIndex: Int) { super.insertArrangedSubview(view, at: stackIndex) } open func addArrangedSubview(_ view: View) { customArrangedSubviews.append(view) super.addArrangedSubview(view) } open func removeArrangedSubview(_ view: View) { guard let indexOfView = customArrangedSubviews.firstIndex(where: { $0 === view }) else { assertionFailure("Unable to find \(view) in \(customArrangedSubviews)") return } customArrangedSubviews.remove(at: indexOfView) super.removeArrangedSubview(view) } open func replaceArrangedSubviews(_ newViews: [View]) { for existingSubview in customArrangedSubviews { removeArrangedSubview(existingSubview) } for view in newViews { addArrangedSubview(view) } } open func insertArrangedSubview(_ view: View, at stackIndex: Int) { customArrangedSubviews.insert(view, at: stackIndex) super.insertArrangedSubview(view, at: stackIndex) } open func configureUIStackView(appearance: BaseWrappedAppearance) { configureUIView(appearance: appearance) axis = appearance.layout.axis distribution = appearance.layout.distribution alignment = appearance.layout.alignment spacing = appearance.layout.spacing } } extension BaseStackView: ConfigurableView where View: ConfigurableView { public func configure(with viewModels: [View.ViewModelType]) { replaceArrangedSubviews(viewModels.map { View(viewModel: $0) }) } } extension BaseStackView: AppearanceConfigurable where View: AppearanceConfigurable { public final class Appearance: UIView.BaseWrappedAppearance, WrappedViewAppearance { public static var defaultAppearance: Self { Self() } public var arrangedSubviewsAppearance: View.Appearance public init(layout: UIView.DefaultStackLayout = .defaultLayout, backgroundColor: UIColor = .clear, border: UIViewBorder = .init(), shadow: UIViewShadow? = nil, arrangedSubviewsAppearance: View.Appearance = .defaultAppearance) { self.arrangedSubviewsAppearance = arrangedSubviewsAppearance super.init(layout: layout, backgroundColor: backgroundColor, border: border, shadow: shadow) } } public func configure(appearance: Appearance) { configureUIStackView(appearance: appearance) for customSubview in customArrangedSubviews { customSubview.configure(appearance: appearance.arrangedSubviewsAppearance) } } }