192 lines
7.3 KiB
Swift
192 lines
7.3 KiB
Swift
//
|
|
// Tab.swift
|
|
// VDS
|
|
//
|
|
// Created by Matt Bruce on 5/18/23.
|
|
//
|
|
|
|
import Foundation
|
|
import VDSColorTokens
|
|
import Combine
|
|
|
|
extension Tabs {
|
|
|
|
@objc(VDSTab)
|
|
open class Tab: Control {
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Initializers
|
|
//--------------------------------------------------
|
|
required public init() {
|
|
super.init(frame: .zero)
|
|
}
|
|
|
|
public override init(frame: CGRect) {
|
|
super.init(frame: .zero)
|
|
}
|
|
|
|
public required init?(coder: NSCoder) {
|
|
super.init(coder: coder)
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Private Properties
|
|
//--------------------------------------------------
|
|
/// TextStyle used on the label.
|
|
private var textStyle: TextStyle {
|
|
if size == .medium {
|
|
return .boldBodyLarge
|
|
} else {
|
|
//specs show that the font size shouldn't change however boldTitleSmall does
|
|
//change point size between iPad/iPhone. This is a "fix" so each device will
|
|
//load the correct pointSize
|
|
return UIDevice.isIPad ? .boldTitleSmall : .boldTitleMedium
|
|
}
|
|
}
|
|
|
|
private var labelWidthConstraint: NSLayoutConstraint?
|
|
private var labelLeadingConstraint: NSLayoutConstraint?
|
|
private var labelTopConstraint: NSLayoutConstraint?
|
|
private var labelBottomConstraint: NSLayoutConstraint?
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Public Properties
|
|
//--------------------------------------------------
|
|
///position of the tab
|
|
open var index: Int = 0
|
|
|
|
///label to write out the text
|
|
open var label: Label = Label().with {
|
|
$0.isUserInteractionEnabled = false
|
|
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
$0.setContentHuggingPriority(.required, for: .horizontal)
|
|
}
|
|
|
|
///orientation of the tabs
|
|
open var orientation: Tabs.Orientation = .horizontal { didSet { setNeedsUpdate() } }
|
|
|
|
///Size for tab
|
|
open var size: Tabs.Size = .medium { didSet { setNeedsUpdate() } }
|
|
|
|
///Text position left or center
|
|
open var textPosition: TextAlignment = .left { didSet { setNeedsUpdate() } }
|
|
|
|
///Sets the Position of the Selected/Hover Border Accent for All Tabs.
|
|
open var indicatorPosition: Tabs.IndicatorPosition = .bottom { didSet { setNeedsUpdate() } }
|
|
|
|
///If provided, it will set fixed width for this Tab.
|
|
open var width: CGFloat? { didSet { setNeedsUpdate() } }
|
|
|
|
///The text label of the tab.
|
|
open var text: String = "Tab" { didSet { setNeedsUpdate() } }
|
|
|
|
///Minimum width for the tab
|
|
open var minWidth: CGFloat = 44.0 { didSet { setNeedsUpdate() } }
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Configuration
|
|
//--------------------------------------------------
|
|
private var textColorConfiguration: SurfaceColorConfiguration { isSelected ? textColorSelectedConfiguration : textColorNonSelectedConfiguration }
|
|
private var textColorNonSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsSecondaryOnlight , VDSColor.elementsSecondaryOndark)
|
|
private var textColorSelectedConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
|
|
private var indicatorColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteRed, VDSColor.elementsPrimaryOndark)
|
|
private var indicatorWidth: CGFloat = 4.0
|
|
|
|
private var indicatorSide: UIRectEdge {
|
|
guard orientation == .horizontal else { return .left }
|
|
return indicatorPosition.value
|
|
}
|
|
|
|
private var leadingSpace: CGFloat {
|
|
guard orientation == .vertical else { return 0 }
|
|
return VDSLayout.Spacing.space4X.value
|
|
}
|
|
|
|
private var otherSpace: CGFloat {
|
|
if orientation == .horizontal {
|
|
return size == .medium ? VDSLayout.Spacing.space3X.value : VDSLayout.Spacing.space4X.value
|
|
} else {
|
|
return VDSLayout.Spacing.space2X.value
|
|
}
|
|
}
|
|
|
|
private let layoutGuide = UILayoutGuide()
|
|
|
|
private func updateWidth() {
|
|
labelWidthConstraint?.isActive = false
|
|
guard let width, width > minWidth else { return }
|
|
labelWidthConstraint?.constant = width
|
|
labelWidthConstraint?.isActive = true
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// MARK: - Overrides
|
|
//--------------------------------------------------
|
|
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
|
|
open override func setup() {
|
|
super.setup()
|
|
|
|
canHighlight = false
|
|
|
|
addLayoutGuide(layoutGuide)
|
|
addSubview(label)
|
|
accessibilityTraits = .button
|
|
isAccessibilityElement = true
|
|
|
|
//pin layoutguide
|
|
layoutGuide.pinToSuperView()
|
|
|
|
//pin trailing
|
|
label.pinTrailing(layoutGuide.trailingAnchor)
|
|
|
|
//setup constraints
|
|
labelWidthConstraint = layoutGuide.width(constant: 0).with { $0.isActive = false }
|
|
layoutGuide.widthGreaterThanEqualTo(minWidth)
|
|
labelTopConstraint = label.pinTop(anchor: layoutGuide.topAnchor)
|
|
labelLeadingConstraint = label.pinLeading(anchor: layoutGuide.leadingAnchor)
|
|
labelBottomConstraint = label.pinBottom(anchor: layoutGuide.bottomAnchor, priority: .defaultHigh)
|
|
}
|
|
|
|
/// Used to make changes to the View based off a change events or from local properties.
|
|
open override func updateView() {
|
|
super.updateView()
|
|
|
|
guard !text.isEmpty else { return }
|
|
|
|
accessibilityIdentifier = "VDSTab:\(text)"
|
|
|
|
//constaints
|
|
updateWidth()
|
|
labelLeadingConstraint?.constant = leadingSpace
|
|
labelTopConstraint?.constant = otherSpace
|
|
labelBottomConstraint?.constant = -otherSpace
|
|
|
|
//label properties
|
|
label.text = text
|
|
label.surface = surface
|
|
label.textStyle = textStyle
|
|
label.textPosition = textPosition
|
|
label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable()
|
|
setNeedsLayout()
|
|
layoutIfNeeded()
|
|
|
|
}
|
|
|
|
/// Used to update any Accessibility properties.
|
|
open override func updateAccessibility() {
|
|
super.updateAccessibility()
|
|
accessibilityLabel = text
|
|
}
|
|
|
|
open override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
|
|
removeBorders()
|
|
|
|
if isSelected {
|
|
addBorder(side: indicatorSide, width: indicatorWidth, color: indicatorColorConfiguration.getColor(self))
|
|
}
|
|
}
|
|
}
|
|
}
|