Merge branch 'develop' into mbruce/inputStepper

This commit is contained in:
Matt Bruce 2024-08-08 11:35:00 -05:00
commit 06b92f48c3
7 changed files with 109 additions and 32 deletions

View File

@ -66,18 +66,21 @@ open class SelectorItemBase<Selector: SelectorBase>: Control, Errorable, Changea
/// Label used to render labelText. /// Label used to render labelText.
open var label = Label().with { open var label = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
$0.textStyle = .boldBodyLarge $0.textStyle = .boldBodyLarge
} }
/// Label used to render childText. /// Label used to render childText.
open var childLabel = Label().with { open var childLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
$0.textStyle = .bodyLarge $0.textStyle = .bodyLarge
} }
/// Label used to render errorText. /// Label used to render errorText.
open var errorLabel = Label().with { open var errorLabel = Label().with {
$0.setContentCompressionResistancePriority(.required, for: .vertical) $0.setContentCompressionResistancePriority(.required, for: .vertical)
$0.setContentCompressionResistancePriority(.required, for: .horizontal)
$0.textStyle = .bodyMedium $0.textStyle = .bodyMedium
} }

View File

@ -12,7 +12,7 @@ import Combine
/// Base Class used to build Views. /// Base Class used to build Views.
@objcMembers @objcMembers
@objc(VDSView) @objc(VDSView)
open class View: UIView, ViewProtocol, UserInfoable { open class View: UIView, ViewProtocol, UserInfoable, Clickable {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
@ -37,6 +37,7 @@ open class View: UIView, ViewProtocol, UserInfoable {
//-------------------------------------------------- //--------------------------------------------------
open var subscribers = Set<AnyCancellable>() open var subscribers = Set<AnyCancellable>()
open var onClickSubscriber: AnyCancellable?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------

View File

@ -154,15 +154,12 @@ open class DatePicker: EntryFieldBase<String> {
selectedDateLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable() selectedDateLabel.textColorConfiguration = primaryColorConfiguration.eraseToAnyColorable()
// tap gesture // tap gesture
containerView containerView.onClick = { [weak self] _ in
.publisher(for: UITapGestureRecognizer()) guard let self else { return }
.sink { [weak self] _ in if isEnabled && !isReadOnly {
guard let self else { return } showPopover()
if isEnabled && !isReadOnly {
showPopover()
}
} }
.store(in: &subscribers) }
NotificationCenter.default NotificationCenter.default
.publisher(for: UIDevice.orientationDidChangeNotification).sink { [weak self] _ in .publisher(for: UIDevice.orientationDidChangeNotification).sink { [weak self] _ in

View File

@ -153,12 +153,9 @@ open class DropdownSelect: EntryFieldBase<String> {
}() }()
// tap gesture // tap gesture
containerView containerView.onClick = { [weak self] _ in
.publisher(for: UITapGestureRecognizer()) self?.launchPicker()
.sink { [weak self] _ in }
self?.launchPicker()
}
.store(in: &subscribers)
containerView.height(44) containerView.height(44)
} }

View File

@ -44,7 +44,7 @@ open class TileContainer: TileContainerBase<TileContainer.Padding> {
} }
} }
open class TileContainerBase<PaddingType: DefaultValuing>: Control where PaddingType.ValueType == CGFloat { open class TileContainerBase<PaddingType: DefaultValuing>: View where PaddingType.ValueType == CGFloat {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
@ -118,6 +118,8 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
$0.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) $0.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
} }
private var isHighlighted: Bool = false { didSet { setNeedsUpdate() } }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
@ -337,6 +339,27 @@ open class TileContainerBase<PaddingType: DefaultValuing>: Control where Padding
set {} set {}
} }
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if let onClickSubscriber {
isHighlighted = true
}
}
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if let onClickSubscriber {
isHighlighted = false
}
}
open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
if let onClickSubscriber {
isHighlighted = false
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Methods // MARK: - Public Methods
//-------------------------------------------------- //--------------------------------------------------

View File

@ -55,6 +55,7 @@ open class Toggle: Control, Changeable, FormFieldable {
private var leftConstraints: [NSLayoutConstraint] = [] private var leftConstraints: [NSLayoutConstraint] = []
private var rightConstraints: [NSLayoutConstraint] = [] private var rightConstraints: [NSLayoutConstraint] = []
private var labelConstraints: [NSLayoutConstraint] = [] private var labelConstraints: [NSLayoutConstraint] = []
private var toggleConstraints: [NSLayoutConstraint] = []
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Configuration // MARK: - Configuration
@ -95,7 +96,7 @@ open class Toggle: Control, Changeable, FormFieldable {
open var toggleView = ToggleView().with { open var toggleView = ToggleView().with {
$0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
$0.isUserInteractionEnabled = false $0.isUserInteractionEnabled = false
$0.isAccessibilityElement = false $0.isAccessibilityElement = false
} }
/// Used in showing the on/off text. /// Used in showing the on/off text.
@ -148,18 +149,6 @@ open class Toggle: Control, Changeable, FormFieldable {
open var value: AnyHashable? { isOn } open var value: AnyHashable? { isOn }
/// The natural size for the receiving view, considering only properties of the view itself.
open override var intrinsicContentSize: CGSize {
if showLabel {
label.sizeToFit()
let size = CGSize(width: label.frame.width + spacingBetween + toggleContainerSize.width,
height: max(toggleContainerSize.height, label.frame.height))
return size
} else {
return toggleContainerSize
}
}
open override var shouldHighlight: Bool { false } open override var shouldHighlight: Bool { false }
//-------------------------------------------------- //--------------------------------------------------
@ -208,6 +197,49 @@ open class Toggle: Control, Changeable, FormFieldable {
label.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor) label.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
] ]
// Set content hugging priority
setContentHuggingPriority(.required, for: .horizontal)
isAccessibilityElement = true
if #available(iOS 17.0, *) {
accessibilityTraits = .toggleButton
} else {
accessibilityTraits = .button
}
addSubview(label)
addSubview(toggleView)
// Set up initial constraints for label and switch
toggleView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
//toggle
toggleConstraints = [
toggleView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
toggleView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor)
]
//toggle and label variants
labelConstraints = [
height(constant: toggleContainerSize.height, priority: .defaultLow),
heightGreaterThanEqualTo(constant: toggleContainerSize.height, priority: .defaultHigh),
label.topAnchor.constraint(equalTo: topAnchor),
label.bottomAnchor.constraint(equalTo: bottomAnchor),
]
//label-toggle
leftConstraints = [
label.leadingAnchor.constraint(equalTo: leadingAnchor),
toggleView.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: spacingBetween),
toggleView.trailingAnchor.constraint(equalTo: trailingAnchor)
]
//toggle-label
rightConstraints = [
toggleView.leadingAnchor.constraint(equalTo: leadingAnchor),
label.leadingAnchor.constraint(equalTo: toggleView.trailingAnchor, constant: spacingBetween),
label.trailingAnchor.constraint(equalTo: trailingAnchor)
]
bridge_accessibilityValueBlock = { [weak self] in bridge_accessibilityValueBlock = { [weak self] in
guard let self else { return "" } guard let self else { return "" }
if showText { if showText {
@ -261,6 +293,8 @@ open class Toggle: Control, Changeable, FormFieldable {
label.isHidden = !showLabel label.isHidden = !showLabel
if showLabel { if showLabel {
NSLayoutConstraint.deactivate(toggleConstraints)
label.textAlignment = textPosition == .left ? .right : .left label.textAlignment = textPosition == .left ? .right : .left
label.textStyle = textStyle label.textStyle = textStyle
label.text = statusText label.text = statusText
@ -279,6 +313,7 @@ open class Toggle: Control, Changeable, FormFieldable {
NSLayoutConstraint.deactivate(leftConstraints) NSLayoutConstraint.deactivate(leftConstraints)
NSLayoutConstraint.deactivate(rightConstraints) NSLayoutConstraint.deactivate(rightConstraints)
NSLayoutConstraint.deactivate(labelConstraints) NSLayoutConstraint.deactivate(labelConstraints)
NSLayoutConstraint.activate(toggleConstraints)
} }
} }
} }

View File

@ -9,12 +9,12 @@ import Foundation
import UIKit import UIKit
import Combine import Combine
public protocol Clickable: ViewProtocol where Self: UIControl { public protocol Clickable: ViewProtocol {
/// Sets the primary Subscriber used for the UIControl event .touchUpInside. /// Sets the primary Subscriber used for the UIControl event .touchUpInside.
var onClickSubscriber: AnyCancellable? { get set } var onClickSubscriber: AnyCancellable? { get set }
} }
extension Clickable { extension Clickable where Self: UIControl {
/// Allows the setting of a completion block against the onClickSubscriber cancellable. This will /// Allows the setting of a completion block against the onClickSubscriber cancellable. This will
/// completion block will get executed against the UIControl publisher for the 'touchUpInside' action. /// completion block will get executed against the UIControl publisher for the 'touchUpInside' action.
public var onClick: ((Self) -> ())? { public var onClick: ((Self) -> ())? {
@ -23,7 +23,7 @@ extension Clickable {
onClickSubscriber?.cancel() onClickSubscriber?.cancel()
if let newValue { if let newValue {
onClickSubscriber = publisher(for: .touchUpInside) onClickSubscriber = publisher(for: .touchUpInside)
.sink { [weak self] c in .sink { [weak self] c in
guard let self, self.isEnabled else { return } guard let self, self.isEnabled else { return }
newValue(c) newValue(c)
} }
@ -34,3 +34,24 @@ extension Clickable {
} }
} }
} }
extension Clickable where Self: UIView {
/// Allows the setting of a completion block against the onClickSubscriber cancellable. This will
/// completion block will get executed against the UIControl publisher for the 'touchUpInside' action.
public var onClick: ((Self) -> ())? {
get { return nil }
set {
onClickSubscriber?.cancel()
if let newValue {
onClickSubscriber = publisher(for: UITapGestureRecognizer())
.sink { [weak self] _ in
guard let self, self.isEnabled else { return }
newValue(self)
}
} else {
onClickSubscriber = nil
}
setNeedsUpdate()
}
}
}