Merge branch 'release/1.0.19' into 'develop'
build for bugfix See merge request BPHV_MIPS/vds_ios!74
This commit is contained in:
commit
495051f04b
@ -95,6 +95,7 @@
|
||||
EAB5FEF5292D371F00998C17 /* ButtonBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF4292D371F00998C17 /* ButtonBase.swift */; };
|
||||
EAB5FEF829393A7200998C17 /* ButtonGroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */; };
|
||||
EAB5FF0129424ACB00998C17 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB5FF0029424ACB00998C17 /* UIControl.swift */; };
|
||||
EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABFEB632A26473700C4C106 /* NSAttributedString.swift */; };
|
||||
EAC846F3294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */; };
|
||||
EAC9257D29119B5400091998 /* TextLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9257C29119B5400091998 /* TextLink.swift */; };
|
||||
EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925822911B35300091998 /* TextLinkCaret.swift */; };
|
||||
@ -222,6 +223,7 @@
|
||||
EAB5FEF4292D371F00998C17 /* ButtonBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonBase.swift; sourceTree = "<group>"; };
|
||||
EAB5FEF729393A7200998C17 /* ButtonGroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupConstants.swift; sourceTree = "<group>"; };
|
||||
EAB5FF0029424ACB00998C17 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
|
||||
EABFEB632A26473700C4C106 /* NSAttributedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAttributedString.swift; sourceTree = "<group>"; };
|
||||
EAC846F2294B95CE00F685BA /* ButtonGroupCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
EAC9257C29119B5400091998 /* TextLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLink.swift; sourceTree = "<group>"; };
|
||||
EAC925822911B35300091998 /* TextLinkCaret.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLinkCaret.swift; sourceTree = "<group>"; };
|
||||
@ -418,6 +420,7 @@
|
||||
children = (
|
||||
EAF7F0992899B17200B287F5 /* CATransaction.swift */,
|
||||
EA33622D2891EA3C0071C351 /* DispatchQueue+Once.swift */,
|
||||
EABFEB632A26473700C4C106 /* NSAttributedString.swift */,
|
||||
EAB2376529E9952D00AABE9A /* UIApplication.swift */,
|
||||
EA3361A7288B23300071C351 /* UIColor.swift */,
|
||||
EA81410F2A127066004F60D2 /* UIColor+VDSColor.swift */,
|
||||
@ -871,6 +874,7 @@
|
||||
EAF7F0B1289B177F00B287F5 /* ColorLabelAttribute.swift in Sources */,
|
||||
EAC9258F2911C9DE00091998 /* EntryField.swift in Sources */,
|
||||
EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */,
|
||||
EABFEB642A26473700C4C106 /* NSAttributedString.swift in Sources */,
|
||||
EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */,
|
||||
EA0FC2C62914222900DF80B4 /* ButtonGroup.swift in Sources */,
|
||||
EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */,
|
||||
@ -1070,7 +1074,7 @@
|
||||
buildSettings = {
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 18;
|
||||
CURRENT_PROJECT_VERSION = 19;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@ -1103,7 +1107,7 @@
|
||||
buildSettings = {
|
||||
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 18;
|
||||
CURRENT_PROJECT_VERSION = 19;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
|
||||
/// Bundle Manager keeps all bundles together for ease of use for searching within any of them for a specific asset
|
||||
public class BundleManager {
|
||||
public static var shared = BundleManager()
|
||||
|
||||
@ -28,6 +30,8 @@ public class BundleManager {
|
||||
})
|
||||
}
|
||||
|
||||
/// With take a location and if found append to the local array for use later for asset searching.
|
||||
/// - Parameter path: Location of the bundle
|
||||
public static func register(_ path: String) {
|
||||
if !shared.paths.contains(where: {$0 == path}) {
|
||||
if let bundle = Bundle(path: path) {
|
||||
@ -40,6 +44,9 @@ public class BundleManager {
|
||||
|
||||
extension BundleManager {
|
||||
|
||||
/// Searches through all registered bundles for an image
|
||||
/// - Parameter name: Name of the image
|
||||
/// - Returns: Will find an image or not.
|
||||
public func image(for name: String) -> UIImage? {
|
||||
var foundImage: UIImage?
|
||||
|
||||
|
||||
@ -21,7 +21,9 @@ public typealias ObjectColorable = Colorable & Initable & ObjectWithable
|
||||
/// config.darkColor = .white
|
||||
///
|
||||
/// let textColor = config.getColor(model) //returns .black
|
||||
///
|
||||
|
||||
/// You can pass in a Surfaceable object and it will give you the color responding.
|
||||
open class SurfaceColorConfiguration: ObjectColorable {
|
||||
public typealias ObjectType = Surfaceable
|
||||
public var lightColor: UIColor = .clear
|
||||
@ -29,15 +31,27 @@ open class SurfaceColorConfiguration: ObjectColorable {
|
||||
|
||||
required public init(){}
|
||||
|
||||
/// Inititialization
|
||||
/// - Parameters:
|
||||
/// - lightColor: Color used for a light surface
|
||||
/// - darkColor: Color used for a dark surface
|
||||
public init(_ lightColor: UIColor, _ darkColor: UIColor) {
|
||||
self.lightColor = lightColor
|
||||
self.darkColor = darkColor
|
||||
}
|
||||
|
||||
|
||||
/// Will get the UIColor for the surface passed in
|
||||
/// - Parameter surface: surface you currently have
|
||||
/// - Returns: color corresponding to the surface you passed
|
||||
public func getColor(_ surface: Surface) -> UIColor {
|
||||
return surface == .light ? lightColor : darkColor
|
||||
}
|
||||
|
||||
|
||||
/// Colorable Generic method that will only take in any object that has a Surfacable implementation
|
||||
/// - Parameter object: any Surfacable object type
|
||||
/// - Returns: UIColor for the object type you passed in
|
||||
public func getColor(_ object: any ObjectType) -> UIColor {
|
||||
return getColor(object.surface)
|
||||
}
|
||||
@ -57,16 +71,30 @@ public protocol KeyColorConfigurable: ObjectColorable {
|
||||
}
|
||||
|
||||
extension KeyColorConfigurable {
|
||||
|
||||
/// Generic Method to set a KeyColorConfiguration with the parameters given
|
||||
/// - Parameters:
|
||||
/// - lightColor: Color used for a light
|
||||
/// - darkColor: Color used for a dark
|
||||
/// - key: <#key description#>
|
||||
public func setSurfaceColors(_ lightColor: UIColor, _ darkColor: UIColor, forKey key: KeyType) {
|
||||
keyColors.append(.init(key: key, surfaceConfig: .init(lightColor, darkColor)))
|
||||
}
|
||||
|
||||
|
||||
/// Removes all keyColors
|
||||
public func reset() {
|
||||
keyColors.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
extension KeyColorConfigurable where ObjectType: Surfaceable {
|
||||
|
||||
/// Default Implementation for when the ObjectType is a Surfacable
|
||||
/// - Parameters:
|
||||
/// - object: Surfaceable ObjectType
|
||||
/// - key: KeyPath to the property of the ObjectType
|
||||
/// - Returns: UIColor corresponding the the key and surface
|
||||
public func getColor(for object: ObjectType, with key: KeyType) -> UIColor {
|
||||
if let keyColor = keyColors.first(where: {$0.key == key }) {
|
||||
return keyColor.surfaceConfig.getColor(object)
|
||||
@ -77,6 +105,8 @@ extension KeyColorConfigurable where ObjectType: Surfaceable {
|
||||
}
|
||||
|
||||
|
||||
/// ColorConfiguration for UIControls and will implement KeyColorConfigurale. This will then use the
|
||||
/// register a SurfaceColorConfiguration for each state.
|
||||
public class ControlColorConfiguration: KeyColorConfigurable {
|
||||
public typealias KeyType = UIControl.State
|
||||
public typealias ObjectType = Surfaceable & UIControl
|
||||
@ -84,10 +114,19 @@ public class ControlColorConfiguration: KeyColorConfigurable {
|
||||
private var lastKeyColor: KeyColorConfiguration<KeyType>?
|
||||
public required init() { }
|
||||
|
||||
|
||||
/// Helper method to set the SurfaceColorConfiguration for a state
|
||||
/// - Parameters:
|
||||
/// - lightColor: Color used for a light
|
||||
/// - darkColor: Color used for a dark
|
||||
/// - state: UIControlState you are keying off of.
|
||||
public func setSurfaceColors(_ lightColor: UIColor, _ darkColor: UIColor, forState state: KeyType) {
|
||||
setSurfaceColors(lightColor, darkColor, forKey: state)
|
||||
}
|
||||
|
||||
/// Colorable implementation for getColor that where the object passed in will be of a UIControl as well as implementing Surfaceable.
|
||||
/// - Parameter object: Object that is of UIControl and implements Surfacable
|
||||
/// - Returns: UIColor corresponding to the UIControl.state and surface property of this object.
|
||||
public func getColor(_ object: any ObjectType) -> UIColor {
|
||||
let state = object.state
|
||||
let surface = object.surface
|
||||
@ -115,6 +154,7 @@ public class ControlColorConfiguration: KeyColorConfigurable {
|
||||
}
|
||||
}
|
||||
|
||||
///Meant to be used with any object that implements Surfaceable and Disabling. More than likely this is any View.
|
||||
public class ViewColorConfiguration: KeyColorConfigurable {
|
||||
public typealias KeyType = Bool
|
||||
public typealias ObjectType = Surfaceable & Disabling
|
||||
@ -122,10 +162,18 @@ public class ViewColorConfiguration: KeyColorConfigurable {
|
||||
|
||||
public required init() { }
|
||||
|
||||
/// Helper method to set the SurfaceColorConfiguration based on whether the object is disabled or not.
|
||||
/// - Parameters:
|
||||
/// - lightColor: Color used for a light
|
||||
/// - darkColor: Color used for a dark
|
||||
/// - disabled: True/False for disabled property
|
||||
public func setSurfaceColors(_ lightColor: UIColor, _ darkColor: UIColor, forDisabled disabled: KeyType) {
|
||||
setSurfaceColors(lightColor, darkColor, forKey: disabled)
|
||||
}
|
||||
|
||||
/// Colorable implementation for getColor that where the object passed in will any object that implementing Surfaceable and Disabling.
|
||||
/// - Parameter object: Object that implements Surfaceable and Disabling
|
||||
/// - Returns: UIColor correspoding to either true/false for the disabled state and surface
|
||||
public func getColor(_ object: ObjectType) -> UIColor {
|
||||
if let keyColor = keyColors.first(where: {$0.key == object.disabled }) {
|
||||
return keyColor.surfaceConfig.getColor(object)
|
||||
@ -154,11 +202,17 @@ public class KeyedColorConfiguration<ObjectType: Surfaceable, KeyType: Equatable
|
||||
self.keyPath = keyPath
|
||||
}
|
||||
|
||||
/// Finds the KeyColorConfiguration for the Object passed in.
|
||||
/// - Parameter object: Object should be of Surfaceable
|
||||
/// - Returns: KeyType for the Object passed in
|
||||
public func getKeyValue(_ object: ObjectType) -> KeyType? {
|
||||
guard let keyPath else { fatalError("keyPath must not be empty, make sure you initialize this class using init(keyPath: \\Object.property) method") }
|
||||
return object[keyPath: keyPath]
|
||||
}
|
||||
|
||||
/// Colorable implementation for getColor that where the object passed in will any object that implementing Surfaceable for the KeyPath registered.
|
||||
/// - Parameter object: Object matching the type registered
|
||||
/// - Returns: UIColor for the Object pertaining to the KeyPath it was registered
|
||||
public func getColor(_ object: ObjectType) -> UIColor {
|
||||
if let key = getKeyValue(object), let keyColor = keyColors.first(where: {$0.key == key }) {
|
||||
return keyColor.surfaceConfig.getColor(object)
|
||||
|
||||
@ -15,7 +15,11 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
|
||||
//--------------------------------------------------
|
||||
// MARK: - Combine Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Set of Subscribers for any Publishers for this Control
|
||||
public var subscribers = Set<AnyCancellable>()
|
||||
|
||||
/// Sets the primary Subscriber used for the TouchUpInside
|
||||
public var onClickSubscriber: AnyCancellable? {
|
||||
willSet {
|
||||
if let onClickSubscriber {
|
||||
@ -28,37 +32,46 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
|
||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||
open var shouldUpdateView: Bool = true
|
||||
|
||||
/// Dictionary for keeping information for this Control use only Primitives
|
||||
open var userInfo = [String: Primitive]()
|
||||
|
||||
|
||||
/// Current Surface used within this Control and used to passdown to child views
|
||||
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||
|
||||
/// Control is disabled or not
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
|
||||
/// Override for isSelected to handle setNeedsUpdate() on changes
|
||||
open override var isSelected: Bool { didSet { setNeedsUpdate() } }
|
||||
|
||||
/// Reference count use to be used in isHighlighted when a subscriber is listening for TouchUpInside.
|
||||
public var touchUpInsideCount: Int = 0
|
||||
|
||||
var isHighlightAnimating = false
|
||||
|
||||
/// Override to deal with only calling setNeedsUpdate() if needed
|
||||
open override var isHighlighted: Bool {
|
||||
didSet {
|
||||
if isHighlightAnimating == false && touchUpInsideCount > 0 {
|
||||
isHighlightAnimating = true
|
||||
UIView.animate(withDuration: 0.1, animations: { [weak self] in
|
||||
self?.updateView()
|
||||
self?.setNeedsUpdate()
|
||||
}) { [weak self] _ in
|
||||
//you update the view since this is typically a quick change
|
||||
UIView.animate(withDuration: 0.1, animations: { [weak self] in
|
||||
self?.updateView()
|
||||
self?.setNeedsUpdate()
|
||||
self?.isHighlightAnimating = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Override to deal with setNeedsUpdate()
|
||||
open override var isEnabled: Bool {
|
||||
get { !disabled }
|
||||
set {
|
||||
@ -92,6 +105,7 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
|
||||
// MARK: - Setup
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Executed on initialization for this Control
|
||||
open func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
@ -99,7 +113,9 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
|
||||
setNeedsUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///Override to deal with sending actions for accessibility
|
||||
/// - Returns: Based on whether the userInteraction is enabled
|
||||
override open func accessibilityActivate() -> Bool {
|
||||
// Hold state in case User wanted isAnimated to remain off.
|
||||
guard isUserInteractionEnabled else { return false }
|
||||
@ -110,22 +126,25 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Update this view based off of property changes
|
||||
open func updateView() {
|
||||
updateAccessibilityLabel()
|
||||
}
|
||||
|
||||
/// Used to update any Accessibility properties
|
||||
open func updateAccessibilityLabel() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Resets to the Controls default values
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
surface = .light
|
||||
disabled = false
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
/// Will be called only once.
|
||||
/// Will be called only once and should be overridden in subclasses to setup UI or defaults
|
||||
open func setup() {
|
||||
backgroundColor = .clear
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
@ -9,13 +9,17 @@ import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
/// Base Class used for any Grouped Form Control of a Selector Type
|
||||
open class SelectorGroupHandlerBase<HandlerType: Control>: Control, Changeable {
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Array of the HandlerType registered
|
||||
public var selectorViews: [HandlerType] = []
|
||||
|
||||
/// The primary subscriber for onChange or the UIControl valueChanged event.
|
||||
public var onChangeSubscriber: AnyCancellable? {
|
||||
willSet {
|
||||
if let onChangeSubscriber {
|
||||
@ -27,6 +31,8 @@ open class SelectorGroupHandlerBase<HandlerType: Control>: Control, Changeable {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Override to update the child SelectorViews disabled property for this group
|
||||
override public var disabled: Bool {
|
||||
didSet {
|
||||
selectorViews.forEach { handler in
|
||||
@ -35,6 +41,7 @@ open class SelectorGroupHandlerBase<HandlerType: Control>: Control, Changeable {
|
||||
}
|
||||
}
|
||||
|
||||
/// Override to update the child SelectorViews surface property for this group
|
||||
override public var surface: Surface {
|
||||
didSet {
|
||||
selectorViews.forEach { handler in
|
||||
@ -43,16 +50,20 @@ open class SelectorGroupHandlerBase<HandlerType: Control>: Control, Changeable {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for the Group to override on a select event
|
||||
/// - Parameter selectedControl: Selected Control the user interacted
|
||||
open func didSelect(_ selectedControl: HandlerType) {
|
||||
fatalError("Must override didSelect")
|
||||
}
|
||||
|
||||
/// Helper method to execute the valueChanged event
|
||||
public func valueChanged() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) { [weak self] in
|
||||
self?.sendActions(for: .valueChanged)
|
||||
}
|
||||
}
|
||||
|
||||
/// Override to update the child SelectorViews reset method for this group
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
selectorViews.forEach{ $0.reset() }
|
||||
@ -60,6 +71,8 @@ open class SelectorGroupHandlerBase<HandlerType: Control>: Control, Changeable {
|
||||
}
|
||||
|
||||
open class SelectorGroupSelectedHandlerBase<HandlerType: Control>: SelectorGroupHandlerBase<HandlerType>{
|
||||
|
||||
/// Current Selected Control for this group
|
||||
public var selectedHandler: HandlerType? {
|
||||
return selectorViews.filter { $0.isSelected == true }.first
|
||||
}
|
||||
|
||||
@ -9,12 +9,19 @@ import Foundation
|
||||
import UIKit
|
||||
|
||||
@objc(VDSSelfSizingCollectionView)
|
||||
/// UICollectionView subclassed used to deal with Changing the size of itself based on its children and layout and changes of its contentSize
|
||||
public final class SelfSizingCollectionView: UICollectionView {
|
||||
|
||||
private var contentSizeObservation: NSKeyValueObservation?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Initializer
|
||||
/// - Parameters:
|
||||
/// - frame: Frame needed
|
||||
/// - layout: Layout used for this CollectionView
|
||||
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
|
||||
super.init(frame: frame, collectionViewLayout: layout)
|
||||
self.setupContentSizeObservation()
|
||||
@ -25,14 +32,18 @@ public final class SelfSizingCollectionView: UICollectionView {
|
||||
self.setupContentSizeObservation()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - UIView
|
||||
//--------------------------------------------------
|
||||
|
||||
|
||||
public override var intrinsicContentSize: CGSize {
|
||||
let contentSize = self.contentSize
|
||||
//print(#function, contentSize)
|
||||
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
|
||||
}
|
||||
|
||||
|
||||
/// Overridden to deal with Appearance Changes
|
||||
public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
//print(type(of: self), #function)
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
@ -43,13 +54,9 @@ public final class SelfSizingCollectionView: UICollectionView {
|
||||
}
|
||||
}
|
||||
|
||||
public override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
|
||||
let size = super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
|
||||
//print(type(of: self), #function, targetSize, "->", size)
|
||||
return size
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Private
|
||||
//--------------------------------------------------
|
||||
|
||||
private func setupContentSizeObservation() {
|
||||
// Observing the value of contentSize seems to be the only reliable way to get the contentSize after the collection view lays out its subviews.
|
||||
@ -63,7 +70,10 @@ public final class SelfSizingCollectionView: UICollectionView {
|
||||
}
|
||||
|
||||
extension UITraitCollection {
|
||||
|
||||
|
||||
/// Used within SelfSizingCollectionView to determine if there is an appearance change
|
||||
/// - Parameter traitCollection: TraitCollection to compare
|
||||
/// - Returns: True/False based on the trailCollection passed in
|
||||
public func hasDifferentTextAppearance(comparedTo traitCollection: UITraitCollection?) -> Bool {
|
||||
var result = self.preferredContentSizeCategory != traitCollection?.preferredContentSizeCategory
|
||||
result = result || self.legibilityWeight != traitCollection?.legibilityWeight
|
||||
|
||||
@ -23,14 +23,19 @@ open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
//--------------------------------------------------
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
/// Key of whether or not updateView() is called in setNeedsUpdate()
|
||||
open var shouldUpdateView: Bool = true
|
||||
|
||||
/// Dictionary for keeping information for this Control use only Primitives
|
||||
open var userInfo = [String: Primitive]()
|
||||
|
||||
open var surface: Surface = .light { didSet { setNeedsUpdate() }}
|
||||
|
||||
/// Current Surface used within this Control and used to passdown to child views
|
||||
open var surface: Surface = .light { didSet { setNeedsUpdate() } }
|
||||
|
||||
/// Control is disabled or not
|
||||
open var disabled: Bool = false { didSet { isEnabled = !disabled } }
|
||||
|
||||
|
||||
/// Override to deal with setNeedsUpdate()
|
||||
open var isEnabled: Bool {
|
||||
get { !disabled }
|
||||
set {
|
||||
@ -64,6 +69,7 @@ open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
// MARK: - Setup
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Executed on initialization for this View
|
||||
open func initialSetup() {
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
@ -75,22 +81,25 @@ open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Overrides
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Update this view based off of property changes
|
||||
open func updateView() {
|
||||
updateAccessibilityLabel()
|
||||
}
|
||||
|
||||
/// Used to update any Accessibility properties
|
||||
open func updateAccessibilityLabel() {
|
||||
|
||||
}
|
||||
|
||||
/// Resets to the Views default values
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
surface = .light
|
||||
disabled = false
|
||||
}
|
||||
|
||||
// MARK: - ViewProtocol
|
||||
/// Will be called only once.
|
||||
/// Will be called only once and should be overridden in subclasses to setup UI or defaults
|
||||
open func setup() {
|
||||
backgroundColor = .clear
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
@ -28,7 +28,7 @@ open class TextLinkCaret: ButtonBase {
|
||||
TextStyle.boldBodyLarge
|
||||
}
|
||||
|
||||
private var imageAttribute: ImageSpaceLabelAttribute?
|
||||
private var imageAttribute: CaretLabelAttribute?
|
||||
|
||||
open override var attributes: [any LabelAttributeModel]? {
|
||||
guard let imageAttribute else { return nil }
|
||||
@ -100,20 +100,20 @@ open class TextLinkCaret: ButtonBase {
|
||||
}
|
||||
|
||||
open override func updateView() {
|
||||
imageAttribute = ImageSpaceLabelAttribute(tintColor: textColor, position: iconPosition)
|
||||
imageAttribute = CaretLabelAttribute(tintColor: textColor, position: iconPosition)
|
||||
super.updateView()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension TextLinkCaret {
|
||||
struct ImageSpaceLabelAttribute: LabelAttributeModel {
|
||||
struct CaretLabelAttribute: LabelAttributeModel {
|
||||
var id: UUID = .init()
|
||||
var location: Int = 0
|
||||
var length: Int = 1
|
||||
var tintColor: UIColor
|
||||
var position: IconPosition
|
||||
var spacerWidth: CGFloat = 2.0
|
||||
var spacerWidth: CGFloat = 4.0
|
||||
var width: CGFloat { caretSize.width + spacerWidth }
|
||||
var caretSize: CGSize { Icon.Size.xsmall.dimensions }
|
||||
|
||||
@ -124,22 +124,22 @@ extension TextLinkCaret {
|
||||
|
||||
func setAttribute(on attributedString: NSMutableAttributedString) {
|
||||
let imageAttr = ImageLabelAttribute(location: location, imageName: "\(position.rawValue)-caret-bold", frame: .init(x: 0, y: 0, width: caretSize.width, height: caretSize.height), tintColor: tintColor)
|
||||
let spaceAttr = ImageLabelAttribute(location: 0, imageName: "info", frame: .init(x: 0, y: 0, width: spacerWidth, height: 5.0), tintColor: .clear)
|
||||
let spacer = NSAttributedString.spacer(for: spacerWidth)
|
||||
|
||||
guard let image = try? imageAttr.getAttachment(),
|
||||
let spacer = try? spaceAttr.getAttachment() else { return }
|
||||
guard let image = try? imageAttr.getAttachment() else { return }
|
||||
|
||||
if position == .right {
|
||||
attributedString.append(NSAttributedString(attachment: spacer))
|
||||
attributedString.append(spacer)
|
||||
attributedString.append(NSAttributedString(attachment: image))
|
||||
} else {
|
||||
attributedString.insert(NSAttributedString(attachment: image), at: 0)
|
||||
attributedString.insert(NSAttributedString(attachment: spacer), at: 1)
|
||||
attributedString.insert(spacer, at: 1)
|
||||
}
|
||||
}
|
||||
|
||||
func isEqual(_ equatable: ImageSpaceLabelAttribute) -> Bool {
|
||||
func isEqual(_ equatable: CaretLabelAttribute) -> Bool {
|
||||
return id == equatable.id && range == equatable.range
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,24 +50,17 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable
|
||||
frame = CGRect(x: 0, y: -1, width: size.value.dimensions.width, height: size.value.dimensions.height)
|
||||
}
|
||||
}
|
||||
|
||||
//create the frame in which to hold the icon
|
||||
let spacerframe = CGRect(x: 0, y: 0, width: VDSLayout.Spacing.space1X.value, height: size.value.dimensions.height)
|
||||
|
||||
|
||||
//create the image icon and match the color of the text
|
||||
let tooltipAttribute = ImageLabelAttribute(location: location,
|
||||
imageName: "info",
|
||||
frame: frame,
|
||||
tintColor: imageTintColor)
|
||||
|
||||
let spacerAttribute = ImageLabelAttribute(location: location,
|
||||
imageName: "info",
|
||||
frame: spacerframe,
|
||||
tintColor: .clear)
|
||||
let spacer = NSAttributedString.spacer(for: VDSLayout.Spacing.space1X.value)
|
||||
|
||||
guard let tooltip = try? tooltipAttribute.getAttachment(),
|
||||
let spacer = try? spacerAttribute.getAttachment() else { return }
|
||||
attributedString.append(NSAttributedString(attachment: spacer))
|
||||
guard let tooltip = try? tooltipAttribute.getAttachment() else { return }
|
||||
attributedString.append(spacer)
|
||||
attributedString.append(NSAttributedString(attachment: tooltip))
|
||||
addHandler(on: attributedString)
|
||||
}
|
||||
|
||||
19
VDS/Extensions/NSAttributedString.swift
Normal file
19
VDS/Extensions/NSAttributedString.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// NSAttributedString.swift
|
||||
// VDS
|
||||
//
|
||||
// Created by Matt Bruce on 5/30/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension NSAttributedString {
|
||||
public static func spacer(for width: CGFloat) -> NSAttributedString {
|
||||
let spacerImage = UIImage()
|
||||
let spacerAttachment = NSTextAttachment()
|
||||
spacerAttachment.bounds = .init(x: 0, y: 0, width: width, height: 1)
|
||||
spacerAttachment.image = spacerImage
|
||||
return NSAttributedString(attachment: spacerAttachment)
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,9 @@ import UIKit
|
||||
|
||||
extension UIApplication {
|
||||
|
||||
/// Helper method to find the top most viewcontroller in the app
|
||||
/// - Parameter controller: UIViewController to test against
|
||||
/// - Returns: Found top most UIViewController
|
||||
public class func topViewController(controller: UIViewController? = UIApplication.shared.windows.first?.rootViewController) -> UIViewController? {
|
||||
|
||||
if let nav = controller as? UINavigationController {
|
||||
|
||||
@ -9,6 +9,8 @@ import Foundation
|
||||
import VDSColorTokens
|
||||
|
||||
extension UIColor {
|
||||
|
||||
/// Since VDSColorTokens is just a Class with Static Properties, this is an Enum for each property of that class for each of use within the VDS Library
|
||||
public enum VDSColor: String, CaseIterable {
|
||||
case paletteBlack
|
||||
case paletteWhite
|
||||
@ -96,7 +98,7 @@ extension UIColor {
|
||||
case elementsLowcontrastOnlight
|
||||
case elementsLowcontrastOndark
|
||||
|
||||
// Map each color name to its corresponding UIColor object.
|
||||
/// Map each color name to its corresponding UIColor object.
|
||||
public var uiColor: UIColor {
|
||||
do {
|
||||
let color = try VDSColorTokens.VDSColor.getTokenByString(tokenName: "VDSColor.\(rawValue)")
|
||||
@ -107,7 +109,10 @@ extension UIColor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Helper method to see if a UIColor exists in the VDSColor enumeration
|
||||
/// - Parameter color: UIColor needed to compare
|
||||
/// - Returns: True if found, false in not found in VDSColor enumeration
|
||||
public static func isVDSColor(color: UIColor) -> Bool {
|
||||
guard let hex = color.hexString else { return false }
|
||||
let found = VDSColor.allCases.first{ $0.uiColor.hexString == hex }
|
||||
|
||||
@ -14,26 +14,45 @@ extension UIColor {
|
||||
// MARK: - Functions
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Convenience to get a grayscale UIColor where the same value is used for red, green, and blue.
|
||||
/// Convenience to get a grayscale UIColor where the same value is used for red, green, and blue
|
||||
/// - Parameters:
|
||||
/// - rgb: red, green, and blue
|
||||
/// - alpha: alphay
|
||||
/// - Returns: UIColor that will be grayscale
|
||||
public class func grayscale(rgb: Int, alpha: CGFloat = 1.0) -> UIColor {
|
||||
|
||||
let grayscale = CGFloat(rgb) / 255.0
|
||||
return UIColor(red: grayscale, green: grayscale, blue: grayscale, alpha: alpha)
|
||||
}
|
||||
|
||||
/// Convenience to get a UIColor.
|
||||
/// Convenience to get an 8-Bit UIColor based on RGB values
|
||||
/// - Parameters:
|
||||
/// - red: Value for Red
|
||||
/// - green: Value for Green
|
||||
/// - blue: Value for Blue
|
||||
/// - alpha: Value for Alpha
|
||||
/// - Returns: UIColor for the values above
|
||||
public class func color8Bits(red: Int, green: Int, blue: Int, alpha: CGFloat = 1.0) -> UIColor {
|
||||
|
||||
return UIColor(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: alpha)
|
||||
}
|
||||
|
||||
/// Convenience to get a UIColor.
|
||||
/// Convenience to get an UIColor based on RGB values
|
||||
/// - Parameters:
|
||||
/// - red: Value for Red
|
||||
/// - green: Value for Green
|
||||
/// - blue: Value for Blue
|
||||
/// - alpha: Value for Alpha
|
||||
/// - Returns: UIColor for the values above
|
||||
public class func color8Bits(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat = 1.0) -> UIColor {
|
||||
|
||||
return UIColor(red: red / 255.0, green: green / 255.0, blue: blue / 255.0, alpha: alpha)
|
||||
}
|
||||
|
||||
/// Gets UIColor via an 8 digit hex string.
|
||||
/// - Parameters:
|
||||
/// - hex: HexCode string
|
||||
/// - Returns: UIColor for the values above
|
||||
public class func getColorBy(hex: String) -> UIColor {
|
||||
|
||||
var hexint: UInt64 = 0
|
||||
@ -48,7 +67,10 @@ extension UIColor {
|
||||
alpha: 1)
|
||||
}
|
||||
|
||||
/// Gets UIColor via an 8 digit hex string. The last two being the alpha channel.
|
||||
/// Gets UIColor via an 8 digit hex string that includes transparency.
|
||||
/// - Parameters:
|
||||
/// - hex: HexCode string
|
||||
/// - Returns: UIColor for the values above
|
||||
public class func getColorWithTransparencyBy(hex: String) -> UIColor {
|
||||
|
||||
var hexint: UInt64 = 0
|
||||
@ -63,6 +85,9 @@ extension UIColor {
|
||||
alpha: (CGFloat(hexint & 0xFF)) / 255)
|
||||
}
|
||||
|
||||
/// Generates a gradient color for the color passed in
|
||||
/// - Parameter color: Primary color for the gradient
|
||||
/// - Returns: UIColor that is gradient
|
||||
public class func gradientColor(_ color: UIColor?) -> UIColor {
|
||||
|
||||
var h: CGFloat = 0
|
||||
@ -77,7 +102,9 @@ extension UIColor {
|
||||
return .white
|
||||
}
|
||||
|
||||
/// - parameter color: The UIColor intended to retrieve its hex value.
|
||||
/// Gets the hex code value for a UIColor
|
||||
/// - Parameter color: UIColor intended to retrieve its hex value
|
||||
/// - Returns: Hex Code
|
||||
public class func hexString(for color: UIColor) -> String? {
|
||||
|
||||
guard let components = color.cgColor.components else { return nil }
|
||||
@ -111,7 +138,8 @@ extension UIColor {
|
||||
return nil
|
||||
}
|
||||
|
||||
/// - parameter color: The UIColor intended to retrieve its hex value.
|
||||
/// Gets the hex code value for the current UIColor
|
||||
/// - Returns: Hex Code string
|
||||
public var hexString: String? {
|
||||
|
||||
guard let components = cgColor.components else { return nil }
|
||||
@ -145,6 +173,8 @@ extension UIColor {
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Initialize a UIColor with a HexCode string
|
||||
/// - Parameter hexString: HexCode string to convert to a UIColor
|
||||
public convenience init(hexString: String) {
|
||||
let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var int = UInt64()
|
||||
@ -163,23 +193,3 @@ extension UIColor {
|
||||
self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIColor {
|
||||
convenience init(
|
||||
light lightModeColor: @escaping @autoclosure () -> UIColor,
|
||||
dark darkModeColor: @escaping @autoclosure () -> UIColor
|
||||
) {
|
||||
self.init { traitCollection in
|
||||
switch traitCollection.userInterfaceStyle {
|
||||
case .light:
|
||||
return lightModeColor()
|
||||
case .dark:
|
||||
return darkModeColor()
|
||||
case .unspecified:
|
||||
return lightModeColor()
|
||||
@unknown default:
|
||||
return lightModeColor()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIDevice {
|
||||
|
||||
/// Helper property to see if your current device running is an iPad or not
|
||||
public static var isIPad: Bool {
|
||||
UIDevice.current.userInterfaceIdiom == .pad
|
||||
}
|
||||
|
||||
@ -10,6 +10,11 @@ import UIKit
|
||||
|
||||
extension UITapGestureRecognizer {
|
||||
|
||||
/// Determines if the touch event has a action attribute within the range given
|
||||
/// - Parameters:
|
||||
/// - label: UILabel in question
|
||||
/// - targetRange: Range to look within
|
||||
/// - Returns: Wether the range in the label has an action
|
||||
public func didTapActionInLabel(_ label: UILabel, inRange targetRange: NSRange) -> Bool {
|
||||
|
||||
guard let attributedText = label.attributedText else { return false }
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
1.0.19
|
||||
=======
|
||||
- CXTDT-419731 - TextLinkCaret - Spacing issue
|
||||
|
||||
1.0.18
|
||||
=======
|
||||
- CXTDT-412383 - Badge Corner Radius / Color issue (resolved in previous build)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user