Compare commits

...

14 Commits

Author SHA1 Message Date
Matt Bruce
c0090deac9 Merge branch 'develop' into feature/subject 2023-07-27 11:05:11 -05:00
Matt Bruce
4fd167cfa3 added paused update
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-19 16:03:06 -05:00
Matt Bruce
df1e8ee2df fixed issue with updateStrategy
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-19 15:38:35 -05:00
Matt Bruce
ce54965edc added wait
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-19 15:22:12 -05:00
Matt Bruce
0be0d71f7b removed un-needed
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-19 15:22:06 -05:00
Matt Bruce
c2998ee0bf reverted back to develop tab
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-19 15:21:56 -05:00
Matt Bruce
ac1e6f692d Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git into feature/subject
# Conflicts:
#	VDS/Classes/Control.swift
#	VDS/Components/Toggle/Toggle.swift

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-19 14:32:05 -05:00
Matt Bruce
c7e47707c2 updated to make the label and such always update
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-14 14:21:46 -05:00
Matt Bruce
3e8c6a12e4 removed intermediate
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-14 14:18:00 -05:00
Matt Bruce
d159075f83 added new updateStrategy
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-14 11:53:57 -05:00
Matt Bruce
a8e1cf9b48 updated tab
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-14 11:53:07 -05:00
Matt Bruce
76c3205921 refmoved shouldUpdateView bools
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-14 09:10:22 -05:00
Matt Bruce
abc7f409e7 updated to have 2 subjects for the strategies
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-14 09:10:05 -05:00
Matt Bruce
1934da2543 added back subject into the flow
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-07-14 08:17:38 -05:00
23 changed files with 102 additions and 75 deletions

View File

@ -16,6 +16,8 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
public var subject = PassthroughSubject<Void, Never>()
public var updateStrategy: HandlerableUpdateStrategy = .immediate
/// Set of Subscribers for any Publishers for this Control /// Set of Subscribers for any Publishers for this Control
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
@ -33,10 +35,7 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
private var initialSetupPerformed = false 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 /// Dictionary for keeping information for this Control use only Primitives
open var userInfo = [String: Primitive]() open var userInfo = [String: Primitive]()
@ -109,7 +108,8 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
if !initialSetupPerformed { if !initialSetupPerformed {
initialSetupPerformed = true initialSetupPerformed = true
setup() setup()
setNeedsUpdate() setupNeedsUpdateEvent()
updateView()
} }
} }
@ -125,6 +125,14 @@ open class Control: UIControl, Handlerable, ViewProtocol, Resettable, UserInfoab
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open override func didMoveToWindow() {
super.didMoveToWindow()
// Keep the strategy if it's 'alwaysImmediate'
guard updateStrategy != .alwaysImmediate else { return }
// Update the strategy based on whether the view is in a window
updateStrategy = window != nil ? .delayed : .immediate
}
/// Update this view based off of property changes /// Update this view based off of property changes
open func updateView() { open func updateView() {

View File

@ -229,7 +229,6 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
label.reset() label.reset()
childLabel.reset() childLabel.reset()
errorLabel.reset() errorLabel.reset()
@ -250,7 +249,6 @@ open class SelectorItemBase<Selector: SelectorControlable>: Control, Errorable,
value = nil value = nil
isSelected = false isSelected = false
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -17,16 +17,16 @@ open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
public var subject = PassthroughSubject<Void, Never>()
public var updateStrategy: HandlerableUpdateStrategy = .immediate
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
private var initialSetupPerformed = false 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 /// Dictionary for keeping information for this Control use only Primitives
open var userInfo = [String: Primitive]() open var userInfo = [String: Primitive]()
@ -72,13 +72,22 @@ open class View: UIView, Handlerable, ViewProtocol, Resettable, UserInfoable {
if !initialSetupPerformed { if !initialSetupPerformed {
initialSetupPerformed = true initialSetupPerformed = true
setup() setup()
setNeedsUpdate() setupNeedsUpdateEvent()
updateView()
} }
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open override func didMoveToWindow() {
super.didMoveToWindow()
// Keep the strategy if it's 'alwaysImmediate'
guard updateStrategy != .alwaysImmediate else { return }
// Update the strategy based on whether the view is in a window
updateStrategy = window != nil ? .delayed : .immediate
}
/// Update this view based off of property changes /// Update this view based off of property changes
open func updateView() { open func updateView() {

View File

@ -78,7 +78,6 @@ open class Badge: View {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
label.reset() label.reset()
label.lineBreakMode = .byTruncatingTail label.lineBreakMode = .byTruncatingTail
label.textPosition = .left label.textPosition = .left
@ -87,7 +86,6 @@ open class Badge: View {
text = "" text = ""
maxWidth = nil maxWidth = nil
numberOfLines = 1 numberOfLines = 1
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -281,13 +281,11 @@ open class BadgeIndicator: View {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
label.reset() label.reset()
label.lineBreakMode = .byTruncatingTail label.lineBreakMode = .byTruncatingTail
label.textPosition = .center label.textPosition = .center
fillColor = .red fillColor = .red
number = nil number = nil
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -133,11 +133,9 @@ open class Button: ButtonBase, Useable {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
use = .primary use = .primary
width = nil width = nil
size = .large size = .large
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -28,6 +28,8 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
public var subject = PassthroughSubject<Void, Never>()
public var updateStrategy: HandlerableUpdateStrategy = .immediate
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
public var onClickSubscriber: AnyCancellable? { public var onClickSubscriber: AnyCancellable? {
willSet { willSet {
@ -44,9 +46,7 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
open var shouldUpdateView: Bool = true
open var availableSizes: [ButtonSize] { [] } open var availableSizes: [ButtonSize] { [] }
open var text: String? { didSet { setNeedsUpdate() } } open var text: String? { didSet { setNeedsUpdate() } }
@ -123,7 +123,8 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
accessibilityCustomActions = [] accessibilityCustomActions = []
setup() setup()
setNeedsUpdate() setupNeedsUpdateEvent()
updateView()
} }
} }
@ -137,18 +138,25 @@ open class ButtonBase: UIButton, Buttonable, Handlerable, ViewProtocol, Resettab
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open func reset() { open func reset() {
shouldUpdateView = false
surface = .light surface = .light
disabled = false disabled = false
text = nil text = nil
accessibilityCustomActions = [] accessibilityCustomActions = []
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open override func didMoveToWindow() {
super.didMoveToWindow()
// Keep the strategy if it's 'alwaysImmediate'
guard updateStrategy != .alwaysImmediate else { return }
// Update the strategy based on whether the view is in a window
updateStrategy = window != nil ? .delayed : .immediate
}
override open var intrinsicContentSize: CGSize { override open var intrinsicContentSize: CGSize {
let intrinsicContentSize = super.intrinsicContentSize let intrinsicContentSize = super.intrinsicContentSize
let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right

View File

@ -90,13 +90,11 @@ open class TextLink: ButtonBase {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
text = nil text = nil
size = .large size = .large
accessibilityCustomActions = [] accessibilityCustomActions = []
isAccessibilityElement = true isAccessibilityElement = true
accessibilityTraits = .link accessibilityTraits = .link
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -256,7 +256,6 @@ open class ButtonIcon: Control {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
kind = .ghost kind = .ghost
surfaceType = .colorFill surfaceType = .colorFill
size = .large size = .large
@ -264,7 +263,6 @@ open class ButtonIcon: Control {
hideBorder = true hideBorder = true
iconOffset = .init(x: 0, y: 0) iconOffset = .init(x: 0, y: 0)
iconName = nil iconName = nil
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -16,15 +16,15 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Combine Properties // MARK: - Combine Properties
//-------------------------------------------------- //--------------------------------------------------
public var subject = PassthroughSubject<Void, Never>()
public var subscribers = Set<AnyCancellable>() public var subscribers = Set<AnyCancellable>()
public var updateStrategy: HandlerableUpdateStrategy = .immediate
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
private var initialSetupPerformed = false private var initialSetupPerformed = false
open var shouldUpdateView: Bool = true
open var useAttributedText: Bool = false open var useAttributedText: Bool = false
open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }} open var useScaledFont: Bool = false { didSet { setNeedsUpdate() }}
@ -105,7 +105,8 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
accessibilityCustomActions = [] accessibilityCustomActions = []
accessibilityTraits = .staticText accessibilityTraits = .staticText
setup() setup()
setNeedsUpdate() setupNeedsUpdateEvent()
updateView()
} }
} }
@ -113,7 +114,6 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open func reset() { open func reset() {
shouldUpdateView = false
surface = .light surface = .light
disabled = false disabled = false
attributes = nil attributes = nil
@ -123,7 +123,6 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
attributedText = nil attributedText = nil
numberOfLines = 0 numberOfLines = 0
backgroundColor = .clear backgroundColor = .clear
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }
@ -136,7 +135,16 @@ open class Label: UILabel, Handlerable, ViewProtocol, Resettable, UserInfoable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Overrides // MARK: - Overrides
//-------------------------------------------------- //--------------------------------------------------
open override func didMoveToWindow() {
super.didMoveToWindow()
// Keep the strategy if it's 'alwaysImmediate'
guard updateStrategy != .alwaysImmediate else { return }
// Update the strategy based on whether the view is in a window
updateStrategy = window != nil ? .delayed : .immediate
}
open func updateView() { open func updateView() {
if !useAttributedText { if !useAttributedText {
if let text = text { if let text = text {

View File

@ -231,9 +231,7 @@ open class Notification: View {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
titleLabel.reset() titleLabel.reset()
titleLabel.text = "" titleLabel.text = ""
titleLabel.textStyle = UIDevice.isIPad ? .boldBodyLarge : .boldBodySmall titleLabel.textStyle = UIDevice.isIPad ? .boldBodyLarge : .boldBodySmall
@ -259,7 +257,6 @@ open class Notification: View {
hideCloseButton = false hideCloseButton = false
fullBleed = false fullBleed = false
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -203,7 +203,6 @@ open class RadioBoxItem: Control, Changeable {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
textLabel.reset() textLabel.reset()
subTextLabel.reset() subTextLabel.reset()
subTextRightLabel.reset() subTextRightLabel.reset()
@ -227,7 +226,6 @@ open class RadioBoxItem: Control, Changeable {
isSelected = false isSelected = false
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -92,7 +92,6 @@ open class RadioSwatch: Control {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
fillImage = nil fillImage = nil
text = "" text = ""
primaryColor = nil primaryColor = nil
@ -100,7 +99,6 @@ open class RadioSwatch: Control {
strikethrough = false strikethrough = false
inputId = nil inputId = nil
value = nil value = nil
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
setNeedsDisplay() setNeedsDisplay()
} }

View File

@ -123,6 +123,9 @@ extension Tabs {
//-------------------------------------------------- //--------------------------------------------------
open override func setup() { open override func setup() {
super.setup() super.setup()
updateStrategy = .alwaysImmediate
label.updateStrategy = .alwaysImmediate
addSubview(label) addSubview(label)
accessibilityTraits = .button accessibilityTraits = .button
@ -137,19 +140,7 @@ extension Tabs {
labelLeadingConstraint = label.leadingAnchor.constraint(equalTo: leadingAnchor) labelLeadingConstraint = label.leadingAnchor.constraint(equalTo: leadingAnchor)
labelLeadingConstraint?.isActive = true labelLeadingConstraint?.isActive = true
let layoutGuide = UILayoutGuide()
addLayoutGuide(layoutGuide)
labelWidthConstraint = layoutGuide.widthAnchor.constraint(greaterThanOrEqualToConstant: minWidth)
labelWidthConstraint?.isActive = true
//activate the constraints
NSLayoutConstraint.activate([layoutGuide.topAnchor.constraint(equalTo: topAnchor),
layoutGuide.bottomAnchor.constraint(equalTo: bottomAnchor),
layoutGuide.leadingAnchor.constraint(equalTo: leadingAnchor),
layoutGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
publisher(for: UITapGestureRecognizer()) publisher(for: UITapGestureRecognizer())
.sink { [weak self] _ in .sink { [weak self] _ in
guard let self else { return } guard let self else { return }

View File

@ -164,6 +164,7 @@ open class Tabs: View {
//-------------------------------------------------- //--------------------------------------------------
open override func setup() { open override func setup() {
super.setup() super.setup()
updateStrategy = .alwaysImmediate
scrollView = UIScrollView() scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.showsHorizontalScrollIndicator = false scrollView.showsHorizontalScrollIndicator = false

View File

@ -192,7 +192,6 @@ open class TileContainer: Control {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
color = .white color = .white
padding = .padding4X padding = .padding4X
aspectRatio = .ratio1x1 aspectRatio = .ratio1x1
@ -201,7 +200,6 @@ open class TileContainer: Control {
height = nil height = nil
showBorder = false showBorder = false
showDropShadows = false showDropShadows = false
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -242,7 +242,6 @@ open class Tilelet: TileContainer {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
shouldUpdateView = false
aspectRatio = .none aspectRatio = .none
color = .black color = .black
//models //models
@ -251,7 +250,6 @@ open class Tilelet: TileContainer {
subTitleModel = nil subTitleModel = nil
descriptiveIconModel = nil descriptiveIconModel = nil
directionalIconModel = nil directionalIconModel = nil
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -199,12 +199,10 @@ open class TitleLockup: View {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
textPosition = .left textPosition = .left
eyebrowModel = nil eyebrowModel = nil
titleModel = nil titleModel = nil
subTitleModel = nil subTitleModel = nil
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -175,7 +175,6 @@ open class Toggle: Control, Changeable {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
label.reset() label.reset()
isOn = false isOn = false
isAnimated = true isAnimated = true
@ -187,8 +186,6 @@ open class Toggle: Control, Changeable {
textPosition = .left textPosition = .left
inputId = nil inputId = nil
value = nil value = nil
shouldUpdateView = true
setNeedsUpdate()
} }
/// This will toggle the state of the Toggle /// This will toggle the state of the Toggle

View File

@ -139,15 +139,12 @@ open class ToggleView: Control, Changeable {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
isOn = false isOn = false
isAnimated = true isAnimated = true
inputId = nil inputId = nil
value = nil value = nil
toggleView.backgroundColor = toggleColorConfiguration.getColor(self) toggleView.backgroundColor = toggleColorConfiguration.getColor(self)
knobView.backgroundColor = knobColorConfiguration.getColor(self) knobView.backgroundColor = knobColorConfiguration.getColor(self)
shouldUpdateView = true
setNeedsUpdate()
} }
/// This will toggle the state of the Toggle and execute the actionBlock if provided. /// This will toggle the state of the Toggle and execute the actionBlock if provided.

View File

@ -142,14 +142,12 @@ open class Tooltip: Control, TooltipLaunchable {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
size = .medium size = .medium
title = "" title = ""
content = "" content = ""
fillColor = .primary fillColor = .primary
closeButtonText = "Close" closeButtonText = "Close"
imageView.image = nil imageView.image = nil
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -80,7 +80,6 @@ open class TrailingTooltipLabel: View, TooltipLaunchable {
/// Resets back to this objects default settings. /// Resets back to this objects default settings.
open override func reset() { open override func reset() {
super.reset() super.reset()
shouldUpdateView = false
labelText = nil labelText = nil
labelAttributes = nil labelAttributes = nil
labelTextStyle = .defaultStyle labelTextStyle = .defaultStyle
@ -88,7 +87,6 @@ open class TrailingTooltipLabel: View, TooltipLaunchable {
tooltipCloseButtonText = "Close" tooltipCloseButtonText = "Close"
tooltipTitle = "" tooltipTitle = ""
tooltipContent = "" tooltipContent = ""
shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }
} }

View File

@ -9,18 +9,43 @@ import Foundation
import Combine import Combine
import UIKit import UIKit
public enum HandlerableUpdateStrategy {
case immediate
case delayed
case alwaysImmediate
case paused
}
public protocol Handlerable: AnyObject, Initable, Disabling, Surfaceable { public protocol Handlerable: AnyObject, Initable, Disabling, Surfaceable {
var subject: PassthroughSubject<Void, Never> { get set }
var subscribers: Set<AnyCancellable> { get set } var subscribers: Set<AnyCancellable> { get set }
var shouldUpdateView: Bool { get set } var updateStrategy: HandlerableUpdateStrategy { get set }
func updateView() func updateView()
} }
extension Handlerable { extension Handlerable {
public func setupNeedsUpdateEvent() {
subject
.debounce(for: .milliseconds(50), scheduler: RunLoop.main)
.sink { [weak self] _ in
self?.updateView()
}.store(in: &subscribers)
}
public func setNeedsUpdate() { public func setNeedsUpdate() {
if shouldUpdateView { switch updateStrategy {
shouldUpdateView = false case .delayed:
subject.send()
case .immediate, .alwaysImmediate:
updateView() updateView()
shouldUpdateView = true case .paused:
break
}
}
public func updateIfNeeded<T: Equatable>(_ oldValue: T, _ newValue: T) {
if oldValue != newValue {
setNeedsUpdate()
} }
} }
} }
@ -33,3 +58,15 @@ extension Handlerable where Self: UIControl {
}).store(in: &subscribers) }).store(in: &subscribers)
} }
} }
extension Handlerable {
@discardableResult func pausedUpdate(_ closure: (_ instance: inout Self) -> Void) -> Self {
var copy = self
let oldStrategy = copy.updateStrategy
copy.updateStrategy = .paused
closure(&copy)
copy.updateStrategy = oldStrategy
copy.updateView()
return copy
}
}