more refactoring

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2023-05-19 15:48:49 -05:00
parent e4efd296bb
commit 18eec4241b

View File

@ -9,7 +9,7 @@ import Foundation
import UIKit import UIKit
import VDSColorTokens import VDSColorTokens
public struct TabItemModel { public struct TabModel {
public var text: String public var text: String
public var onClick: (() -> Void)? public var onClick: (() -> Void)?
public var width: CGFloat? public var width: CGFloat?
@ -55,52 +55,20 @@ public class Tabs: View {
case value(CGFloat) 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 onTabChange: ((Int) -> Void)?
public var orientation: Orientation = .vertical { didSet { setNeedsUpdate() } }
public var overflow: Overflow = .none { public var borderLine: Bool = true { didSet { setNeedsUpdate() } }
didSet { setNeedsLayout() } public var fillContainer: Bool = false { didSet { setNeedsUpdate() } }
} public var indicatorFillTab: Bool = false { didSet { setNeedsUpdate() } }
public var indicatorPosition: IndicatorPosition = .bottom { didSet { setNeedsUpdate() } }
public var selectedIndex: Int = 0 { public var minWidth: CGFloat = 44.0 { didSet { setNeedsUpdate() } }
didSet { setNeedsLayout() } public var overflow: Overflow = .scroll { didSet { setNeedsUpdate() } }
} public var selectedIndex: Int = 0 { didSet { setNeedsUpdate() } }
public var size: Size = .medium { didSet { setNeedsUpdate() } }
public var size: Size = .medium { public var sticky: Bool = false { didSet { setNeedsUpdate() } }
didSet { setNeedsLayout() } public var width: Width = .percentage(0.25) { didSet { setNeedsUpdate() } }
} public var tabModels: [TabModel] = [] { didSet { updateTabItems(with: tabModels) } }
public var sticky: Bool = false {
didSet { setNeedsLayout() }
}
public var width: Width = .percentage(0.25) {
didSet { setNeedsLayout() }
}
private var tabItems: [TabItem] = [] private var tabItems: [TabItem] = []
private var tabStackView: UIStackView! private var tabStackView: UIStackView!
private var scrollView: UIScrollView! private var scrollView: UIScrollView!
@ -109,7 +77,6 @@ public class Tabs: View {
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
commonInit()
} }
public convenience required init() { public convenience required init() {
@ -118,10 +85,10 @@ public class Tabs: View {
public required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
commonInit()
} }
private func commonInit() { open override func setup() {
super.setup()
scrollView = UIScrollView() scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.showsHorizontalScrollIndicator = false scrollView.showsHorizontalScrollIndicator = false
@ -145,7 +112,7 @@ public class Tabs: View {
contentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true contentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true
} }
public func updateTabItems(with models: [TabItemModel]) { private func updateTabItems(with models: [TabModel]) {
// Clear existing tab items // Clear existing tab items
for tabItem in tabItems { for tabItem in tabItems {
tabItem.removeFromSuperview() tabItem.removeFromSuperview()
@ -158,7 +125,6 @@ public class Tabs: View {
tabItem.text = model.text tabItem.text = model.text
tabItem.onClick = model.onClick tabItem.onClick = model.onClick
tabItem.width = model.width tabItem.width = model.width
tabItem.textStyle = size.textStyle
tabItems.append(tabItem) tabItems.append(tabItem)
tabStackView.addArrangedSubview(tabItem) tabStackView.addArrangedSubview(tabItem)
@ -167,8 +133,7 @@ public class Tabs: View {
tabItem.isUserInteractionEnabled = true tabItem.isUserInteractionEnabled = true
tabItem.addGestureRecognizer(tapGesture) tabItem.addGestureRecognizer(tapGesture)
} }
setNeedsUpdate()
setNeedsLayout()
} }
@objc private func tabItemTapped(_ gesture: UITapGestureRecognizer) { @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() { public override func layoutSubviews() {
super.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 // Apply border line
layer.remove(layerName: "borderLineLayer") 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 { if borderLine {
let borderLineLayer = CALayer() let borderLineLayer = CALayer()
borderLineLayer.name = "borderLineLayer" borderLineLayer.name = "borderLineLayer"
@ -228,35 +190,7 @@ public class Tabs: View {
layer.addSublayer(borderLineLayer) 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 // Apply width
if orientation == .vertical { if orientation == .vertical {
switch width { switch width {
@ -265,38 +199,41 @@ public class Tabs: View {
case .value(let amount): case .value(let amount):
contentView.widthAnchor.constraint(equalToConstant: amount).isActive = true 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 { } 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 // Enable scrolling if necessary
let contentWidth = contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width let contentWidth = contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width
scrollView.contentSize = CGSize(width: contentWidth, height: scrollView.bounds.height) scrollView.contentSize = CGSize(width: contentWidth, height: scrollView.bounds.height)
//addDebugBorder(color: .blue)
} }
} }
public class TabItem: View { 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 label: Label = Label()
public var onClick: (() -> Void)? { didSet { setNeedsUpdate() } } public var onClick: (() -> Void)? { didSet { setNeedsUpdate() } }
public var width: CGFloat? { didSet { setNeedsUpdate() } } public var width: CGFloat? { didSet { setNeedsUpdate() } }
public var selected: Bool = false { didSet { setNeedsUpdate() } } public var selected: Bool = false { didSet { setNeedsUpdate() } }
public var text: String? { 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 labelMinWidthConstraint: NSLayoutConstraint?
private var labelWidthConstraint: NSLayoutConstraint? private var labelWidthConstraint: NSLayoutConstraint?
private var labelLeadingConstraint: NSLayoutConstraint?
private var textColorConfiguration: SurfaceColorConfiguration { selected ? textColorSelectedConfiguration : textColorNonSelectedConfiguration } private var textColorConfiguration: SurfaceColorConfiguration { selected ? textColorSelectedConfiguration : textColorNonSelectedConfiguration }
private var textColorNonSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOnlight) private var textColorNonSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOnlight)
private var textColorSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark) 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) { public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
@ -307,9 +244,10 @@ public class TabItem: View {
label.translatesAutoresizingMaskIntoConstraints = false label.translatesAutoresizingMaskIntoConstraints = false
label label
.pinTop(5) .pinTop(5)
.pinLeading(5)
.pinTrailing(5) .pinTrailing(5)
.pinBottom(6) .pinBottom(6)
labelLeadingConstraint = label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0)
labelLeadingConstraint?.isActive = true
labelMinWidthConstraint = label.widthAnchor.constraint(greaterThanOrEqualToConstant: 44.0) labelMinWidthConstraint = label.widthAnchor.constraint(greaterThanOrEqualToConstant: 44.0)
labelMinWidthConstraint?.isActive = true labelMinWidthConstraint?.isActive = true
labelWidthConstraint = label.widthAnchor.constraint(equalToConstant: 44.0) labelWidthConstraint = label.widthAnchor.constraint(equalToConstant: 44.0)
@ -331,9 +269,13 @@ public class TabItem: View {
} }
public override func updateView() { public override func updateView() {
label.textPosition = textPosition if orientation == .horizontal {
label.textPosition = .center
} else {
label.textPosition = .left
}
label.text = text label.text = text
label.textStyle = textStyle label.textStyle = size.textStyle
label.textColor = textColorConfiguration.getColor(self) label.textColor = textColorConfiguration.getColor(self)
if let width { if let width {
labelMinWidthConstraint?.isActive = false labelMinWidthConstraint?.isActive = false
@ -344,11 +286,30 @@ public class TabItem: View {
labelMinWidthConstraint?.isActive = true labelMinWidthConstraint?.isActive = true
} }
if let indicatorPosition { if selected {
addBorder(side: indicatorPosition, width: 4.0, color: .red) 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 { } else {
removeBorders() removeBorders()
} }
setNeedsDisplay()
} }
// public override func draw(_ rect: CGRect) {
// super.draw(rect)
//
// addDebugBorder(color: .green)
// }
} }