From 18eec4241bebc79309773c89fc2897248ec6f7ac Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 19 May 2023 15:48:49 -0500 Subject: [PATCH] more refactoring Signed-off-by: Matt Bruce --- VDS/Components/Tabs/Tabs.swift | 217 ++++++++++++++------------------- 1 file changed, 89 insertions(+), 128 deletions(-) diff --git a/VDS/Components/Tabs/Tabs.swift b/VDS/Components/Tabs/Tabs.swift index 761c41d9..2febfc1b 100644 --- a/VDS/Components/Tabs/Tabs.swift +++ b/VDS/Components/Tabs/Tabs.swift @@ -9,7 +9,7 @@ import Foundation import UIKit import VDSColorTokens -public struct TabItemModel { +public struct TabModel { public var text: String public var onClick: (() -> Void)? public var width: CGFloat? @@ -55,52 +55,20 @@ public class Tabs: View { case value(CGFloat) } - public var orientation: Orientation = .horizontal { - didSet { setNeedsLayout() } - } - - public var borderLine: Bool = true { - didSet { setNeedsLayout() } - } - - public var fillContainer: Bool = true { - didSet { setNeedsLayout() } - } - - public var indicatorFillTab: Bool = false { - didSet { setNeedsLayout() } - } - - public var indicatorPosition: IndicatorPosition = .bottom { - didSet { setNeedsLayout() } - } - - public var minWidth: CGFloat = 44.0 { - didSet { setNeedsLayout() } - } - public var onTabChange: ((Int) -> Void)? - - public var overflow: Overflow = .none { - didSet { setNeedsLayout() } - } - - public var selectedIndex: Int = 0 { - didSet { setNeedsLayout() } - } - - public var size: Size = .medium { - didSet { setNeedsLayout() } - } - - public var sticky: Bool = false { - didSet { setNeedsLayout() } - } - - public var width: Width = .percentage(0.25) { - didSet { setNeedsLayout() } - } - + public var orientation: Orientation = .vertical { didSet { setNeedsUpdate() } } + public var borderLine: Bool = true { didSet { setNeedsUpdate() } } + public var fillContainer: Bool = false { didSet { setNeedsUpdate() } } + public var indicatorFillTab: Bool = false { didSet { setNeedsUpdate() } } + public var indicatorPosition: IndicatorPosition = .bottom { didSet { setNeedsUpdate() } } + public var minWidth: CGFloat = 44.0 { didSet { setNeedsUpdate() } } + public var overflow: Overflow = .scroll { didSet { setNeedsUpdate() } } + public var selectedIndex: Int = 0 { didSet { setNeedsUpdate() } } + public var size: Size = .medium { didSet { setNeedsUpdate() } } + public var sticky: Bool = false { didSet { setNeedsUpdate() } } + public var width: Width = .percentage(0.25) { didSet { setNeedsUpdate() } } + public var tabModels: [TabModel] = [] { didSet { updateTabItems(with: tabModels) } } + private var tabItems: [TabItem] = [] private var tabStackView: UIStackView! private var scrollView: UIScrollView! @@ -109,7 +77,6 @@ public class Tabs: View { public override init(frame: CGRect) { super.init(frame: frame) - commonInit() } public convenience required init() { @@ -118,10 +85,10 @@ public class Tabs: View { public required init?(coder: NSCoder) { super.init(coder: coder) - commonInit() } - private func commonInit() { + open override func setup() { + super.setup() scrollView = UIScrollView() scrollView.translatesAutoresizingMaskIntoConstraints = false scrollView.showsHorizontalScrollIndicator = false @@ -145,7 +112,7 @@ public class Tabs: View { contentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true } - public func updateTabItems(with models: [TabItemModel]) { + private func updateTabItems(with models: [TabModel]) { // Clear existing tab items for tabItem in tabItems { tabItem.removeFromSuperview() @@ -158,7 +125,6 @@ public class Tabs: View { tabItem.text = model.text tabItem.onClick = model.onClick tabItem.width = model.width - tabItem.textStyle = size.textStyle tabItems.append(tabItem) tabStackView.addArrangedSubview(tabItem) @@ -167,8 +133,7 @@ public class Tabs: View { tabItem.isUserInteractionEnabled = true tabItem.addGestureRecognizer(tapGesture) } - - setNeedsLayout() + setNeedsUpdate() } @objc private func tabItemTapped(_ gesture: UITapGestureRecognizer) { @@ -180,41 +145,38 @@ public class Tabs: View { } } + public override func updateView() { + super.updateView() + if fillContainer { + tabStackView.distribution = .fillEqually + } else { + tabStackView.distribution = .fill + } + tabStackView.axis = orientation == .horizontal ? .horizontal : .vertical + tabStackView.alignment = orientation == .horizontal ? .fill : .leading + tabStackView.spacing = orientation == .horizontal ? VDSLayout.Spacing.space6X.value : VDSLayout.Spacing.space4X.value + + setNeedsLayout() + } + public override func layoutSubviews() { super.layoutSubviews() - // Update tab appearance based on properties - - for (index, tabItem) in tabItems.enumerated() { - if selectedIndex == index { - // Apply selected style to the current tab - if orientation == .vertical { - tabItem.indicatorPosition = .left - } else { - if indicatorPosition == .top { - tabItem.indicatorPosition = .top - } else { - tabItem.indicatorPosition = .bottom - } - } - tabItem.selected = true - } else { - // Apply default style to other tabs - tabItem.indicatorPosition = nil - tabItem.selected = false - } - - if orientation == .horizontal { - tabItem.textPosition = .center - } else { - tabItem.textPosition = .left - } - tabItem.surface = surface - } - // Apply border line layer.remove(layerName: "borderLineLayer") + // Update tab appearance based on properties + for (index, tabItem) in tabItems.enumerated() { + if selectedIndex == index { + tabItem.selected = true + } else { + tabItem.selected = false + } + tabItem.size = size + tabItem.orientation = orientation + tabItem.surface = surface + } + if borderLine { let borderLineLayer = CALayer() borderLineLayer.name = "borderLineLayer" @@ -228,35 +190,7 @@ public class Tabs: View { layer.addSublayer(borderLineLayer) } - - // Apply fill container - tabStackView.alignment = fillContainer && orientation == .horizontal ? .fill : .leading - - // Apply indicator fill tab - if orientation == .vertical { - if indicatorFillTab { - tabItems.forEach { $0.label.textAlignment = .left } - } else { - tabItems.forEach { $0.label.textAlignment = .center } - } - } - - // Apply indicator position - if orientation == .horizontal { - if indicatorPosition == .top { - tabStackView.alignment = .top - } else if indicatorPosition == .bottom { - tabStackView.alignment = .bottom - } - } - - // Apply sticky - if sticky && orientation == .vertical { - scrollView.pinTop() - } else { - scrollView.pinTop(layoutMargins.top) - } - + // Apply width if orientation == .vertical { switch width { @@ -265,38 +199,41 @@ public class Tabs: View { case .value(let amount): contentView.widthAnchor.constraint(equalToConstant: amount).isActive = true } - } - - // Apply overflow - if orientation == .horizontal && overflow == .scroll { - let contentWidth = tabStackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width - contentView.widthAnchor.constraint(equalToConstant: contentWidth).isActive = true } else { - contentView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true + // Apply overflow + if orientation == .horizontal && overflow == .scroll { + let contentWidth = tabStackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width + contentView.widthAnchor.constraint(equalToConstant: contentWidth).isActive = true + } else { + contentView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true + } } // Enable scrolling if necessary let contentWidth = contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width scrollView.contentSize = CGSize(width: contentWidth, height: scrollView.bounds.height) + + //addDebugBorder(color: .blue) } } public class TabItem: View { + public var orientation: Tabs.Orientation = .horizontal { didSet { setNeedsUpdate() } } + public var size: Tabs.Size = .medium { didSet { setNeedsUpdate() } } public var label: Label = Label() public var onClick: (() -> Void)? { didSet { setNeedsUpdate() } } public var width: CGFloat? { didSet { setNeedsUpdate() } } public var selected: Bool = false { didSet { setNeedsUpdate() } } public var text: String? { didSet { setNeedsUpdate() } } - public var textStyle: TextStyle = .bodyMedium - public var textPosition: TextPosition = .center { didSet { setNeedsUpdate() } } - public var indicatorPosition: UIRectEdge? { didSet { setNeedsUpdate() } } private var labelMinWidthConstraint: NSLayoutConstraint? private var labelWidthConstraint: NSLayoutConstraint? - + private var labelLeadingConstraint: NSLayoutConstraint? + private var textColorConfiguration: SurfaceColorConfiguration { selected ? textColorSelectedConfiguration : textColorNonSelectedConfiguration } private var textColorNonSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOnlight) private var textColorSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) - + private var indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteRed, VDSColor.elementsPrimaryOndark) + private var indicatorWidth: CGFloat = 4.0 public override init(frame: CGRect) { super.init(frame: frame) @@ -307,9 +244,10 @@ public class TabItem: View { label.translatesAutoresizingMaskIntoConstraints = false label .pinTop(5) - .pinLeading(5) .pinTrailing(5) .pinBottom(6) + labelLeadingConstraint = label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0) + labelLeadingConstraint?.isActive = true labelMinWidthConstraint = label.widthAnchor.constraint(greaterThanOrEqualToConstant: 44.0) labelMinWidthConstraint?.isActive = true labelWidthConstraint = label.widthAnchor.constraint(equalToConstant: 44.0) @@ -331,9 +269,13 @@ public class TabItem: View { } public override func updateView() { - label.textPosition = textPosition + if orientation == .horizontal { + label.textPosition = .center + } else { + label.textPosition = .left + } label.text = text - label.textStyle = textStyle + label.textStyle = size.textStyle label.textColor = textColorConfiguration.getColor(self) if let width { labelMinWidthConstraint?.isActive = false @@ -344,11 +286,30 @@ public class TabItem: View { labelMinWidthConstraint?.isActive = true } - if let indicatorPosition { - addBorder(side: indicatorPosition, width: 4.0, color: .red) + if selected { + var indicatorPosition: UIRectEdge = .top + if orientation == .vertical { + indicatorPosition = .left + } else { + if indicatorPosition == .top { + indicatorPosition = .top + } else { + indicatorPosition = .bottom + } + } + addBorder(side: indicatorPosition, width: indicatorWidth, color: indicatorColorConfiguration.getColor(self)) } else { removeBorders() } + + setNeedsDisplay() } + +// public override func draw(_ rect: CGRect) { +// super.draw(rect) +// +// addDebugBorder(color: .green) +// } + }