From 01083bd8c8552addc6743ab43a34a75a94e75758 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 28 Jul 2022 18:06:49 -0500 Subject: [PATCH] added classes Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 50 +++++++-- VDS/Classes/List.swift | 79 ++++++++++++++ VDS/Classes/Published.swift | 83 ++++++++++++++ .../VDSControl.swift} | 7 +- VDS/Classes/ViewModelState.swift | 21 ++++ VDS/Components/Label/VDSLabel.swift | 103 ++++++++++++++++++ VDS/Components/Label/VDSLabelModel.swift | 12 ++ VDS/Protocols/Initable.swift | 12 ++ VDS/Protocols/Labelable.swift | 15 +++ 9 files changed, 369 insertions(+), 13 deletions(-) create mode 100644 VDS/Classes/List.swift create mode 100644 VDS/Classes/Published.swift rename VDS/{BaseClasses/Control.swift => Classes/VDSControl.swift} (87%) create mode 100644 VDS/Classes/ViewModelState.swift create mode 100644 VDS/Components/Label/VDSLabel.swift create mode 100644 VDS/Components/Label/VDSLabelModel.swift create mode 100644 VDS/Protocols/Initable.swift create mode 100644 VDS/Protocols/Labelable.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 3f1fde97..5d30c97a 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -20,7 +20,7 @@ EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361AE288B26310071C351 /* FormFieldable.swift */; }; EA3361B1288B26490071C351 /* Invertable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B0288B26490071C351 /* Invertable.swift */; }; EA3361B3288B265D0071C351 /* Changable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B2288B265D0071C351 /* Changable.swift */; }; - EA3361B6288B2A410071C351 /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B5288B2A410071C351 /* Control.swift */; }; + EA3361B6288B2A410071C351 /* VDSControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B5288B2A410071C351 /* VDSControl.swift */; }; EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */; }; EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361BC288B2C760071C351 /* TypeAlias.swift */; }; EA3361BF288B2EA60071C351 /* Modelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361BE288B2EA60071C351 /* Modelable.swift */; }; @@ -37,6 +37,13 @@ EA3362302891EB4A0071C351 /* VDSFonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33622F2891EB4A0071C351 /* VDSFonts.swift */; }; EA3362322891F2ED0071C351 /* VDSFontStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362312891F2ED0071C351 /* VDSFontStyles.swift */; }; EA33623E2892EE950071C351 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33623D2892EE950071C351 /* UIDevice.swift */; }; + EA3362402892EF6C0071C351 /* VDSLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33623F2892EF6B0071C351 /* VDSLabel.swift */; }; + EA3362432892EFF20071C351 /* VDSLabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362422892EFF20071C351 /* VDSLabelModel.swift */; }; + EA3362452892F9130071C351 /* Labelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362442892F9130071C351 /* Labelable.swift */; }; + EA33624728931B050071C351 /* Initable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33624628931B050071C351 /* Initable.swift */; }; + EA33624928931B5C0071C351 /* ViewModelState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33624828931B5C0071C351 /* ViewModelState.swift */; }; + EA33624B28931B750071C351 /* Published.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33624A28931B750071C351 /* Published.swift */; }; + EA33624D28931BB50071C351 /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33624C28931BB50071C351 /* List.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -64,7 +71,7 @@ EA3361AE288B26310071C351 /* FormFieldable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormFieldable.swift; sourceTree = ""; }; EA3361B0288B26490071C351 /* Invertable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Invertable.swift; sourceTree = ""; }; EA3361B2288B265D0071C351 /* Changable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Changable.swift; sourceTree = ""; }; - EA3361B5288B2A410071C351 /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = ""; }; + EA3361B5288B2A410071C351 /* VDSControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSControl.swift; sourceTree = ""; }; EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewProtocol.swift; sourceTree = ""; }; EA3361BC288B2C760071C351 /* TypeAlias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeAlias.swift; sourceTree = ""; }; EA3361BE288B2EA60071C351 /* Modelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modelable.swift; sourceTree = ""; }; @@ -81,6 +88,13 @@ EA33622F2891EB4A0071C351 /* VDSFonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSFonts.swift; sourceTree = ""; }; EA3362312891F2ED0071C351 /* VDSFontStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSFontStyles.swift; sourceTree = ""; }; EA33623D2892EE950071C351 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = ""; }; + EA33623F2892EF6B0071C351 /* VDSLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSLabel.swift; sourceTree = ""; }; + EA3362422892EFF20071C351 /* VDSLabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSLabelModel.swift; sourceTree = ""; }; + EA3362442892F9130071C351 /* Labelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Labelable.swift; sourceTree = ""; }; + EA33624628931B050071C351 /* Initable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Initable.swift; sourceTree = ""; }; + EA33624828931B5C0071C351 /* ViewModelState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelState.swift; sourceTree = ""; }; + EA33624A28931B750071C351 /* Published.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Published.swift; sourceTree = ""; }; + EA33624C28931BB50071C351 /* List.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = List.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -127,7 +141,7 @@ EA33616E288B19200071C351 /* VDS */ = { isa = PBXGroup; children = ( - EA3361B4288B2A360071C351 /* BaseClasses */, + EA3361B4288B2A360071C351 /* Classes */, EA33619D288B1E330071C351 /* Components */, EA3361A6288B23240071C351 /* Extensions */, EA3361AB288B25EC0071C351 /* Protocols */, @@ -162,6 +176,7 @@ isa = PBXGroup; children = ( EA3361A0288B1E6F0071C351 /* Toggle */, + EA3362412892EF700071C351 /* Label */, ); path = Components; sourceTree = ""; @@ -193,7 +208,9 @@ EA3361AC288B26190071C351 /* DataTrackable.swift */, EA3361A9288B25E40071C351 /* Disabling.swift */, EA3361AE288B26310071C351 /* FormFieldable.swift */, + EA33624628931B050071C351 /* Initable.swift */, EA3361B0288B26490071C351 /* Invertable.swift */, + EA3362442892F9130071C351 /* Labelable.swift */, EA3361BE288B2EA60071C351 /* Modelable.swift */, EA3361C8289054C50071C351 /* Surfaceable.swift */, EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, @@ -201,12 +218,15 @@ path = Protocols; sourceTree = ""; }; - EA3361B4288B2A360071C351 /* BaseClasses */ = { + EA3361B4288B2A360071C351 /* Classes */ = { isa = PBXGroup; children = ( - EA3361B5288B2A410071C351 /* Control.swift */, + EA3361B5288B2A410071C351 /* VDSControl.swift */, + EA33624A28931B750071C351 /* Published.swift */, + EA33624828931B5C0071C351 /* ViewModelState.swift */, + EA33624C28931BB50071C351 /* List.swift */, ); - path = BaseClasses; + path = Classes; sourceTree = ""; }; EA3361B9288B2BE30071C351 /* Utilities */ = { @@ -246,6 +266,15 @@ path = Fonts; sourceTree = ""; }; + EA3362412892EF700071C351 /* Label */ = { + isa = PBXGroup; + children = ( + EA33623F2892EF6B0071C351 /* VDSLabel.swift */, + EA3362422892EFF20071C351 /* VDSLabelModel.swift */, + ); + path = Label; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -361,17 +390,24 @@ files = ( EA3362322891F2ED0071C351 /* VDSFontStyles.swift in Sources */, EA3361C328902D960071C351 /* VDSToggle.swift in Sources */, + EA3362402892EF6C0071C351 /* VDSLabel.swift in Sources */, EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */, EA3361C5289030FC0071C351 /* Accessable.swift in Sources */, + EA33624928931B5C0071C351 /* ViewModelState.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */, EA3361A2288B1E840071C351 /* VDSToggleModel.swift in Sources */, + EA3362432892EFF20071C351 /* VDSLabelModel.swift in Sources */, + EA33624728931B050071C351 /* Initable.swift in Sources */, EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, EA3361B3288B265D0071C351 /* Changable.swift in Sources */, EA336171288B19200071C351 /* VDS.docc in Sources */, EA3361AA288B25E40071C351 /* Disabling.swift in Sources */, - EA3361B6288B2A410071C351 /* Control.swift in Sources */, + EA3361B6288B2A410071C351 /* VDSControl.swift in Sources */, + EA33624D28931BB50071C351 /* List.swift in Sources */, + EA3362452892F9130071C351 /* Labelable.swift in Sources */, + EA33624B28931B750071C351 /* Published.swift in Sources */, EA3361B1288B26490071C351 /* Invertable.swift in Sources */, EA3361AD288B26190071C351 /* DataTrackable.swift in Sources */, EA33623E2892EE950071C351 /* UIDevice.swift in Sources */, diff --git a/VDS/Classes/List.swift b/VDS/Classes/List.swift new file mode 100644 index 00000000..4c92a95f --- /dev/null +++ b/VDS/Classes/List.swift @@ -0,0 +1,79 @@ +// +// List.swift +// VDS +// +// Created by Matt Bruce on 7/28/22. +// +// https://www.swiftbysundell.com/articles/picking-the-right-data-structure-in-swift/ + +import Foundation + +public struct List { + public private(set) var firstNode: Node? + public private(set) var lastNode: Node? +} + +extension List { + public class Node { + public var value: Value + public fileprivate(set) weak var previous: Node? + public fileprivate(set) var next: Node? + + public init(value: Value) { + self.value = value + } + } +} + +extension List: Sequence { + public func makeIterator() -> AnyIterator { + var node = firstNode + + return AnyIterator { + // Iterate through all of our nodes by continuously + // moving to the next one and extract its value: + let value = node?.value + node = node?.next + return value + } + } +} + +extension List { + @discardableResult + public mutating func append(_ value: Value) -> Node { + let node = Node(value: value) + node.previous = lastNode + + lastNode?.next = node + lastNode = node + + if firstNode == nil { + firstNode = node + } + + return node + } +} + +extension List { + public mutating func remove(_ node: Node) { + node.previous?.next = node.next + node.next?.previous = node.previous + + // Using "triple-equals" we can compare two class + // instances by identity, rather than by value: + if firstNode === node { + firstNode = node.next + } + + if lastNode === node { + lastNode = node.previous + } + + // Completely disconnect the node by removing its + // sibling references: + node.next = nil + node.previous = nil + } +} diff --git a/VDS/Classes/Published.swift b/VDS/Classes/Published.swift new file mode 100644 index 00000000..59cc23b4 --- /dev/null +++ b/VDS/Classes/Published.swift @@ -0,0 +1,83 @@ +//// +//// Published.swift +//// VDS +//// +//// Created by Matt Bruce on 7/28/22. +//// +// +//import Foundation +// +////https://www.swiftbysundell.com/articles/published-properties-in-swift/ +//@propertyWrapper +//public struct Published { +// public var projectedValue: Published { self } +// public var wrappedValue: Value { +// didSet { +// valueDidChange() +// } +// } +// +// private var observations = MutableReference( +// value: List<(Value) -> Void>() +// ) +// +// public init(wrappedValue: Value) { +// self.wrappedValue = wrappedValue +// } +//} +// +//private extension Published { +// func valueDidChange() { +// for closure in observations.value { +// closure(wrappedValue) +// } +// } +//} +// +//extension Published { +// public func observe(with closure: @escaping (Value) -> Void) -> Cancellable { +// // To further mimmic Combine's behaviors, we'll call +// // each observation closure as soon as it's attached to +// // our property: +// closure(wrappedValue) +// +// let node = observations.value.append(closure) +// +// return Cancellable { [weak observations] in +// observations?.value.remove(node) +// } +// } +//} +// +//public class Cancellable { +// private var closure: (() -> Void)? +// +// public init(closure: @escaping () -> Void) { +// self.closure = closure +// } +// +// deinit { +// cancel() +// } +// +// public func cancel() { +// closure?() +// closure = nil +// } +//} +// +// +/////https://www.swiftbysundell.com/articles/combining-value-and-reference-types-in-swift/ +//public class Reference { +// fileprivate(set) var value: Value +// +// public init(value: Value) { +// self.value = value +// } +//} +// +//public class MutableReference: Reference { +// public func update(with value: Value) { +// self.value = value +// } +//} diff --git a/VDS/BaseClasses/Control.swift b/VDS/Classes/VDSControl.swift similarity index 87% rename from VDS/BaseClasses/Control.swift rename to VDS/Classes/VDSControl.swift index 9d947b7a..e8b4d8e5 100644 --- a/VDS/BaseClasses/Control.swift +++ b/VDS/Classes/VDSControl.swift @@ -8,12 +8,11 @@ import Foundation import UIKit -@objcMembers open class Control: UIControl, Modelable, ViewProtocol { +@objcMembers open class VDSControl: UIControl, ViewProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- private var initialSetupPerformed = false - public var model: ModelType? //-------------------------------------------------- // MARK: - Initializers @@ -45,10 +44,6 @@ import UIKit } } - open func set(with model: ModelType) { - self.model = model - } - open func reset() { backgroundColor = .clear } diff --git a/VDS/Classes/ViewModelState.swift b/VDS/Classes/ViewModelState.swift new file mode 100644 index 00000000..639ec51d --- /dev/null +++ b/VDS/Classes/ViewModelState.swift @@ -0,0 +1,21 @@ +// +// ViewModelState.swift +// VDS +// +// Created by Matt Bruce on 7/28/22. +// + +import Foundation +import Combine + +////final public class ViewModelState: ObservableObject, Initable { +//// @Published public var viewModel = ViewModelType() +//// public init(){} +////} +//final public class ViewModelState: ObservableObject, Modelable { +// @Published public var model: ViewModelType +// public init(){} +// public func set(with model: ViewModelType) { +// self.model = model +// } +//} diff --git a/VDS/Components/Label/VDSLabel.swift b/VDS/Components/Label/VDSLabel.swift new file mode 100644 index 00000000..d9a57396 --- /dev/null +++ b/VDS/Components/Label/VDSLabel.swift @@ -0,0 +1,103 @@ +// +// VDSLabel.swift +// VDS +// +// Created by Matt Bruce on 7/28/22. +// + +import Foundation +import UIKit +import VDSColorTokens +import Combine + +open class DefaultLabelModel: VDSLabelModel { + public var fontCategory: VDSFontCategory = .body + public var fontSize: VDSFontSize = .large + public var fontWeight: VDSFontWeight = .regular + public var textPosition: VDSTextPosition = .left + public var surface: Surface = .light + required public init(){} +} + +open class VDSLabel: UILabel, Modelable, ObservableObject { + @Published public var model: VDSLabelModel = DefaultLabelModel() + private var cancellable: AnyCancellable? + + public var fontSize: VDSFontSize = .large { + didSet { + if fontSize != model.fontSize { + model.fontSize = fontSize + } + } + } + + public var textPosition: VDSTextPosition = .left{ + didSet { + if textPosition != model.textPosition { + model.textPosition = textPosition + } + } + } + + public var fontWeight: VDSFontWeight = .regular { + didSet { + if fontWeight != model.fontWeight { + model.fontWeight = fontWeight + } + } + } + + public var fontCategory: VDSFontCategory = .body { + didSet { + if fontCategory != model.fontCategory { + model.fontCategory = fontCategory + } + } + } + + public var surface: Surface = .light { + didSet { + if surface != model.surface { + model.surface = surface + } + } + } + + //Initializers + public convenience init() { + self.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + func setup() { + cancellable = $model.sink { [weak self] viewModel in + self?.onStateChange(viewModel: viewModel) + } + } + + //functions + private func onStateChange(viewModel: VDSLabelModel) { + textAlignment = viewModel.textPosition.textAlignment + textColor = viewModel.surface == .dark ? VDSColor.elementsPrimaryOndark : VDSColor.elementsPrimaryOnlight + + guard let vdsFont = try? VDSFontStyle.font(for: viewModel.fontCategory, fontWeight: viewModel.fontWeight, fontSize: viewModel.fontSize) else { + font = VDSFontStyle.RegularBodyLarge.font + return + } + font = vdsFont + } + + //Modelable + public func set(with model: VDSLabelModel) { + self.model = model + } +} diff --git a/VDS/Components/Label/VDSLabelModel.swift b/VDS/Components/Label/VDSLabelModel.swift new file mode 100644 index 00000000..ca93ae16 --- /dev/null +++ b/VDS/Components/Label/VDSLabelModel.swift @@ -0,0 +1,12 @@ +// +// VDSLabelModel.swift +// VDS +// +// Created by Matt Bruce on 7/28/22. +// + +import Foundation +import UIKit + +public protocol VDSLabelModel: Labelable, Surfaceable { +} diff --git a/VDS/Protocols/Initable.swift b/VDS/Protocols/Initable.swift new file mode 100644 index 00000000..8a6eacb9 --- /dev/null +++ b/VDS/Protocols/Initable.swift @@ -0,0 +1,12 @@ +// +// Initable.swift +// VDS +// +// Created by Matt Bruce on 7/28/22. +// + +import Foundation + +public protocol Initable { + init() +} diff --git a/VDS/Protocols/Labelable.swift b/VDS/Protocols/Labelable.swift new file mode 100644 index 00000000..90dc545c --- /dev/null +++ b/VDS/Protocols/Labelable.swift @@ -0,0 +1,15 @@ +// +// LabelModelable.swift +// VDS +// +// Created by Matt Bruce on 7/28/22. +// + +import Foundation + +public protocol Labelable { + var fontSize: VDSFontSize { get set } + var textPosition: VDSTextPosition { get set } + var fontWeight: VDSFontWeight { get set } + var fontCategory: VDSFontCategory { get set } +}