more refactoring
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
e4efd296bb
commit
18eec4241b
@ -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)
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user