From cd327a7f780427586b3acddb9620f06824942cc0 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Aug 2022 14:35:28 -0500 Subject: [PATCH 1/5] added new attribute Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 10 ++++--- .../Attributes/LabelAttributeAttachment.swift | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 VDS/Components/Label/Attributes/LabelAttributeAttachment.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index c6251dea..79827a76 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ EAF7F11828A1475A00B287F5 /* RadioButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */; }; EAF7F12C28A1617600B287F5 /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F12B28A1617600B287F5 /* SelectorBase.swift */; }; EAF7F12F28A1619600B287F5 /* SelectorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F12E28A1619600B287F5 /* SelectorModel.swift */; }; + EAF7F13328A2A16500B287F5 /* LabelAttributeAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F13228A2A16500B287F5 /* LabelAttributeAttachment.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -139,6 +140,7 @@ EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonModel.swift; sourceTree = ""; }; EAF7F12B28A1617600B287F5 /* SelectorBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = ""; }; EAF7F12E28A1619600B287F5 /* SelectorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorModel.swift; sourceTree = ""; }; + EAF7F13228A2A16500B287F5 /* LabelAttributeAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelAttributeAttachment.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -269,9 +271,9 @@ EA3362442892F9130071C351 /* Labelable.swift */, EAF7F0BA289D80ED00B287F5 /* Modelable.swift */, EA3361BE288B2EA60071C351 /* ModelHandlerable.swift */, + EAF7F0A5289B0CE000B287F5 /* Resetable.swift */, EA3361C8289054C50071C351 /* Surfaceable.swift */, EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, - EAF7F0A5289B0CE000B287F5 /* Resetable.swift */, ); path = Protocols; sourceTree = ""; @@ -348,11 +350,12 @@ isa = PBXGroup; children = ( EAF7F0A3289B017C00B287F5 /* LabelAttributeModel.swift */, + EAF7F0B2289B1ADC00B287F5 /* LabelAttributeAction.swift */, + EAF7F13228A2A16500B287F5 /* LabelAttributeAttachment.swift */, + EAF7F0B0289B177F00B287F5 /* LabelAttributeColor.swift */, EAF7F0AA289B13FD00B287F5 /* LabelAttributeFont.swift */, EAF7F0AC289B142900B287F5 /* LabelAttributeStrikeThrough.swift */, EAF7F0AE289B144C00B287F5 /* LabelAttributeUnderline.swift */, - EAF7F0B0289B177F00B287F5 /* LabelAttributeColor.swift */, - EAF7F0B2289B1ADC00B287F5 /* LabelAttributeAction.swift */, ); path = Attributes; sourceTree = ""; @@ -510,6 +513,7 @@ EA33624728931B050071C351 /* Initable.swift in Sources */, EAF7F0A4289B017C00B287F5 /* LabelAttributeModel.swift in Sources */, EAF7F0B1289B177F00B287F5 /* LabelAttributeColor.swift in Sources */, + EAF7F13328A2A16500B287F5 /* LabelAttributeAttachment.swift in Sources */, EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */, EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */, EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */, diff --git a/VDS/Components/Label/Attributes/LabelAttributeAttachment.swift b/VDS/Components/Label/Attributes/LabelAttributeAttachment.swift new file mode 100644 index 00000000..35008d14 --- /dev/null +++ b/VDS/Components/Label/Attributes/LabelAttributeAttachment.swift @@ -0,0 +1,26 @@ +// +// LabelAttributeAttachment.swift +// VDS +// +// Created by Matt Bruce on 8/9/22. +// + +import Foundation +import UIKit + +public protocol LabelAttributeAttachment: LabelAttributeModel { + func getAttachment() throws -> NSTextAttachment +} + +extension LabelAttributeAttachment { + public func setAttribute(on attributedString: NSMutableAttributedString) { + do { + let mutableString = NSMutableAttributedString() + let attachment = try getAttachment() + mutableString.append(NSAttributedString(attachment: attachment)) + attributedString.insert(mutableString, at: location) + } catch { + //TODO: Error Handling + } + } +} From 4e6d9b2f9d1da10ef1e0bc3730bc0ee32dfd0540 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Aug 2022 14:36:37 -0500 Subject: [PATCH 2/5] new funcs Signed-off-by: Matt Bruce --- VDS/Classes/Control.swift | 31 ++++++++++++---- VDS/Classes/View.swift | 34 ++++++++++++++---- VDS/Components/Label/Label.swift | 35 +++++++++++++++---- .../SelectorBase/SelectorBase.swift | 6 +++- VDS/Protocols/ModelHandlerable.swift | 5 ++- 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/VDS/Classes/Control.swift b/VDS/Classes/Control.swift index 53b2f7f4..eecc62a6 100644 --- a/VDS/Classes/Control.swift +++ b/VDS/Classes/Control.swift @@ -13,14 +13,19 @@ import Combine open class Control: UIControl, ModelHandlerable, ViewProtocol, Resettable { @Published public var model: ModelType - private var cancellable: AnyCancellable? + private var cancellables = Set() + private var shouldUpdate: Bool = false open func set(with model: ModelType) { self.model = model } - open func onStateChange(viewModel: ModelType) { - + open func shouldUpdateView(viewModel: ModelType) -> Bool { + fatalError("Implement shouldUpdateView") + } + + open func updateView(viewModel: ModelType) { + fatalError("Implement updateView") } //-------------------------------------------------- @@ -58,9 +63,23 @@ open class Control: UIControl, ModelHandlerable, ViewProto public func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - cancellable = $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in - self?.onStateChange(viewModel: viewModel) - } + //setup shouldUpdate + $model.sink { [weak self] viewModel in + guard let self = self else { return } + let s = self.shouldUpdateView(viewModel: viewModel) + print("shouldUpdate - \(Self.self): \(s)") + self.shouldUpdate = s + }.store(in: &cancellables) + + //setup viewUpdate + $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in + guard let self = self else { return } + if self.shouldUpdate { + self.updateView(viewModel: viewModel) + self.shouldUpdate = false + print("didUpdate - \(Self.self)") + } + }.store(in: &cancellables) setup() } } diff --git a/VDS/Classes/View.swift b/VDS/Classes/View.swift index fc587d60..7e1812cb 100644 --- a/VDS/Classes/View.swift +++ b/VDS/Classes/View.swift @@ -13,16 +13,21 @@ import Combine open class View: UIView, ModelHandlerable, ViewProtocol, Resettable { @Published public var model: ModelType - private var cancellable: AnyCancellable? - + private var cancellables = Set() + private var shouldUpdate: Bool = false + open func set(with model: ModelType) { self.model = model } - open func onStateChange(viewModel: ModelType) { - + open func shouldUpdateView(viewModel: ModelType) -> Bool { + fatalError("Implement shouldUpdateView") } + open func updateView(viewModel: ModelType) { + fatalError("Implement updateView") + } + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -57,9 +62,24 @@ open class View: UIView, ModelHandlerable, ViewProtocol, R public func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - cancellable = $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in - self?.onStateChange(viewModel: viewModel) - } + //setup shouldUpdate + $model.sink { [weak self] viewModel in + guard let self = self else { return } + let s = self.shouldUpdateView(viewModel: viewModel) + print("shouldUpdate - \(Self.self): \(s)") + self.shouldUpdate = s + }.store(in: &cancellables) + + //setup viewUpdate + $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in + guard let self = self else { return } + if self.shouldUpdate { + self.updateView(viewModel: viewModel) + self.shouldUpdate = false + print("didUpdate - \(Self.self)") + } + }.store(in: &cancellables) + setup() } } diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 92670fc4..afc6f0b2 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -18,7 +18,8 @@ open class LabelBase: UILabel, ModelHandlerable, Initable // MARK: - Combine Properties //-------------------------------------------------- @Published public var model: ModelType - private var cancellable: AnyCancellable? + private var cancellables = Set() + private var shouldUpdate: Bool = false //-------------------------------------------------- // MARK: - Properties @@ -94,9 +95,23 @@ open class LabelBase: UILabel, ModelHandlerable, Initable translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] accessibilityTraits = .staticText - cancellable = $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in - self?.onStateChange(viewModel: viewModel) - } + + //setup shouldUpdate + $model.sink { [weak self] viewModel in + guard let self = self else { return } + self.shouldUpdate = self.shouldUpdateView(viewModel: viewModel) + print("shouldUpdate - \(Self.self): \(self.shouldUpdate)") + }.store(in: &cancellables) + + //setup viewUpdate + $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in + guard let self = self else { return } + if self.shouldUpdate { + self.updateView(viewModel: viewModel) + self.shouldUpdate = false + print("didUpdate - \(Self.self)") + } + }.store(in: &cancellables) } public func reset() { @@ -120,7 +135,15 @@ open class LabelBase: UILabel, ModelHandlerable, Initable //-------------------------------------------------- /// Follow the SwiftUI View paradigm /// - Parameter viewModel: state - open func onStateChange(viewModel: ModelType) { + open func shouldUpdateView(viewModel: ModelType) -> Bool { + return viewModel.text != model.text + || viewModel.disabled != model.disabled + || viewModel.surface != model.surface + || viewModel.font != model.font + || viewModel.textPosition != model.textPosition + } + + open func updateView(viewModel: ModelType) { textAlignment = viewModel.textPosition.textAlignment textColor = textColorConfiguration.getColor(viewModel) @@ -146,7 +169,7 @@ open class LabelBase: UILabel, ModelHandlerable, Initable attribute.setAttribute(on: mutableText) //see if the attribute is Actionable - if let actionable = attribute as? LabelAttributeActionable{ + if let actionable = attribute as? any LabelAttributeActionable{ //create a accessibleAction let customAccessibilityAction = customAccessibilityAction(range: actionable.range) diff --git a/VDS/Components/SelectorBase/SelectorBase.swift b/VDS/Components/SelectorBase/SelectorBase.swift index 34678aed..b75d2791 100644 --- a/VDS/Components/SelectorBase/SelectorBase.swift +++ b/VDS/Components/SelectorBase/SelectorBase.swift @@ -300,7 +300,11 @@ open class SelectorBase: Control, Changable //-------------------------------------------------- /// Follow the SwiftUI View paradigm /// - Parameter viewModel: state - open override func onStateChange(viewModel: ModelType) { + open override func shouldUpdateView(viewModel: ModelType) -> Bool { + return true + } + + open override func updateView(viewModel: ModelType) { let enabled = !viewModel.disabled updateLabels(viewModel) diff --git a/VDS/Protocols/ModelHandlerable.swift b/VDS/Protocols/ModelHandlerable.swift index 3943ff61..49a79416 100644 --- a/VDS/Protocols/ModelHandlerable.swift +++ b/VDS/Protocols/ModelHandlerable.swift @@ -6,10 +6,13 @@ // import Foundation - + public protocol ModelHandlerable { associatedtype ModelType: Modelable var model: ModelType { get set } + init(with model: ModelType) func set(with model: ModelType) + func shouldUpdateView(viewModel: ModelType) -> Bool + func updateView(viewModel: ModelType) } From 9292ba8e5a5fe37a2e44166b680fee45987b2135 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Aug 2022 14:36:49 -0500 Subject: [PATCH 3/5] new func override Signed-off-by: Matt Bruce --- VDS/Components/Toggle/Toggle.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 22b60dcb..e60b3b93 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -384,7 +384,11 @@ open class ToggleBase: Control, Changable { //-------------------------------------------------- /// Follow the SwiftUI View paradigm /// - Parameter viewModel: state - open override func onStateChange(viewModel: ModelType) { + open override func shouldUpdateView(viewModel: ModelType) -> Bool { + return true + } + + open override func updateView(viewModel: ModelType) { label.set(with: viewModel.label) updateLabel(viewModel) updateToggle(viewModel) From 49b3868acc589faee6684b4020cc2259b6b21e07 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Aug 2022 16:25:37 -0500 Subject: [PATCH 4/5] update subscriber Signed-off-by: Matt Bruce --- VDS/Classes/Control.swift | 25 ++++++++----------------- VDS/Classes/View.swift | 26 ++++++++------------------ VDS/Components/Label/Label.swift | 26 +++++++++----------------- 3 files changed, 25 insertions(+), 52 deletions(-) diff --git a/VDS/Classes/Control.swift b/VDS/Classes/Control.swift index eecc62a6..bd07a505 100644 --- a/VDS/Classes/Control.swift +++ b/VDS/Classes/Control.swift @@ -13,8 +13,7 @@ import Combine open class Control: UIControl, ModelHandlerable, ViewProtocol, Resettable { @Published public var model: ModelType - private var cancellables = Set() - private var shouldUpdate: Bool = false + private var cancellable: AnyCancellable? open func set(with model: ModelType) { self.model = model @@ -63,23 +62,15 @@ open class Control: UIControl, ModelHandlerable, ViewProto public func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - //setup shouldUpdate - $model.sink { [weak self] viewModel in - guard let self = self else { return } - let s = self.shouldUpdateView(viewModel: viewModel) - print("shouldUpdate - \(Self.self): \(s)") - self.shouldUpdate = s - }.store(in: &cancellables) - //setup viewUpdate - $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in + cancellable = $model.filter { viewModel in + return self.shouldUpdateView(viewModel: viewModel) + + }.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in guard let self = self else { return } - if self.shouldUpdate { - self.updateView(viewModel: viewModel) - self.shouldUpdate = false - print("didUpdate - \(Self.self)") - } - }.store(in: &cancellables) + self.updateView(viewModel: viewModel) + print("didUpdate - \(Self.self)") + } setup() } } diff --git a/VDS/Classes/View.swift b/VDS/Classes/View.swift index 7e1812cb..8b72726a 100644 --- a/VDS/Classes/View.swift +++ b/VDS/Classes/View.swift @@ -13,8 +13,7 @@ import Combine open class View: UIView, ModelHandlerable, ViewProtocol, Resettable { @Published public var model: ModelType - private var cancellables = Set() - private var shouldUpdate: Bool = false + private var cancellable: AnyCancellable? open func set(with model: ModelType) { self.model = model @@ -62,24 +61,15 @@ open class View: UIView, ModelHandlerable, ViewProtocol, R public func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - //setup shouldUpdate - $model.sink { [weak self] viewModel in - guard let self = self else { return } - let s = self.shouldUpdateView(viewModel: viewModel) - print("shouldUpdate - \(Self.self): \(s)") - self.shouldUpdate = s - }.store(in: &cancellables) - //setup viewUpdate - $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in + cancellable = $model.filter { viewModel in + return self.shouldUpdateView(viewModel: viewModel) + + }.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in guard let self = self else { return } - if self.shouldUpdate { - self.updateView(viewModel: viewModel) - self.shouldUpdate = false - print("didUpdate - \(Self.self)") - } - }.store(in: &cancellables) - + self.updateView(viewModel: viewModel) + print("didUpdate - \(Self.self)") + } setup() } } diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index afc6f0b2..813fea1f 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -18,8 +18,7 @@ open class LabelBase: UILabel, ModelHandlerable, Initable // MARK: - Combine Properties //-------------------------------------------------- @Published public var model: ModelType - private var cancellables = Set() - private var shouldUpdate: Bool = false + private var cancellable: AnyCancellable? //-------------------------------------------------- // MARK: - Properties @@ -95,23 +94,16 @@ open class LabelBase: UILabel, ModelHandlerable, Initable translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] accessibilityTraits = .staticText - - //setup shouldUpdate - $model.sink { [weak self] viewModel in - guard let self = self else { return } - self.shouldUpdate = self.shouldUpdateView(viewModel: viewModel) - print("shouldUpdate - \(Self.self): \(self.shouldUpdate)") - }.store(in: &cancellables) - + //setup viewUpdate - $model.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in + cancellable = $model.filter { viewModel in + return self.shouldUpdateView(viewModel: viewModel) + + }.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in guard let self = self else { return } - if self.shouldUpdate { - self.updateView(viewModel: viewModel) - self.shouldUpdate = false - print("didUpdate - \(Self.self)") - } - }.store(in: &cancellables) + self.updateView(viewModel: viewModel) + print("didUpdate - \(Self.self)") + } } public func reset() { From c70997edb9a3e82ae25330b05382a73eda11dae9 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 9 Aug 2022 16:27:03 -0500 Subject: [PATCH 5/5] removed prints Signed-off-by: Matt Bruce --- VDS/Classes/Control.swift | 4 ++-- VDS/Classes/View.swift | 2 +- VDS/Components/Label/Label.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VDS/Classes/Control.swift b/VDS/Classes/Control.swift index bd07a505..ae0bba87 100644 --- a/VDS/Classes/Control.swift +++ b/VDS/Classes/Control.swift @@ -69,8 +69,8 @@ open class Control: UIControl, ModelHandlerable, ViewProto }.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in guard let self = self else { return } self.updateView(viewModel: viewModel) - print("didUpdate - \(Self.self)") - } + + } setup() } } diff --git a/VDS/Classes/View.swift b/VDS/Classes/View.swift index 8b72726a..c15f29b6 100644 --- a/VDS/Classes/View.swift +++ b/VDS/Classes/View.swift @@ -68,7 +68,7 @@ open class View: UIView, ModelHandlerable, ViewProtocol, R }.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in guard let self = self else { return } self.updateView(viewModel: viewModel) - print("didUpdate - \(Self.self)") + } setup() } diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 813fea1f..5860473e 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -102,7 +102,7 @@ open class LabelBase: UILabel, ModelHandlerable, Initable }.debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main).sink { [weak self] viewModel in guard let self = self else { return } self.updateView(viewModel: viewModel) - print("didUpdate - \(Self.self)") + } }