vds_ios/VDS/Components/Footnote/FootnoteGroup.swift

180 lines
6.1 KiB
Swift

//
// FootnoteGroup.swift
// VDS
//
// Created by Kanamarlapudi, Vasavi on 29/08/24.
//
import Foundation
import UIKit
import VDSCoreTokens
/// This must always be paired with one or more ``Footnote`` in a FootnoteGroup.
@objcMembers
@objc(VDSFootnoteGroup)
open class FootnoteGroup: View {
//--------------------------------------------------
// 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: - enums
//--------------------------------------------------
/// Enum used to describe the width of a fixed value or percentage of parent's width.
public enum Width {
case percentage(CGFloat)
case value(CGFloat)
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
/// Array of ``Footnote`` for the Footnote items.
open var footnoteItems: [Footnote] = [] { didSet { setNeedsUpdate() } }
/// Any percentage or pixel value and cannot exceed container size.
/// If there is a width that is larger than container size, the footnote will resize to container's width.
open var width: Width? {
get { _width }
set {
if let newValue {
switch newValue {
case .percentage(let percentage):
if percentage <= 100.0 {
_width = newValue
}
case .value(let value):
if value > 0 {
_width = newValue
}
}
} else {
_width = nil
}
setNeedsUpdate()
}
}
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var _width: Width? = nil
private lazy var stackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.axis = .vertical
$0.distribution = .fill
$0.spacing = VDSLayout.space3X
$0.backgroundColor = .clear
}
//--------------------------------------------------
// MARK: - Configuration Properties
//--------------------------------------------------
internal var maxWidth: CGFloat { frame.size.width }
internal var minWidth: CGFloat { containerSize.width }
internal var containerSize: CGSize { CGSize(width: 55, height: 44) }
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
internal var widthConstraint: NSLayoutConstraint?
internal var trailingEqualsConstraint: NSLayoutConstraint?
internal var trailingLessThanEqualsConstraint: NSLayoutConstraint?
//--------------------------------------------------
// 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()
// add footnote item stackview.
addSubview(stackView)
stackView.pinTop().pinBottom().pinLeading()
trailingEqualsConstraint = stackView.pinTrailing(anchor: trailingAnchor)
// width constraints
trailingLessThanEqualsConstraint = stackView.pinTrailingLessThanOrEqualTo(anchor: trailingAnchor)?.deactivate()
widthConstraint = stackView.widthAnchor.constraint(equalToConstant: 0).deactivate()
}
open override func setDefaults() {
super.setDefaults()
width = nil
footnoteItems = []
}
/// Resets to default settings.
open override func reset() {
super.reset()
}
/// Used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
updateContainerWidth()
// symbol containers are as wide as the widest symbol container in the group.
var symbolMaxWidth = 0.0
if footnoteItems.count > 0 {
for index in 0...footnoteItems.count - 1 {
let footnote: Footnote = footnoteItems[index]
let separatorWidth = Label().with { $0.text = footnote.symbolType; $0.sizeToFit() }.intrinsicContentSize.width
symbolMaxWidth = separatorWidth > symbolMaxWidth ? separatorWidth : symbolMaxWidth
}
}
stackView.subviews.forEach{$0.removeFromSuperview()}
// add symbol label, text label to stack.
if footnoteItems.count > 0 {
for index in 0...footnoteItems.count - 1 {
let footnote: Footnote = footnoteItems[index]
footnote.symbolWiderWidth = symbolMaxWidth
footnote.surface = surface
stackView.addArrangedSubview(footnote)
}
}
}
/// Update container width after updating content.
internal func updateContainerWidth() {
var newWidth = 0.0
switch width {
case .percentage(let percentage):
newWidth = max(maxWidth * ((percentage) / 100), minWidth)
case .value(let value):
newWidth = value > maxWidth ? maxWidth : value
case nil:
newWidth = maxWidth
}
widthConstraint?.deactivate()
trailingLessThanEqualsConstraint?.deactivate()
trailingEqualsConstraint?.deactivate()
if newWidth > minWidth && newWidth < maxWidth {
widthConstraint?.constant = newWidth
widthConstraint?.activate()
trailingLessThanEqualsConstraint?.activate()
} else {
trailingEqualsConstraint?.activate()
}
}
}