From c975f2b06ef63550a5801587d8a5bbe38736f87c Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 31 Aug 2022 13:29:44 -0500 Subject: [PATCH] added Gesture publisher / extension to UIView Signed-off-by: Matt Bruce --- VDS.xcodeproj/project.pbxproj | 4 ++ VDS/Components/Checkbox/Checkbox.swift | 10 ++-- VDS/Components/RadioBox/RadioBox.swift | 10 ++-- VDS/Components/RadioButton/RadioButton.swift | 12 ++-- VDS/Components/RadioSwatch/RadioSwatch.swift | 12 ++-- VDS/Components/Toggle/Toggle.swift | 10 ++-- .../UIGestureRecognizer+Publisher.swift | 60 +++++++++++++++++++ 7 files changed, 91 insertions(+), 27 deletions(-) create mode 100644 VDS/Publishers/UIGestureRecognizer+Publisher.swift diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 29d74e81..bf869a43 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */; }; EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */; }; EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */; }; + EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* Checkbox.swift */; }; EAF7F0962899861000B287F5 /* CheckboxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0942899861000B287F5 /* CheckboxModel.swift */; }; EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; }; @@ -161,6 +162,7 @@ EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typography.swift; sourceTree = ""; }; EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Publisher+Bind.swift"; sourceTree = ""; }; EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControlPublisher.swift; sourceTree = ""; }; + EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; EAF7F0932899861000B287F5 /* Checkbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; EAF7F0942899861000B287F5 /* CheckboxModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxModel.swift; sourceTree = ""; }; EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = ""; }; @@ -436,6 +438,7 @@ EA89200128AECF2A006B9984 /* UIButton+Publisher.swift */, EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */, EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */, + EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */, ); path = Publishers; sourceTree = ""; @@ -621,6 +624,7 @@ EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */, EAF7F13328A2A16500B287F5 /* LabelAttributeAttachment.swift in Sources */, EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */, + EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */, EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */, EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */, EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */, diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index 2733765b..5179c98c 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -127,7 +127,11 @@ open class CheckboxBase: Control, Changable open override func setup() { super.setup() - addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(Self.tap))) + + //add tapGesture to self + publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.sendActions(for: .touchUpInside) + }.store(in: &subscribers) isAccessibilityElement = true accessibilityTraits = .button @@ -204,10 +208,6 @@ open class CheckboxBase: Control, Changable setAccessibilityLabel() onChange = nil } - - @objc func tap() { - sendActions(for: .touchUpInside) - } /// This will checkbox the state of the Selector and execute the actionBlock if provided. open override func defaultAction() { diff --git a/VDS/Components/RadioBox/RadioBox.swift b/VDS/Components/RadioBox/RadioBox.swift index 99302e51..7354f295 100644 --- a/VDS/Components/RadioBox/RadioBox.swift +++ b/VDS/Components/RadioBox/RadioBox.swift @@ -128,7 +128,11 @@ open class RadioBoxBase: Control, Changable open override func setup() { super.setup() - addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(Self.tap))) + + //add tapGesture to self + publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.sendActions(for: .touchUpInside) + }.store(in: &subscribers) isAccessibilityElement = true accessibilityTraits = .button @@ -203,10 +207,6 @@ open class RadioBoxBase: Control, Changable setAccessibilityLabel() onChange = nil } - - @objc func tap() { - sendActions(for: .touchUpInside) - } /// This will radioBox the state of the Selector and execute the actionBlock if provided. open override func defaultAction() { diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index aff7525f..0b9de50b 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -126,7 +126,11 @@ open class RadioButtonBase: Control, Cha open override func setup() { super.setup() - addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(Self.tap))) + + //add tapGesture to self + publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.sendActions(for: .touchUpInside) + }.store(in: &subscribers) isAccessibilityElement = true accessibilityTraits = .button @@ -203,11 +207,7 @@ open class RadioButtonBase: Control, Cha setAccessibilityLabel() onChange = nil } - - @objc func tap() { - sendActions(for: .touchUpInside) - } - + /// This will checkbox the state of the Selector and execute the actionBlock if provided. open override func defaultAction() { guard !isSelected else { return } diff --git a/VDS/Components/RadioSwatch/RadioSwatch.swift b/VDS/Components/RadioSwatch/RadioSwatch.swift index 1f433afb..34f65eee 100644 --- a/VDS/Components/RadioSwatch/RadioSwatch.swift +++ b/VDS/Components/RadioSwatch/RadioSwatch.swift @@ -88,7 +88,11 @@ open class RadioSwatchBase: Control, Cha open override func setup() { super.setup() - addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(Self.tap))) + + //add tapGesture to self + publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.sendActions(for: .touchUpInside) + }.store(in: &subscribers) isAccessibilityElement = true accessibilityTraits = .button @@ -121,11 +125,7 @@ open class RadioSwatchBase: Control, Cha setAccessibilityLabel() onChange = nil } - - @objc func tap() { - sendActions(for: .touchUpInside) - } - + open override func defaultAction() { isSelected.toggle() sendActions(for: .valueChanged) diff --git a/VDS/Components/Toggle/Toggle.swift b/VDS/Components/Toggle/Toggle.swift index 6f7efeab..62e01de6 100644 --- a/VDS/Components/Toggle/Toggle.swift +++ b/VDS/Components/Toggle/Toggle.swift @@ -254,7 +254,11 @@ open class ToggleBase: Control, Changable { //-------------------------------------------------- open override func setup() { super.setup() - addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(Self.tap))) + + //add tapGesture to self + publisher(for: UITapGestureRecognizer()).sink { [weak self] _ in + self?.sendActions(for: .touchUpInside) + }.store(in: &subscribers) isAccessibilityElement = true accessibilityTraits = .button @@ -311,10 +315,6 @@ open class ToggleBase: Control, Changable { onChange = nil } - @objc func tap() { - sendActions(for: .touchUpInside) - } - /// This will toggle the state of the Toggle and execute the actionBlock if provided. open override func defaultAction() { isOn.toggle() diff --git a/VDS/Publishers/UIGestureRecognizer+Publisher.swift b/VDS/Publishers/UIGestureRecognizer+Publisher.swift new file mode 100644 index 00000000..ae9c7108 --- /dev/null +++ b/VDS/Publishers/UIGestureRecognizer+Publisher.swift @@ -0,0 +1,60 @@ +// +// UIGestureRecognizer.swift +// VDS +// +// Created by Matt Bruce on 8/31/22. +// + +import Foundation +import UIKit +import Combine + +extension UIView { + + public func publisher(for gestureRecognizer: G) -> UIGestureRecognizer.Publisher where G: UIGestureRecognizer { + UIGestureRecognizer.Publisher(gestureRecognizer: gestureRecognizer, view: self) + } +} + +extension UIGestureRecognizer { + + public struct Publisher: Combine.Publisher where G: UIGestureRecognizer { + + public typealias Output = G + public typealias Failure = Never + + public let gestureRecognizer: G + public let view: UIView + + public func receive(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input { + subscriber.receive( + subscription: Subscription(subscriber: subscriber, gestureRecognizer: gestureRecognizer, on: view) + ) + } + } + + public class Subscription: Combine.Subscription where S.Input == G, S.Failure == Never { + + public var subscriber: S? + public let gestureRecognizer: G + public let view: UIView + + public init(subscriber: S, gestureRecognizer: G, on view: UIView) { + self.subscriber = subscriber + self.gestureRecognizer = gestureRecognizer + self.view = view + gestureRecognizer.addTarget(self, action: #selector(handle)) + view.addGestureRecognizer(gestureRecognizer) + } + + @objc private func handle(_ gesture: UIGestureRecognizer) { + _ = subscriber?.receive(gestureRecognizer) + } + + public func cancel() { + view.removeGestureRecognizer(gestureRecognizer) + } + + public func request(_ demand: Subscribers.Demand) { } + } +}