Merge branch 'develop' into mbruce/inputStepper
This commit is contained in:
commit
06b92f48c3
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user