Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios into vasavk/breadcrumbs
This commit is contained in:
commit
52cc0a52c7
@ -20,8 +20,10 @@
|
|||||||
5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; };
|
5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; };
|
||||||
5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; };
|
5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; };
|
||||||
7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; };
|
7115BD3C2B84C0C200E0A610 /* TileContainerChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */; };
|
||||||
71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */; };
|
71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */; };
|
||||||
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; };
|
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; };
|
||||||
|
71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */; };
|
||||||
|
71FC86E02B973AE500700965 /* DropShadowConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */; };
|
||||||
EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; };
|
EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; };
|
||||||
EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; };
|
EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; };
|
||||||
EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; };
|
EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; };
|
||||||
@ -192,8 +194,10 @@
|
|||||||
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = "<group>"; };
|
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = "<group>"; };
|
||||||
5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
||||||
7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = "<group>"; };
|
7115BD3B2B84C0C200E0A610 /* TileContainerChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = TileContainerChangeLog.txt; sourceTree = "<group>"; };
|
||||||
71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dropshadowable.swift; sourceTree = "<group>"; };
|
71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowable.swift; sourceTree = "<group>"; };
|
||||||
71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = "<group>"; };
|
71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = "<group>"; };
|
||||||
|
71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceConfigurationValue.swift; sourceTree = "<group>"; };
|
||||||
|
71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropShadowConfiguration.swift; sourceTree = "<group>"; };
|
||||||
EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = "<group>"; };
|
EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = "<group>"; };
|
||||||
EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = "<group>"; };
|
EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = "<group>"; };
|
||||||
EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = "<group>"; };
|
EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = "<group>"; };
|
||||||
@ -584,7 +588,7 @@
|
|||||||
EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */,
|
EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */,
|
||||||
EAB1D2CC28ABE76000DAE764 /* Withable.swift */,
|
EAB1D2CC28ABE76000DAE764 /* Withable.swift */,
|
||||||
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */,
|
5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */,
|
||||||
71BFA7092B7F70E6000DCE33 /* Dropshadowable.swift */,
|
71BFA7092B7F70E6000DCE33 /* DropShadowable.swift */,
|
||||||
);
|
);
|
||||||
path = Protocols;
|
path = Protocols;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -603,6 +607,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
EA3361BC288B2C760071C351 /* TypeAlias.swift */,
|
EA3361BC288B2C760071C351 /* TypeAlias.swift */,
|
||||||
|
71FC86DD2B9738B900700965 /* SurfaceConfigurationValue.swift */,
|
||||||
|
71FC86DF2B973AE500700965 /* DropShadowConfiguration.swift */,
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1030,7 +1036,7 @@
|
|||||||
EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */,
|
EAB2376A29E9E59100AABE9A /* TooltipLaunchable.swift in Sources */,
|
||||||
18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */,
|
18A65A042B96F050006602CC /* BreadcrumbItem.swift in Sources */,
|
||||||
EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */,
|
EAB2375D29E8789100AABE9A /* Tooltip.swift in Sources */,
|
||||||
71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */,
|
71BFA70A2B7F70E6000DCE33 /* DropShadowable.swift in Sources */,
|
||||||
EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */,
|
EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */,
|
||||||
EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */,
|
EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */,
|
||||||
EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */,
|
EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */,
|
||||||
@ -1055,6 +1061,7 @@
|
|||||||
EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */,
|
EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */,
|
||||||
EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */,
|
EAF7F0952899861000B287F5 /* CheckboxItem.swift in Sources */,
|
||||||
EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */,
|
EA985BE82968951C00F2FF2E /* TileletTitleModel.swift in Sources */,
|
||||||
|
71FC86DE2B9738B900700965 /* SurfaceConfigurationValue.swift in Sources */,
|
||||||
EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */,
|
EA297A5529FB07760031ED56 /* TooltipLabelAttribute.swift in Sources */,
|
||||||
EA985BEA29689B6D00F2FF2E /* TileletSubTitleModel.swift in Sources */,
|
EA985BEA29689B6D00F2FF2E /* TileletSubTitleModel.swift in Sources */,
|
||||||
EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */,
|
EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */,
|
||||||
@ -1125,6 +1132,7 @@
|
|||||||
EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */,
|
EAB2376829E9992800AABE9A /* TooltipAlertViewController.swift in Sources */,
|
||||||
EA33623E2892EE950071C351 /* UIDevice.swift in Sources */,
|
EA33623E2892EE950071C351 /* UIDevice.swift in Sources */,
|
||||||
EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */,
|
EA985C692971B90B00F2FF2E /* IconSize.swift in Sources */,
|
||||||
|
71FC86E02B973AE500700965 /* DropShadowConfiguration.swift in Sources */,
|
||||||
EA985C672970C21600F2FF2E /* VDSLayout.swift in Sources */,
|
EA985C672970C21600F2FF2E /* VDSLayout.swift in Sources */,
|
||||||
EA3362302891EB4A0071C351 /* Font.swift in Sources */,
|
EA3362302891EB4A0071C351 /* Font.swift in Sources */,
|
||||||
EAF7F0AD289B142900B287F5 /* StrikeThroughLabelAttribute.swift in Sources */,
|
EAF7F0AD289B142900B287F5 /* StrikeThroughLabelAttribute.swift in Sources */,
|
||||||
|
|||||||
@ -102,7 +102,6 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
/// Instead of use labelText and labelTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes.
|
/// Instead of use labelText and labelTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes.
|
||||||
open var labelAttributedText: NSAttributedString? {
|
open var labelAttributedText: NSAttributedString? {
|
||||||
didSet {
|
didSet {
|
||||||
label.useAttributedText = !(labelAttributedText?.string.isEmpty ?? true)
|
|
||||||
label.attributedText = labelAttributedText
|
label.attributedText = labelAttributedText
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
@ -117,7 +116,6 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
|
|||||||
/// Instead of use childText and childTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes.
|
/// Instead of use childText and childTextAttirbutes, this is a fully baked NSAttributedString with both text and attributes.
|
||||||
open var childAttributedText: NSAttributedString? {
|
open var childAttributedText: NSAttributedString? {
|
||||||
didSet {
|
didSet {
|
||||||
childLabel.useAttributedText = !(childAttributedText?.string.isEmpty ?? true)
|
|
||||||
childLabel.attributedText = childAttributedText
|
childLabel.attributedText = childAttributedText
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,10 +34,17 @@ public final class SelfSizingCollectionView: UICollectionView {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private var contentSizeObservation: NSKeyValueObservation?
|
|
||||||
private var collectionViewHeight: NSLayoutConstraint?
|
private var collectionViewHeight: NSLayoutConstraint?
|
||||||
private var anyCancellable: AnyCancellable?
|
private var anyCancellable: AnyCancellable?
|
||||||
|
private var contentSizeSubject = CurrentValueSubject<CGSize, Never>(.zero)
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
public var contentSizePublisher: AnyPublisher<CGSize, Never> {
|
||||||
|
contentSizeSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Overrides
|
// MARK: - Overrides
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -45,7 +52,6 @@ public final class SelfSizingCollectionView: UICollectionView {
|
|||||||
/// The natural size for the receiving view, considering only properties of the view itself.
|
/// The natural size for the receiving view, considering only properties of the view itself.
|
||||||
public override var intrinsicContentSize: CGSize {
|
public override var intrinsicContentSize: CGSize {
|
||||||
let contentSize = self.contentSize
|
let contentSize = self.contentSize
|
||||||
//print(#function, contentSize)
|
|
||||||
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
|
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,18 +73,17 @@ public final class SelfSizingCollectionView: UICollectionView {
|
|||||||
//ensure autoLayout uses intrinsic height
|
//ensure autoLayout uses intrinsic height
|
||||||
setContentHuggingPriority(.required, for: .vertical)
|
setContentHuggingPriority(.required, for: .vertical)
|
||||||
setContentCompressionResistancePriority(.required, for: .vertical)
|
setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
collectionViewHeight = heightAnchor.constraint(equalToConstant: 0).activate()
|
collectionViewHeight = height(constant: 0, priority: .defaultHigh)
|
||||||
|
|
||||||
// Observing the value of contentSize seems to be the only reliable way to get the contentSize after the collection view lays out its subviews.
|
anyCancellable = self.publisher(for: \.contentSize, options: [.new])
|
||||||
self.contentSizeObservation = self.observe(\.contentSize, options: [.old, .new]) { [weak self] _, change in
|
.sink { [weak self] compare in
|
||||||
// If we don't specify `options: [.old, .new]`, the change.oldValue and .newValue will always be `nil`.
|
guard let self else { return }
|
||||||
if change.newValue != change.oldValue {
|
if compare.height != self.collectionViewHeight?.constant {
|
||||||
self?.invalidateIntrinsicContentSize()
|
self.invalidateIntrinsicContentSize()
|
||||||
if let height = change.newValue?.height {
|
self.collectionViewHeight?.constant = compare.height
|
||||||
self?.collectionViewHeight?.constant = height
|
self.contentSizeSubject.send(compare)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -102,6 +102,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
open func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
|
initialSetupPerformed = true
|
||||||
backgroundColor = .clear
|
backgroundColor = .clear
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
accessibilityCustomActions = []
|
accessibilityCustomActions = []
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
import VDSFormControlsTokens
|
import VDSFormControlsTokens
|
||||||
|
import Combine
|
||||||
|
|
||||||
/// A button group contains combinations of related CTAs including ``Button``, ``TextLink``, and ``TextLinkCaret``. This group component controls a combination's orientation, spacing, size and allowable size pairings.
|
/// A button group contains combinations of related CTAs including ``Button``, ``TextLink``, and ``TextLinkCaret``. This group component controls a combination's orientation, spacing, size and allowable size pairings.
|
||||||
@objc(VDSButtonGroup)
|
@objc(VDSButtonGroup)
|
||||||
@ -98,6 +99,8 @@ open class ButtonGroup: View {
|
|||||||
buttons.forEach { $0.surface = surface }
|
buttons.forEach { $0.surface = surface }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open var contentSizePublisher: AnyPublisher<CGSize, Never> { collectionView.contentSizePublisher }
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
@ -108,6 +111,7 @@ open class ButtonGroup: View {
|
|||||||
$0.delegate = self
|
$0.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// CollectionView that renders the array of buttonBase obects.
|
||||||
fileprivate lazy var collectionView: SelfSizingCollectionView = {
|
fileprivate lazy var collectionView: SelfSizingCollectionView = {
|
||||||
|
|
||||||
return SelfSizingCollectionView(frame: .zero, collectionViewLayout: positionLayout).with {
|
return SelfSizingCollectionView(frame: .zero, collectionViewLayout: positionLayout).with {
|
||||||
|
|||||||
@ -232,19 +232,26 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct LowContrastColorFillFloatingConfiguration: Configuration, Dropshadowable {
|
private struct LowContrastColorFillFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||||
var kind: Kind = .lowContrast
|
var kind: Kind = .lowContrast
|
||||||
var surfaceType: SurfaceType = .colorFill
|
var surfaceType: SurfaceType = .colorFill
|
||||||
var floating: Bool = true
|
var floating: Bool = true
|
||||||
var backgroundColorConfiguration: AnyColorable = {
|
var backgroundColorConfiguration: AnyColorable = {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable()
|
SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable()
|
||||||
}()
|
}()
|
||||||
var shadowColorConfiguration: AnyColorable = {
|
private let dropshadow1Configuration = DropShadowConfiguration().with {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
}()
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.12), CGFloat(0.22))
|
||||||
var shadowOpacity: CGFloat = 0.16
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1))
|
||||||
var shadowOffset: CGSize = .init(width: 0, height: 2)
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(10), CGFloat(12))
|
||||||
var shadowRadius: CGFloat = 4
|
}
|
||||||
|
private let dropshadow2Configuration = DropShadowConfiguration().with {
|
||||||
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.05), CGFloat(0.15))
|
||||||
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2))
|
||||||
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(4), CGFloat(6))
|
||||||
|
}
|
||||||
|
var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] }
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct LowContrastMediaConfiguration: Configuration, Borderable {
|
private struct LowContrastMediaConfiguration: Configuration, Borderable {
|
||||||
@ -260,19 +267,26 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct LowContrastMediaFloatingConfiguration: Configuration, Dropshadowable {
|
private struct LowContrastMediaFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||||
var kind: Kind = .lowContrast
|
var kind: Kind = .lowContrast
|
||||||
var surfaceType: SurfaceType = .media
|
var surfaceType: SurfaceType = .media
|
||||||
var floating: Bool = true
|
var floating: Bool = true
|
||||||
var backgroundColorConfiguration: AnyColorable = {
|
var backgroundColorConfiguration: AnyColorable = {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable()
|
SurfaceColorConfiguration(VDSColor.paletteWhite, VDSColor.paletteGray20).eraseToAnyColorable()
|
||||||
}()
|
}()
|
||||||
var shadowColorConfiguration: AnyColorable = {
|
private let dropshadow1Configuration = DropShadowConfiguration().with {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
}()
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.12), CGFloat(0.22))
|
||||||
var shadowOpacity: CGFloat = 0.16
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1))
|
||||||
var shadowOffset: CGSize = .init(width: 0, height: 2)
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(10), CGFloat(12))
|
||||||
var shadowRadius: CGFloat = 4
|
}
|
||||||
|
private let dropshadow2Configuration = DropShadowConfiguration().with {
|
||||||
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.05), CGFloat(0.15))
|
||||||
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2))
|
||||||
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(4), CGFloat(6))
|
||||||
|
}
|
||||||
|
var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] }
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct HighContrastConfiguration: Configuration {
|
private struct HighContrastConfiguration: Configuration {
|
||||||
@ -291,7 +305,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct HighContrastFloatingConfiguration: Configuration, Dropshadowable {
|
private struct HighContrastFloatingConfiguration: Configuration, DropShadowableConfiguration {
|
||||||
var kind: Kind = .highContrast
|
var kind: Kind = .highContrast
|
||||||
var surfaceType: SurfaceType = .colorFill
|
var surfaceType: SurfaceType = .colorFill
|
||||||
var floating: Bool = true
|
var floating: Bool = true
|
||||||
@ -305,12 +319,19 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled])
|
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: [.selected, .disabled])
|
||||||
}.eraseToAnyColorable()
|
}.eraseToAnyColorable()
|
||||||
}()
|
}()
|
||||||
var shadowColorConfiguration: AnyColorable = {
|
private let dropshadow1Configuration = DropShadowConfiguration().with {
|
||||||
SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
}()
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.22), CGFloat(0.12))
|
||||||
var shadowOpacity: CGFloat = 0.16
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 1), .init(width: 0, height: 1))
|
||||||
var shadowOffset: CGSize = .init(width: 0, height: 2)
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(12), CGFloat(10))
|
||||||
var shadowRadius: CGFloat = 6
|
}
|
||||||
|
private let dropshadow2Configuration = DropShadowConfiguration().with {
|
||||||
|
$0.shadowColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteBlack).eraseToAnyColorable()
|
||||||
|
$0.shadowOpacityConfiguration = SurfaceConfigurationValue(CGFloat(0.15), CGFloat(0.05))
|
||||||
|
$0.shadowOffsetConfiguration = SurfaceConfigurationValue(.init(width: 0, height: 2), .init(width: 0, height: 2))
|
||||||
|
$0.shadowRadiusConfiguration = SurfaceConfigurationValue(CGFloat(6), CGFloat(4))
|
||||||
|
}
|
||||||
|
var configurations: [DropShadowable] { [dropshadow1Configuration, dropshadow2Configuration] }
|
||||||
}
|
}
|
||||||
|
|
||||||
private var badgeIndicatorDefaultSize: CGSize = .zero
|
private var badgeIndicatorDefaultSize: CGSize = .zero
|
||||||
@ -322,7 +343,7 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
open override func setup() {
|
open override func setup() {
|
||||||
super.setup()
|
super.setup()
|
||||||
isAccessibilityElement = true
|
isAccessibilityElement = true
|
||||||
accessibilityTraits = .image
|
accessibilityTraits = .button
|
||||||
accessibilityElements = [badgeIndicator]
|
accessibilityElements = [badgeIndicator]
|
||||||
|
|
||||||
//create a layoutGuide for the icon to key off of
|
//create a layoutGuide for the icon to key off of
|
||||||
@ -452,12 +473,6 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
layer.borderColor = nil
|
layer.borderColor = nil
|
||||||
layer.borderWidth = 0
|
layer.borderWidth = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if let dropshadowable = currentConfig as? Dropshadowable {
|
|
||||||
addDropShadow(dropshadowable)
|
|
||||||
} else {
|
|
||||||
removeDropShadows()
|
|
||||||
}
|
|
||||||
|
|
||||||
badgeIndicatorCenterXConstraint?.constant = badgeIndicatorOffset.x + badgeIndicatorDefaultSize.width/2
|
badgeIndicatorCenterXConstraint?.constant = badgeIndicatorOffset.x + badgeIndicatorDefaultSize.width/2
|
||||||
badgeIndicatorCenterYConstraint?.constant = badgeIndicatorOffset.y + badgeIndicatorDefaultSize.height/2
|
badgeIndicatorCenterYConstraint?.constant = badgeIndicatorOffset.y + badgeIndicatorDefaultSize.height/2
|
||||||
@ -467,6 +482,12 @@ open class ButtonIcon: Control, Changeable, FormFieldable {
|
|||||||
if showBadgeIndicator {
|
if showBadgeIndicator {
|
||||||
updateExpandDirectionalConstraints()
|
updateExpandDirectionalConstraints()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let configurations = (currentConfig as? DropShadowableConfiguration)?.configurations {
|
||||||
|
addDropShadows(configurations)
|
||||||
|
} else {
|
||||||
|
removeDropShadows()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -42,6 +42,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Properties
|
// MARK: - Private Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
private enum TextSetMode {
|
||||||
|
case text
|
||||||
|
case attributedText
|
||||||
|
}
|
||||||
|
|
||||||
|
private var textSetMode: TextSetMode = .text
|
||||||
|
|
||||||
private var initialSetupPerformed = false
|
private var initialSetupPerformed = false
|
||||||
|
|
||||||
private var edgeInsets: UIEdgeInsets { textStyle.edgeInsets }
|
private var edgeInsets: UIEdgeInsets { textStyle.edgeInsets }
|
||||||
@ -102,10 +109,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||||
open var shouldUpdateView: Bool = true
|
open var shouldUpdateView: Bool = true
|
||||||
|
|
||||||
/// Determines if the label should use its own attributedText property instead of rendering the attributedText propert
|
|
||||||
/// based of other local properties, such as textStyle, textColor, surface, etc... The default value is false.
|
|
||||||
open var useAttributedText: Bool = false
|
|
||||||
|
|
||||||
/// Will determine if a scaled font should be used for the font.
|
/// Will determine if a scaled font should be used for the font.
|
||||||
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }}
|
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }}
|
||||||
@ -128,19 +131,40 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
|
|
||||||
/// Line break mode for the label, default is set to word wrapping.
|
/// Line break mode for the label, default is set to word wrapping.
|
||||||
open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }}
|
open override var lineBreakMode: NSLineBreakMode { didSet { setNeedsUpdate() }}
|
||||||
|
|
||||||
private var _text: String?
|
|
||||||
|
|
||||||
/// Text that will be used in the label.
|
/// Text that will be used in the label.
|
||||||
override open var text: String? {
|
override open var text: String! {
|
||||||
get { _text }
|
get { super.text }
|
||||||
set {
|
set {
|
||||||
if _text != newValue || newValue != attributedText?.string {
|
textSetMode = .text
|
||||||
_text = newValue
|
styleText(newValue)
|
||||||
useAttributedText = false
|
}
|
||||||
attributes?.removeAll()
|
}
|
||||||
setNeedsUpdate()
|
|
||||||
|
///AttributedText that will be used in the label.
|
||||||
|
override open var attributedText: NSAttributedString? {
|
||||||
|
get { super.attributedText }
|
||||||
|
set {
|
||||||
|
textSetMode = .attributedText
|
||||||
|
styleAttributedText(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open var font: UIFont! {
|
||||||
|
didSet {
|
||||||
|
if let font, initialSetupPerformed {
|
||||||
|
textStyle = TextStyle.convert(font: font)
|
||||||
}
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open var textColor: UIColor! {
|
||||||
|
didSet {
|
||||||
|
if let textColor, initialSetupPerformed {
|
||||||
|
textColorConfiguration = SurfaceColorConfiguration(textColor, textColor).eraseToAnyColorable()
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,13 +186,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
open func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
|
initialSetupPerformed = true
|
||||||
//register for ContentSizeChanges
|
//register for ContentSizeChanges
|
||||||
NotificationCenter
|
NotificationCenter
|
||||||
.Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification)
|
.Publisher(center: .default, name: UIContentSizeCategory.didChangeNotification)
|
||||||
.sink { [weak self] notification in
|
.sink { [weak self] notification in
|
||||||
self?.setNeedsUpdate()
|
self?.setNeedsUpdate()
|
||||||
}.store(in: &subscribers)
|
}.store(in: &subscribers)
|
||||||
|
|
||||||
backgroundColor = .clear
|
backgroundColor = .clear
|
||||||
numberOfLines = 0
|
numberOfLines = 0
|
||||||
lineBreakMode = .byWordWrapping
|
lineBreakMode = .byWordWrapping
|
||||||
@ -200,30 +224,13 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func updateView() {
|
open func updateView() {
|
||||||
if !useAttributedText {
|
restyleText()
|
||||||
if let text {
|
|
||||||
accessibilityCustomActions = []
|
//force a drawText
|
||||||
|
setNeedsDisplay()
|
||||||
//create the primary string
|
|
||||||
let mutableText = NSMutableAttributedString.mutableText(for: text,
|
setNeedsLayout()
|
||||||
textStyle: textStyle,
|
layoutIfNeeded()
|
||||||
useScaledFont: useScaledFont,
|
|
||||||
textColor: textColorConfiguration.getColor(self),
|
|
||||||
alignment: textAlignment,
|
|
||||||
lineBreakMode: lineBreakMode)
|
|
||||||
|
|
||||||
applyAttributes(mutableText)
|
|
||||||
|
|
||||||
//set the attributed text
|
|
||||||
attributedText = mutableText
|
|
||||||
|
|
||||||
//force a drawText
|
|
||||||
setNeedsDisplay()
|
|
||||||
|
|
||||||
setNeedsLayout()
|
|
||||||
layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open func updateAccessibility() {
|
open func updateAccessibility() {
|
||||||
@ -269,6 +276,56 @@ open class Label: UILabel, ViewProtocol, UserInfoable {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
private func restyleText() {
|
||||||
|
if textSetMode == .text {
|
||||||
|
styleText(text)
|
||||||
|
} else {
|
||||||
|
styleAttributedText(attributedText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func styleText(_ newValue: String!) {
|
||||||
|
defer { invalidateIntrinsicContentSize() }
|
||||||
|
guard let newValue else {
|
||||||
|
// We don't need to use attributed text
|
||||||
|
super.attributedText = nil
|
||||||
|
super.text = newValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accessibilityCustomActions = []
|
||||||
|
|
||||||
|
//create the primary string
|
||||||
|
let mutableText = NSMutableAttributedString.mutableText(for: newValue,
|
||||||
|
textStyle: textStyle,
|
||||||
|
useScaledFont: useScaledFont,
|
||||||
|
textColor: textColorConfiguration.getColor(self),
|
||||||
|
alignment: textAlignment,
|
||||||
|
lineBreakMode: lineBreakMode)
|
||||||
|
|
||||||
|
applyAttributes(mutableText)
|
||||||
|
|
||||||
|
// Set attributed text to match typography
|
||||||
|
super.attributedText = mutableText
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func styleAttributedText(_ newValue: NSAttributedString?) {
|
||||||
|
defer { invalidateIntrinsicContentSize() }
|
||||||
|
guard let newValue = newValue else {
|
||||||
|
// We don't need any additional styling
|
||||||
|
super.attributedText = newValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let mutableText = NSMutableAttributedString(attributedString: newValue)
|
||||||
|
|
||||||
|
applyAttributes(mutableText)
|
||||||
|
|
||||||
|
// Modify attributed text to match typography
|
||||||
|
super.attributedText = mutableText
|
||||||
|
}
|
||||||
|
|
||||||
private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) {
|
private func applyAttributes(_ mutableAttributedString: NSMutableAttributedString) {
|
||||||
actions = []
|
actions = []
|
||||||
|
|
||||||
|
|||||||
@ -100,7 +100,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
|
|||||||
/// If provided, the RadioBox textAttributedText will be rendered.
|
/// If provided, the RadioBox textAttributedText will be rendered.
|
||||||
open var textAttributedText: NSAttributedString? {
|
open var textAttributedText: NSAttributedString? {
|
||||||
didSet {
|
didSet {
|
||||||
textLabel.useAttributedText = !(textAttributedText?.string.isEmpty ?? true)
|
|
||||||
textLabel.attributedText = textAttributedText
|
textLabel.attributedText = textAttributedText
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
@ -115,7 +114,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
|
|||||||
/// If provided, the RadioBox subTextAttributedText will be rendered.
|
/// If provided, the RadioBox subTextAttributedText will be rendered.
|
||||||
open var subTextAttributedText: NSAttributedString? {
|
open var subTextAttributedText: NSAttributedString? {
|
||||||
didSet {
|
didSet {
|
||||||
subTextLabel.useAttributedText = !(subTextAttributedText?.string.isEmpty ?? true)
|
|
||||||
subTextLabel.attributedText = subTextAttributedText
|
subTextLabel.attributedText = subTextAttributedText
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
@ -130,7 +128,6 @@ open class RadioBoxItem: Control, Changeable, FormFieldable {
|
|||||||
/// If provided, the RadioBox subTextRightAttributedText will be rendered.
|
/// If provided, the RadioBox subTextRightAttributedText will be rendered.
|
||||||
open var subTextRightAttributedText: NSAttributedString? {
|
open var subTextRightAttributedText: NSAttributedString? {
|
||||||
didSet {
|
didSet {
|
||||||
subTextRightLabel.useAttributedText = !(subTextRightAttributedText?.string.isEmpty ?? true)
|
|
||||||
subTextRightLabel.attributedText = subTextRightAttributedText
|
subTextRightLabel.attributedText = subTextRightAttributedText
|
||||||
setNeedsUpdate()
|
setNeedsUpdate()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,13 +154,13 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
|
|||||||
open var showError: Bool = false { didSet { setNeedsUpdate() } }
|
open var showError: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Whether or not to show the internal error
|
/// Whether or not to show the internal error
|
||||||
internal var showInternalError: Bool = false { didSet { setNeedsUpdate() } }
|
open internal(set) var hasInternalError: Bool = false { didSet { setNeedsUpdate() } }
|
||||||
|
|
||||||
/// Override UIControl state to add the .error state if showError is true.
|
/// Override UIControl state to add the .error state if showError is true.
|
||||||
open override var state: UIControl.State {
|
open override var state: UIControl.State {
|
||||||
get {
|
get {
|
||||||
var state = super.state
|
var state = super.state
|
||||||
if showError || showInternalError {
|
if showError || hasInternalError {
|
||||||
state.insert(.error)
|
state.insert(.error)
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
@ -380,7 +380,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func updateErrorLabel(){
|
open func updateErrorLabel(){
|
||||||
if showError, showInternalError, let errorText, let internalErrorText {
|
if showError, hasInternalError, let errorText, let internalErrorText {
|
||||||
errorLabel.text = [internalErrorText, errorText].joined(separator: "\n")
|
errorLabel.text = [internalErrorText, errorText].joined(separator: "\n")
|
||||||
errorLabel.surface = surface
|
errorLabel.surface = surface
|
||||||
errorLabel.isEnabled = isEnabled
|
errorLabel.isEnabled = isEnabled
|
||||||
@ -398,7 +398,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldable {
|
|||||||
icon.color = VDSColor.paletteBlack
|
icon.color = VDSColor.paletteBlack
|
||||||
icon.surface = surface
|
icon.surface = surface
|
||||||
icon.isHidden = !isEnabled
|
icon.isHidden = !isEnabled
|
||||||
} else if showInternalError, let internalErrorText {
|
} else if hasInternalError, let internalErrorText {
|
||||||
errorLabel.text = internalErrorText
|
errorLabel.text = internalErrorText
|
||||||
errorLabel.surface = surface
|
errorLabel.surface = surface
|
||||||
errorLabel.isEnabled = isEnabled
|
errorLabel.isEnabled = isEnabled
|
||||||
|
|||||||
@ -247,16 +247,16 @@ open class TextArea: EntryFieldBase {
|
|||||||
let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)"
|
let countStr = (count > maxLength ?? 0) ? ("-" + "\(count-(maxLength ?? 0))") : "\(count)"
|
||||||
if let maxLength, maxLength > 0 {
|
if let maxLength, maxLength > 0 {
|
||||||
if count > maxLength {
|
if count > maxLength {
|
||||||
showInternalError = true
|
hasInternalError = true
|
||||||
internalErrorText = "You have exceeded the character limit."
|
internalErrorText = "You have exceeded the character limit."
|
||||||
return countStr
|
return countStr
|
||||||
} else {
|
} else {
|
||||||
showInternalError = false
|
hasInternalError = false
|
||||||
internalErrorText = nil
|
internalErrorText = nil
|
||||||
return ("\(countStr)" + "/" + "\(maxLength)")
|
return ("\(countStr)" + "/" + "\(maxLength)")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showInternalError = false
|
hasInternalError = false
|
||||||
internalErrorText = nil
|
internalErrorText = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,6 +94,7 @@ open class TextView: UITextView, ViewProtocol {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
open func initialSetup() {
|
open func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
|
initialSetupPerformed = true
|
||||||
backgroundColor = .clear
|
backgroundColor = .clear
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
accessibilityCustomActions = []
|
accessibilityCustomActions = []
|
||||||
|
|||||||
@ -184,9 +184,15 @@ open class TileContainer: Control {
|
|||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
private let cornerRadius = VDSFormControls.borderradius * 2
|
private let cornerRadius = VDSFormControls.borderradius * 2
|
||||||
|
|
||||||
private var backgroundColorConfiguration = BackgroundColorConfiguration()
|
private var backgroundColorConfiguration = BackgroundColorConfiguration()
|
||||||
private var dropshadowConfiguration = DropshadowConfiguration()
|
private let dropShadowConfiguration = DropShadowConfiguration().with {
|
||||||
|
$0.shadowColorConfiguration = SurfaceColorConfiguration().with {
|
||||||
|
$0.lightColor = VDSColor.elementsPrimaryOnlight
|
||||||
|
}.eraseToAnyColorable()
|
||||||
|
$0.shadowOffsetConfiguration = .init(.init(width: 0, height: 6), .zero)
|
||||||
|
$0.shadowRadiusConfiguration = .init(3.0, 0.0)
|
||||||
|
$0.shadowOpacityConfiguration = .init(0.01, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
private var borderColorConfiguration = SurfaceColorConfiguration().with {
|
private var borderColorConfiguration = SurfaceColorConfiguration().with {
|
||||||
$0.lightColor = VDSColor.elementsLowcontrastOnlight
|
$0.lightColor = VDSColor.elementsLowcontrastOnlight
|
||||||
@ -311,7 +317,7 @@ open class TileContainer: Control {
|
|||||||
heightConstraint?.isActive = false
|
heightConstraint?.isActive = false
|
||||||
}
|
}
|
||||||
if showDropShadows, surface == .light {
|
if showDropShadows, surface == .light {
|
||||||
addDropShadow(dropshadowConfiguration)
|
addDropShadow(dropShadowConfiguration)
|
||||||
} else {
|
} else {
|
||||||
removeDropShadows()
|
removeDropShadows()
|
||||||
}
|
}
|
||||||
@ -397,15 +403,6 @@ open class TileContainer: Control {
|
|||||||
|
|
||||||
extension TileContainer {
|
extension TileContainer {
|
||||||
|
|
||||||
struct DropshadowConfiguration: Dropshadowable {
|
|
||||||
var shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().with {
|
|
||||||
$0.lightColor = VDSColor.elementsPrimaryOnlight
|
|
||||||
}.eraseToAnyColorable()
|
|
||||||
var shadowOpacity: CGFloat = 0.01
|
|
||||||
var shadowOffset: CGSize = .init(width: 0, height: 6)
|
|
||||||
var shadowRadius: CGFloat = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
final class BackgroundColorConfiguration: ObjectColorable {
|
final class BackgroundColorConfiguration: ObjectColorable {
|
||||||
|
|
||||||
typealias ObjectType = TileContainer
|
typealias ObjectType = TileContainer
|
||||||
|
|||||||
@ -68,6 +68,7 @@ open class TitleLockup: View {
|
|||||||
/// Label used to render the eyebrow model.
|
/// Label used to render the eyebrow model.
|
||||||
open var eyebrowLabel = Label().with {
|
open var eyebrowLabel = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.setContentHuggingPriority(.required, for: .vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Model used in rendering the eyebrow label.
|
/// Model used in rendering the eyebrow label.
|
||||||
@ -77,6 +78,7 @@ open class TitleLockup: View {
|
|||||||
/// Label used to render the title model.
|
/// Label used to render the title model.
|
||||||
open var titleLabel = Label().with {
|
open var titleLabel = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.setContentHuggingPriority(.required, for: .vertical)
|
||||||
$0.accessibilityTraits.insert([.header])
|
$0.accessibilityTraits.insert([.header])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +89,7 @@ open class TitleLockup: View {
|
|||||||
/// Label used to render the subtitle model.
|
/// Label used to render the subtitle model.
|
||||||
open var subTitleLabel = Label().with {
|
open var subTitleLabel = Label().with {
|
||||||
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
$0.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
$0.setContentHuggingPriority(.required, for: .vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Model used in rendering the subtitle label.
|
/// Model used in rendering the subtitle label.
|
||||||
@ -380,7 +383,7 @@ open class TitleLockup: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//pin the last view to the bottom of this view
|
//pin the last view to the bottom of this view
|
||||||
previousView?.pinBottom(0, .defaultHigh)
|
previousView?.pinBottom(0)
|
||||||
|
|
||||||
//debugging for borders
|
//debugging for borders
|
||||||
eyebrowLabel.debugBorder(show: hasDebugBorder, color: .green)
|
eyebrowLabel.debugBorder(show: hasDebugBorder, color: .green)
|
||||||
|
|||||||
@ -6,15 +6,17 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
/// Enum that is matched up for the Verizon fonts.
|
/// Enum that is matched up for the Verizon fonts.
|
||||||
public enum Font: String, FontProtocol {
|
public enum Font: FontProtocol {
|
||||||
case edsBold
|
case edsBold
|
||||||
case edsRegular
|
case edsRegular
|
||||||
case dsLight
|
case dsLight
|
||||||
case etxBold
|
case etxBold
|
||||||
case etxRegular
|
case etxRegular
|
||||||
|
case custom(UIFont)
|
||||||
|
|
||||||
public var fontName: String {
|
public var fontName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .edsBold:
|
case .edsBold:
|
||||||
@ -27,11 +29,32 @@ public enum Font: String, FontProtocol {
|
|||||||
return "VerizonNHGeTX-Bold"
|
return "VerizonNHGeTX-Bold"
|
||||||
case .etxRegular:
|
case .etxRegular:
|
||||||
return "VerizonNHGeTX-Regular"
|
return "VerizonNHGeTX-Regular"
|
||||||
|
case .custom(let font):
|
||||||
|
return font.fontName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static var allCases: [Font] {
|
||||||
|
[.edsBold, .edsRegular, .dsLight, .etxBold, .etxRegular]
|
||||||
|
}
|
||||||
|
|
||||||
/// File Extension for each of the Font enums.
|
/// File Extension for each of the Font enums.
|
||||||
public var fontFileExtension: String {
|
public var fontFileExtension: String {
|
||||||
return "otf"
|
return "otf"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a UIFont for the fontName and size given.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - size: Size of the font
|
||||||
|
/// - Returns: UIFont for the fontName and Size.
|
||||||
|
public func font(ofSize size: CGFloat) -> UIFont{
|
||||||
|
DispatchQueue.once(block: { self.register() })
|
||||||
|
switch self {
|
||||||
|
case .custom(let font):
|
||||||
|
return font
|
||||||
|
default:
|
||||||
|
guard let found = UIFont(name: self.fontName, size: size) else { return .systemFont(ofSize: size) }
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,9 +9,10 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
/// Used in Classes that require Fonts
|
/// Used in Classes that require Fonts
|
||||||
public protocol FontProtocol: CaseIterable, RawRepresentable, Hashable {
|
public protocol FontProtocol {
|
||||||
var fontFileExtension: String { get }
|
var fontFileExtension: String { get }
|
||||||
var fontName: String { get }
|
var fontName: String { get }
|
||||||
|
static var allCases: [Self] { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FontProtocol {
|
extension FontProtocol {
|
||||||
|
|||||||
89
VDS/Protocols/DropShadowable.swift
Normal file
89
VDS/Protocols/DropShadowable.swift
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// DropShadowable.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 16/02/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
/**
|
||||||
|
DropShadowable protocol helps with the configuration values for adding drop shadows for light & dark surfaces.
|
||||||
|
*/
|
||||||
|
protocol DropShadowable {
|
||||||
|
///Shadow Color configuration for light and dark surfaces
|
||||||
|
var shadowColorConfiguration: AnyColorable { get set }
|
||||||
|
///Shadow Opacity configuration for light and dark surfaces
|
||||||
|
var shadowOpacityConfiguration: SurfaceConfigurationValue<CGFloat> { get set }
|
||||||
|
///Shadow Offset configuration for light and dark surfaces
|
||||||
|
var shadowOffsetConfiguration: SurfaceConfigurationValue<CGSize> { get set }
|
||||||
|
///Shadow Radius configuration for light and dark surfaces
|
||||||
|
var shadowRadiusConfiguration: SurfaceConfigurationValue<CGFloat> { get set }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
DropShadowableConfiguration protocol helps with multiple drop shadows configurations can be added to a view.
|
||||||
|
*/
|
||||||
|
protocol DropShadowableConfiguration {
|
||||||
|
|
||||||
|
///Configurations are the DropShadowable list, these are applied on the view
|
||||||
|
var configurations: [DropShadowable] { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Extension on ViewProtocol for adding drop shadows & gradient layer on view.
|
||||||
|
*/
|
||||||
|
extension ViewProtocol where Self: UIView {
|
||||||
|
|
||||||
|
func addDropShadow(_ config: DropShadowable) {
|
||||||
|
addDropShadows([config])
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDropShadows(_ configs: [DropShadowable]) {
|
||||||
|
removeDropShadows()
|
||||||
|
layer.backgroundColor = backgroundColor?.cgColor
|
||||||
|
layer.masksToBounds = false
|
||||||
|
for config in configs {
|
||||||
|
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius)
|
||||||
|
let shadowLayer = CALayer()
|
||||||
|
shadowLayer.shadowPath = shadowPath.cgPath
|
||||||
|
shadowLayer.frame = bounds
|
||||||
|
shadowLayer.position = .init(x: bounds.midX, y: bounds.midY)
|
||||||
|
shadowLayer.backgroundColor = backgroundColor?.cgColor
|
||||||
|
shadowLayer.cornerRadius = layer.cornerRadius
|
||||||
|
shadowLayer.shadowColor = config.shadowColorConfiguration.getColor(self).cgColor
|
||||||
|
shadowLayer.shadowOpacity = Float(config.shadowOpacityConfiguration.value(for: self))
|
||||||
|
shadowLayer.shadowOffset = config.shadowOffsetConfiguration.value(for: self)
|
||||||
|
shadowLayer.shadowRadius = config.shadowRadiusConfiguration.value(for: self)
|
||||||
|
shadowLayer.name = "dropShadowLayer"
|
||||||
|
shadowLayer.shouldRasterize = true
|
||||||
|
shadowLayer.rasterizationScale = UIScreen.main.scale
|
||||||
|
layer.insertSublayer(shadowLayer, at: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeDropShadows() {
|
||||||
|
layer.sublayers?.removeAll { $0.name == "dropShadowLayer" }
|
||||||
|
}
|
||||||
|
|
||||||
|
func addGradientLayer(with firstColor: UIColor, secondColor: UIColor) {
|
||||||
|
removeGradientLayer()
|
||||||
|
let gradientLayer = CAGradientLayer()
|
||||||
|
gradientLayer.frame = bounds
|
||||||
|
gradientLayer.startPoint = CGPoint(x: 0, y: 1)
|
||||||
|
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
|
||||||
|
gradientLayer.position = center
|
||||||
|
gradientLayer.shouldRasterize = true
|
||||||
|
gradientLayer.backgroundColor = UIColor.clear.cgColor
|
||||||
|
gradientLayer.rasterizationScale = UIScreen.main.scale
|
||||||
|
gradientLayer.cornerRadius = layer.cornerRadius
|
||||||
|
gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor]
|
||||||
|
gradientLayer.name = "gradientLayer"
|
||||||
|
layer.insertSublayer(gradientLayer, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeGradientLayer() {
|
||||||
|
layer.sublayers?.removeAll { $0.name == "gradientLayer" }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,64 +0,0 @@
|
|||||||
//
|
|
||||||
// Dropshadowable.swift
|
|
||||||
// VDS
|
|
||||||
//
|
|
||||||
// Created by Bandaru, Krishna Kishore on 16/02/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
protocol Dropshadowable {
|
|
||||||
|
|
||||||
var shadowColorConfiguration: AnyColorable { get set }
|
|
||||||
var shadowOpacity: CGFloat { get set }
|
|
||||||
var shadowOffset: CGSize { get set }
|
|
||||||
var shadowRadius: CGFloat { get set }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ViewProtocol where Self: UIView {
|
|
||||||
|
|
||||||
func addDropShadow(_ config: Dropshadowable) {
|
|
||||||
removeDropShadows()
|
|
||||||
layer.backgroundColor = backgroundColor?.cgColor
|
|
||||||
layer.masksToBounds = false
|
|
||||||
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.cornerRadius)
|
|
||||||
let shadowLayer = CALayer()
|
|
||||||
shadowLayer.shadowPath = shadowPath.cgPath
|
|
||||||
shadowLayer.frame = bounds
|
|
||||||
shadowLayer.position = center
|
|
||||||
shadowLayer.backgroundColor = UIColor.clear.cgColor
|
|
||||||
shadowLayer.cornerRadius = layer.cornerRadius
|
|
||||||
shadowLayer.shadowColor = config.shadowColorConfiguration.getColor(self).cgColor
|
|
||||||
shadowLayer.shadowOpacity = Float(config.shadowOpacity)
|
|
||||||
shadowLayer.shadowOffset = .init(width: config.shadowOffset.width, height: config.shadowOffset.height)
|
|
||||||
shadowLayer.shadowRadius = config.shadowRadius
|
|
||||||
shadowLayer.name = "dropShadowLayer"
|
|
||||||
shadowLayer.shouldRasterize = true
|
|
||||||
shadowLayer.rasterizationScale = UIScreen.main.scale
|
|
||||||
layer.insertSublayer(shadowLayer, at: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDropShadows() {
|
|
||||||
layer.sublayers?.removeAll { $0.name == "dropShadowLayer" }
|
|
||||||
}
|
|
||||||
|
|
||||||
func addGradientLayer(with firstColor: UIColor, secondColor: UIColor) {
|
|
||||||
removeGradientLayer()
|
|
||||||
let gradientLayer = CAGradientLayer()
|
|
||||||
gradientLayer.frame = bounds
|
|
||||||
gradientLayer.startPoint = CGPoint(x: 0, y: 1)
|
|
||||||
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
|
|
||||||
gradientLayer.position = center
|
|
||||||
gradientLayer.shouldRasterize = true
|
|
||||||
gradientLayer.rasterizationScale = UIScreen.main.scale
|
|
||||||
gradientLayer.cornerRadius = layer.cornerRadius
|
|
||||||
gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor]
|
|
||||||
gradientLayer.name = "gradientLayer"
|
|
||||||
layer.insertSublayer(gradientLayer, at: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeGradientLayer() {
|
|
||||||
layer.sublayers?.removeAll { $0.name == "gradientLayer" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -209,6 +209,13 @@ extension TextStyle {
|
|||||||
boldMicro
|
boldMicro
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func convert(font: UIFont) -> TextStyle {
|
||||||
|
guard let found = allCases.first(where: { font.fontName == $0.fontFace.fontName && font.pointSize == $0.pointSize} ) else {
|
||||||
|
return TextStyle(rawValue: "Custom\(font.fontName)", fontFace: .custom(font), pointSize: font.pointSize)
|
||||||
|
}
|
||||||
|
return found
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TextStyle {
|
extension TextStyle {
|
||||||
|
|||||||
33
VDS/Utilities/DropShadowConfiguration.swift
Normal file
33
VDS/Utilities/DropShadowConfiguration.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// DropShadowConfiguration.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 05/03/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/**
|
||||||
|
DropShadowConfiguration confirms to DropShadowable where it has configurable properties required for drop shadow
|
||||||
|
*/
|
||||||
|
final class DropShadowConfiguration: DropShadowable, ObjectWithable {
|
||||||
|
|
||||||
|
typealias CGFloatConfigurationValue = SurfaceConfigurationValue<CGFloat>
|
||||||
|
typealias CGSizeConfigurationValue = SurfaceConfigurationValue<CGSize>
|
||||||
|
|
||||||
|
///Shadow Color configuration for light and dark surfaces
|
||||||
|
var shadowColorConfiguration: AnyColorable
|
||||||
|
///Shadow Opacity configuration for light and dark surfaces
|
||||||
|
var shadowOpacityConfiguration: CGFloatConfigurationValue
|
||||||
|
///Shadow Offset configuration for light and dark surfaces
|
||||||
|
var shadowOffsetConfiguration: CGSizeConfigurationValue
|
||||||
|
///Shadow Radius configuration for light and dark surfaces
|
||||||
|
var shadowRadiusConfiguration: CGFloatConfigurationValue
|
||||||
|
|
||||||
|
init(shadowColorConfiguration: AnyColorable = SurfaceColorConfiguration().eraseToAnyColorable(), shadowOpacity: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0), shadowOffset: CGSizeConfigurationValue = CGSizeConfigurationValue(.zero, .zero), shadowRadius: CGFloatConfigurationValue = CGFloatConfigurationValue(1.0, 1.0)) {
|
||||||
|
self.shadowColorConfiguration = shadowColorConfiguration
|
||||||
|
self.shadowOpacityConfiguration = shadowOpacity
|
||||||
|
self.shadowOffsetConfiguration = shadowOffset
|
||||||
|
self.shadowRadiusConfiguration = shadowRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
31
VDS/Utilities/SurfaceConfigurationValue.swift
Normal file
31
VDS/Utilities/SurfaceConfigurationValue.swift
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// SurfaceConfigurationValue.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 05/03/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/**
|
||||||
|
SurfaceConfiguration is a type that holds the generic datatype for light surface & dark surface and returns the value based on the surface.
|
||||||
|
*/
|
||||||
|
struct SurfaceConfigurationValue<ValueType> {
|
||||||
|
|
||||||
|
var lightValue: ValueType
|
||||||
|
var darkValue: ValueType
|
||||||
|
|
||||||
|
public init(_ lightValue: ValueType, _ darkValue: ValueType) {
|
||||||
|
self.lightValue = lightValue
|
||||||
|
self.darkValue = darkValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(value: ValueType) {
|
||||||
|
self.lightValue = value
|
||||||
|
self.darkValue = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public func value(for object: Surfaceable) -> ValueType {
|
||||||
|
object.surface == .light ? lightValue : darkValue
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user