diff --git a/VDS/BaseClasses/Control.swift b/VDS/BaseClasses/Control.swift index cc2246aa..274d7d9b 100644 --- a/VDS/BaseClasses/Control.swift +++ b/VDS/BaseClasses/Control.swift @@ -35,13 +35,7 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- open var subscribers = Set() - open var onClickSubscriber: AnyCancellable? { - willSet { - if let onClickSubscriber { - onClickSubscriber.cancel() - } - } - } + open var onClickSubscriber: AnyCancellable? //-------------------------------------------------- // MARK: - Private Properties @@ -118,6 +112,8 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { backgroundColor = .clear surface = .light isEnabled = true + onClick = nil + userInfo.removeAll() } //-------------------------------------------------- diff --git a/VDS/BaseClasses/Selector/SelectorBase.swift b/VDS/BaseClasses/Selector/SelectorBase.swift index 9552b50e..38d087cc 100644 --- a/VDS/BaseClasses/Selector/SelectorBase.swift +++ b/VDS/BaseClasses/Selector/SelectorBase.swift @@ -47,13 +47,7 @@ open class SelectorBase: Control, SelectorControlable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var onChangeSubscriber: AnyCancellable? { - willSet { - if let onChangeSubscriber { - onChangeSubscriber.cancel() - } - } - } + open var onChangeSubscriber: AnyCancellable? open var size = CGSize(width: 20, height: 20) { didSet { setNeedsUpdate() } } @@ -120,13 +114,6 @@ open class SelectorBase: Control, SelectorControlable { accessibilityTraits = .button } - /// Used to make changes to the View based off a change events or from local properties. - open override func updateView() { - super.updateView() - setNeedsLayout() - layoutIfNeeded() - } - /// Used to update any Accessibility properties.ß open override func updateAccessibility() { super.updateAccessibility() @@ -135,4 +122,8 @@ open class SelectorBase: Control, SelectorControlable { /// This will change the state of the Selector and execute the actionBlock if provided. open func toggle() { } + open override func reset() { + super.reset() + onChange = nil + } } diff --git a/VDS/BaseClasses/Selector/SelectorGroupBase.swift b/VDS/BaseClasses/Selector/SelectorGroupBase.swift index 64b3cdee..997a71b2 100644 --- a/VDS/BaseClasses/Selector/SelectorGroupBase.swift +++ b/VDS/BaseClasses/Selector/SelectorGroupBase.swift @@ -75,13 +75,7 @@ open class SelectorGroupBase: Control, SelectorGroup, } } - open var onChangeSubscriber: AnyCancellable? { - willSet { - if let onChangeSubscriber { - onChangeSubscriber.cancel() - } - } - } + open var onChangeSubscriber: AnyCancellable? /// Whether the Control is enabled or not. override open var isEnabled: Bool { @@ -130,6 +124,7 @@ open class SelectorGroupBase: Control, SelectorGroup, /// Resets to default settings. open override func reset() { super.reset() + onChange = nil items.forEach{ $0.reset() } } } diff --git a/VDS/BaseClasses/Selector/SelectorItemBase.swift b/VDS/BaseClasses/Selector/SelectorItemBase.swift index 1900c2c5..9eee8f29 100644 --- a/VDS/BaseClasses/Selector/SelectorItemBase.swift +++ b/VDS/BaseClasses/Selector/SelectorItemBase.swift @@ -61,13 +61,7 @@ open class SelectorItemBase: Control, Errorable, //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var onChangeSubscriber: AnyCancellable? { - willSet { - if let onChangeSubscriber { - onChangeSubscriber.cancel() - } - } - } + open var onChangeSubscriber: AnyCancellable? /// Label used to render labelText. open var label = Label().with { @@ -223,6 +217,7 @@ open class SelectorItemBase: Control, Errorable, value = nil isSelected = false + onChange = nil shouldUpdateView = true setNeedsUpdate() } diff --git a/VDS/Components/BadgeIndicator/BadgeIndicator.swift b/VDS/Components/BadgeIndicator/BadgeIndicator.swift index 89d886c6..a327270d 100644 --- a/VDS/Components/BadgeIndicator/BadgeIndicator.swift +++ b/VDS/Components/BadgeIndicator/BadgeIndicator.swift @@ -344,7 +344,6 @@ open class BadgeIndicator: View { label.isEnabled = isEnabled label.sizeToFit() setNeedsLayout() - layoutIfNeeded() } open override func updateAccessibility() { diff --git a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift index 08a823fa..424ebcf3 100644 --- a/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift +++ b/VDS/Components/Breadcrumbs/BreadcrumbCellItem.swift @@ -84,7 +84,7 @@ final class BreadcrumbCellItem: UICollectionViewCell { separator.textColor = textColorConfiguration.getColor(surface) separator.isHidden = hideSlash self.breadCrumbItem = breadCrumbItem - layoutIfNeeded() + setNeedsLayout() } } diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index b0694848..80010ee5 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -38,13 +38,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { /// Set of Subscribers for any Publishers for this Control. open var subscribers = Set() - open var onClickSubscriber: AnyCancellable? { - willSet { - if let onClickSubscriber { - onClickSubscriber.cancel() - } - } - } + open var onClickSubscriber: AnyCancellable? //-------------------------------------------------- // MARK: - Private Properties @@ -140,6 +134,8 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { isEnabled = true text = nil accessibilityCustomActions = [] + onClick = nil + userInfo.removeAll() shouldUpdateView = true setNeedsUpdate() } diff --git a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift index 864397d3..658cb05e 100644 --- a/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift +++ b/VDS/Components/Icon/ButtonIcon/ButtonIcon.swift @@ -109,7 +109,8 @@ open class ButtonIcon: Control, Changeable, FormFieldable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - + public var onChangeSubscriber: AnyCancellable? + ///Badge Indicator object used to render for the ButtonIcon. open var badgeIndicator = BadgeIndicator().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -172,8 +173,6 @@ open class ButtonIcon: Control, Changeable, FormFieldable { /// Used to move the icon inside the button in both x and y axis. open var iconOffset: CGPoint = .init(x: 0, y: 0) { didSet { setNeedsUpdate() } } - open var onChangeSubscriber: AnyCancellable? - open var inputId: String? { didSet { setNeedsUpdate() } } open var value: AnyHashable? { didSet { setNeedsUpdate() } } @@ -365,7 +364,6 @@ open class ButtonIcon: Control, Changeable, FormFieldable { open override func setup() { super.setup() isAccessibilityElement = false - accessibilityElements = [icon, badgeIndicator] //create a layoutGuide for the icon to key off of let iconLayoutGuide = UILayoutGuide() @@ -436,6 +434,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable { showBadgeIndicator = false selectable = false badgeIndicatorModel = nil + onChange = nil shouldUpdateView = true setNeedsUpdate() } @@ -459,6 +458,18 @@ open class ButtonIcon: Control, Changeable, FormFieldable { setNeedsLayout() } + open override func updateAccessibility() { + super.updateAccessibility() + var elements = [Any]() + if iconName != nil { + elements.append(icon) + } + if badgeIndicatorModel != nil && showBadgeIndicator { + elements.append(badgeIndicator) + } + accessibilityElements = elements.count > 0 ? elements : nil + } + open override func layoutSubviews() { super.layoutSubviews() diff --git a/VDS/Components/Label/Attributes/ColorLabelAttribute.swift b/VDS/Components/Label/Attributes/ColorLabelAttribute.swift index 50229ab0..fd1b9a21 100644 --- a/VDS/Components/Label/Attributes/ColorLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/ColorLabelAttribute.swift @@ -31,12 +31,13 @@ public struct ColorLabelAttribute: LabelAttributeModel { } public func setAttribute(on attributedString: NSMutableAttributedString) { - guard isValidRange(on: attributedString) else { return } - var colorRange = range if length == 0 && location == 0 { colorRange = .init(location: location, length: attributedString.length) } + + if !attributedString.isValid(range: colorRange) { return } + let attributeKey = isForegroundColor ? NSAttributedString.Key.foregroundColor : NSAttributedString.Key.backgroundColor attributedString.removeAttribute(attributeKey, range: colorRange) attributedString.addAttribute(attributeKey, value: color, range: colorRange) diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index df67d431..febb12ca 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -230,7 +230,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable { setNeedsDisplay() setNeedsLayout() - layoutIfNeeded() } open func updateAccessibility() { diff --git a/VDS/Components/RadioBox/RadioBoxItem.swift b/VDS/Components/RadioBox/RadioBoxItem.swift index de6223e1..c4105f42 100644 --- a/VDS/Components/RadioBox/RadioBoxItem.swift +++ b/VDS/Components/RadioBox/RadioBoxItem.swift @@ -52,13 +52,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var onChangeSubscriber: AnyCancellable? { - willSet { - if let onChangeSubscriber { - onChangeSubscriber.cancel() - } - } - } + open var onChangeSubscriber: AnyCancellable? /// Label used to render the text. open var textLabel = Label().with { @@ -218,6 +212,7 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { value = nil isSelected = false + onChange = nil shouldUpdateView = true setNeedsUpdate() @@ -236,7 +231,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable { updateLabels() setNeedsLayout() - layoutIfNeeded() } /// Used to update any Accessibility properties. diff --git a/VDS/Components/Tabs/Tab.swift b/VDS/Components/Tabs/Tab.swift index af095d85..e96e20d6 100644 --- a/VDS/Components/Tabs/Tab.swift +++ b/VDS/Components/Tabs/Tab.swift @@ -172,7 +172,6 @@ extension Tabs { label.textAlignment = textAlignment.value label.textColorConfiguration = textColorConfiguration.eraseToAnyColorable() setNeedsLayout() - layoutIfNeeded() } /// Used to update any Accessibility properties. diff --git a/VDS/Components/TextFields/EntryFieldBase.swift b/VDS/Components/TextFields/EntryFieldBase.swift index 7ce12d75..9f7cf49d 100644 --- a/VDS/Components/TextFields/EntryFieldBase.swift +++ b/VDS/Components/TextFields/EntryFieldBase.swift @@ -119,13 +119,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var onChangeSubscriber: AnyCancellable? { - willSet { - if let onChangeSubscriber { - onChangeSubscriber.cancel() - } - } - } + open var onChangeSubscriber: AnyCancellable? open var titleLabel = Label().with { $0.setContentCompressionResistancePriority(.required, for: .vertical) @@ -304,6 +298,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable { defaultValue = nil required = false readOnly = false + onChange = nil } /// Used to make changes to the View based off a change events or from local properties. diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 61b91eaf..bdf19831 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -8,6 +8,7 @@ import Foundation import VDSTokens import UIKit +import Combine @objc(VDSTileContainer) open class TileContainer: TileContainerBase { @@ -43,7 +44,6 @@ open class TileContainer: TileContainerBase { } open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -111,6 +111,19 @@ open class TileContainerBase: Control where Padding //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + public override var onClickSubscriber: AnyCancellable? { + didSet { + if onClickSubscriber != nil { + isAccessibilityElement = true + accessibilityTraits = .link + accessibilityHint = "Double tap to open." + } else { + isAccessibilityElement = false + accessibilityTraits.remove(.link) + } + } + } + /// This takes an image source url and applies it as a background image. open var backgroundImage: UIImage? { didSet { setNeedsUpdate() } } @@ -171,7 +184,7 @@ open class TileContainerBase: Control where Padding open var showBorder: Bool = false { didSet { setNeedsUpdate() } } /// Determines if there is a drop shadow or not. - open var showDropShadows: Bool = false { didSet { setNeedsUpdate() } } + open var showDropShadow: Bool = false { didSet { setNeedsUpdate() } } //-------------------------------------------------- // MARK: - Constraints @@ -274,7 +287,7 @@ open class TileContainerBase: Control where Padding width = nil height = nil showBorder = false - showDropShadows = false + showDropShadow = false shouldUpdateView = true setNeedsUpdate() } @@ -316,12 +329,14 @@ open class TileContainerBase: Control where Padding widthConstraint?.isActive = false heightConstraint?.isActive = false } - if showDropShadows, surface == .light { + + applyBackgroundEffects() + + if showDropShadow, surface == .light { addDropShadow(dropShadowConfiguration) } else { removeDropShadows() } - applyBackgroundEffects() } /// Used to update frames for the added CAlayers to our view @@ -337,6 +352,7 @@ open class TileContainerBase: Control where Padding /// This will place a view within the contentView of this component. public func addContentView(_ view: UIView, shouldPin: Bool = true) { + view.removeFromSuperview() containerView.addSubview(view) if shouldPin { view.pinToSuperView() diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index ba03ed5f..fb0e5cf8 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -22,7 +22,7 @@ open class Tilelet: TileContainerBase { public enum Padding: String, DefaultValuing, CaseIterable { case small case large - + public static var defaultValue: Self { .large } public var value: CGFloat { @@ -63,7 +63,7 @@ open class Tilelet: TileContainerBase { public required init?(coder: NSCoder) { super.init(coder: coder) } - + //-------------------------------------------------- // MARK: - Enums //-------------------------------------------------- @@ -105,12 +105,6 @@ open class Tilelet: TileContainerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public override var onClickSubscriber: AnyCancellable? { - didSet { - isAccessibilityElement = onClickSubscriber != nil - } - } - /// Title lockup positioned in the contentView. open var titleLockup = TitleLockup().with { $0.standardStyleConfiguration = .init(styleConfigurations: [ @@ -213,7 +207,7 @@ open class Tilelet: TileContainerBase { open var directionalIcon = Icon().with { $0.name = .rightArrow } - + private var _textWidth: TextWidth? /// If provided, width of Button components will be rendered based on this value. If omitted, default button widths are rendered. @@ -276,7 +270,7 @@ open class Tilelet: TileContainerBase { _descriptiveIconModel = nil setNeedsUpdate() } - } + } //-------------------------------------------------- // MARK: - Constraints @@ -294,7 +288,7 @@ open class Tilelet: TileContainerBase { internal var titleLockupEyebrowLabelHeightGreaterThanConstraint: NSLayoutConstraint? internal var titleLockupTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? internal var titleLockupSubTitleLabelHeightGreaterThanConstraint: NSLayoutConstraint? - + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- @@ -305,9 +299,6 @@ open class Tilelet: TileContainerBase { color = .black addContentView(stackView) - accessibilityTraits = .link - accessibilityElements = [badge, titleLockup, descriptiveIcon, directionalIcon] - //badge badgeContainerView.addSubview(badge) badge @@ -328,7 +319,7 @@ open class Tilelet: TileContainerBase { titleLockupBottomGreaterThanConstraint = titleLockupContainerView.bottomAnchor.constraint(greaterThanOrEqualTo: titleLockup.bottomAnchor) titleLockupTopGreaterThanConstraint = titleLockup.topAnchor.constraint(greaterThanOrEqualTo: titleLockupContainerView.topAnchor) titleLockupCenterYConstraint = titleLockup.centerYAnchor.constraint(equalTo: titleLockupContainerView.centerYAnchor) - + iconContainerView.addSubview(descriptiveIcon) iconContainerView.addSubview(directionalIcon) @@ -357,12 +348,12 @@ open class Tilelet: TileContainerBase { badge.label.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-1), for: .vertical) titleLockup.subTitleLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-2), for: .vertical) titleLockup.eyebrowLabel.setContentCompressionResistancePriority(UILayoutPriority(labelPriority-3), for: .vertical) - + titleLockup.titleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority), for: .vertical) badge.label.setContentHuggingPriority(UILayoutPriority(labelPriority-1), for: .vertical) titleLockup.subTitleLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-2), for: .vertical) titleLockup.eyebrowLabel.setContentHuggingPriority(UILayoutPriority(labelPriority-3), for: .vertical) - + /** Added these constraints for: At fixed width & height if all the labels(Badge, Eyebrow, Title, Subtitle) are having more number of lines then we should display atleast one line of content per label instead of pushing labels out of bounds. @@ -412,13 +403,27 @@ open class Tilelet: TileContainerBase { if width != nil && (aspectRatio != .none || height != nil) { updateTextPositionAlignment() } - layoutIfNeeded() + setNeedsLayout() } /// Used to update any Accessibility properties. open override func updateAccessibility() { super.updateAccessibility() - + var elements = [Any]() + if badgeModel != nil { + elements.append(badge) + } + if titleModel != nil || subTitleModel != nil || eyebrowModel != nil { + elements.append(titleLockup) + } + if descriptiveIconModel != nil { + elements.append(descriptiveIcon) + } + if directionalIconModel != nil { + elements.append(directionalIcon) + } + accessibilityElements = elements.count > 0 ? elements : nil + setAccessibilityLabel(for: [badge.label, titleLockup.eyebrowLabel, titleLockup.titleLabel, titleLockup.subTitleLabel]) } diff --git a/VDS/Components/TitleLockup/TitleLockup.swift b/VDS/Components/TitleLockup/TitleLockup.swift index a8fab6fa..c0c3b0b5 100644 --- a/VDS/Components/TitleLockup/TitleLockup.swift +++ b/VDS/Components/TitleLockup/TitleLockup.swift @@ -272,7 +272,21 @@ open class TitleLockup: View { titleLabel.textColorConfiguration = textColorPrimaryConfiguration - accessibilityElements = [eyebrowLabel, titleLabel, subTitleLabel] + } + + open override func updateAccessibility() { + super.updateAccessibility() + var elements = [Any]() + if eyebrowModel != nil { + elements.append(eyebrowLabel) + } + if titleModel != nil { + elements.append(titleLabel) + } + if subTitleModel != nil { + elements.append(subTitleLabel) + } + accessibilityElements = elements.count > 0 ? elements : nil } /// Resets to default settings. diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 9e77ed12..b73630d6 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -88,13 +88,7 @@ open class Toggle: Control, Changeable, FormFieldable { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - open var onChangeSubscriber: AnyCancellable? { - willSet { - if let onChangeSubscriber { - onChangeSubscriber.cancel() - } - } - } + open var onChangeSubscriber: AnyCancellable? /// Actual toggle used in this component. open var toggleView = ToggleView().with { @@ -231,6 +225,7 @@ open class Toggle: Control, Changeable, FormFieldable { textPosition = .left inputId = nil value = nil + onChange = nil shouldUpdateView = true setNeedsUpdate() } diff --git a/VDS/Components/Toggle/ToggleView.swift b/VDS/Components/Toggle/ToggleView.swift index ef88d914..58001857 100644 --- a/VDS/Components/Toggle/ToggleView.swift +++ b/VDS/Components/Toggle/ToggleView.swift @@ -166,6 +166,7 @@ open class ToggleView: Control, Changeable, FormFieldable { value = nil toggleView.backgroundColor = toggleColorConfiguration.getColor(self) knobView.backgroundColor = knobColorConfiguration.getColor(self) + onChange = nil shouldUpdateView = true setNeedsUpdate() } @@ -227,7 +228,6 @@ open class ToggleView: Control, Changeable, FormFieldable { knobTrailingConstraint?.isActive = true knobLeadingConstraint?.isActive = true setNeedsLayout() - layoutIfNeeded() } private func updateToggle() { diff --git a/VDS/Protocols/Changeable.swift b/VDS/Protocols/Changeable.swift index 6ef3eb8e..c44fa841 100644 --- a/VDS/Protocols/Changeable.swift +++ b/VDS/Protocols/Changeable.swift @@ -26,6 +26,8 @@ extension Changeable { .sink { c in newValue(c) } + } else { + onChangeSubscriber = nil } } } diff --git a/VDS/Protocols/Clickable.swift b/VDS/Protocols/Clickable.swift index 29b66aaf..eddcfa3c 100644 --- a/VDS/Protocols/Clickable.swift +++ b/VDS/Protocols/Clickable.swift @@ -20,6 +20,7 @@ extension Clickable { public var onClick: ((Self) -> ())? { get { return nil } set { + onClickSubscriber?.cancel() if let newValue { onClickSubscriber = publisher(for: .touchUpInside) .sink { [weak self] c in @@ -27,7 +28,6 @@ extension Clickable { newValue(c) } } else { - onClickSubscriber?.cancel() onClickSubscriber = nil } }