diff --git a/VDS/BaseClasses/Control.swift b/VDS/BaseClasses/Control.swift index 2a9fe769..7aa08717 100644 --- a/VDS/BaseClasses/Control.swift +++ b/VDS/BaseClasses/Control.swift @@ -129,7 +129,6 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- open var accessibilityAction: ((Control) -> Void)? - private var _isAccessibilityElement: Bool = false open override var isAccessibilityElement: Bool { get { var block: AXBoolReturnBlock? @@ -137,7 +136,7 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { // if #available(iOS 17, *) { // block = isAccessibilityElementBlock // } - + if block == nil { block = bridge_isAccessibilityElementBlock } @@ -145,15 +144,14 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { if let block { return block() } else { - return _isAccessibilityElement + return super.isAccessibilityElement } } set { - _isAccessibilityElement = newValue + super.isAccessibilityElement = newValue } } - private var _accessibilityLabel: String? open override var accessibilityLabel: String? { get { var block: AXStringReturnBlock? @@ -168,15 +166,14 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { if let block { return block() } else { - return _accessibilityLabel + return super.accessibilityLabel } } set { - _accessibilityLabel = newValue + super.accessibilityLabel = newValue } } - private var _accessibilityHint: String? open override var accessibilityHint: String? { get { var block: AXStringReturnBlock? @@ -191,15 +188,14 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { if let block { return block() } else { - return _accessibilityHint + return super.accessibilityHint } } set { - _accessibilityHint = newValue + super.accessibilityHint = newValue } } - private var _accessibilityValue: String? open override var accessibilityValue: String? { get { var block: AXStringReturnBlock? @@ -215,11 +211,11 @@ open class Control: UIControl, ViewProtocol, UserInfoable, Clickable { if let block{ return block() } else { - return _accessibilityValue + return super.accessibilityValue } } set { - _accessibilityValue = newValue + super.accessibilityValue = newValue } } diff --git a/VDS/BaseClasses/View.swift b/VDS/BaseClasses/View.swift index c7df1765..7e88df8e 100644 --- a/VDS/BaseClasses/View.swift +++ b/VDS/BaseClasses/View.swift @@ -96,7 +96,6 @@ open class View: UIView, ViewProtocol, UserInfoable { //-------------------------------------------------- open var accessibilityAction: ((View) -> Void)? - private var _isAccessibilityElement: Bool = false open override var isAccessibilityElement: Bool { get { var block: AXBoolReturnBlock? @@ -112,22 +111,21 @@ open class View: UIView, ViewProtocol, UserInfoable { if let block { return block() } else { - return _isAccessibilityElement + return super.isAccessibilityElement } } set { - _isAccessibilityElement = newValue + super.isAccessibilityElement = newValue } } - private var _accessibilityLabel: String? open override var accessibilityLabel: String? { get { var block: AXStringReturnBlock? // if #available(iOS 17, *) { // block = accessibilityLabelBlock // } -// + if block == nil { block = bridge_accessibilityLabelBlock } @@ -135,15 +133,14 @@ open class View: UIView, ViewProtocol, UserInfoable { if let block { return block() } else { - return _accessibilityLabel + return super.accessibilityLabel } } set { - _accessibilityLabel = newValue + super.accessibilityLabel = newValue } } - private var _accessibilityHint: String? open override var accessibilityHint: String? { get { var block: AXStringReturnBlock? @@ -158,15 +155,14 @@ open class View: UIView, ViewProtocol, UserInfoable { if let block { return block() } else { - return _accessibilityHint + return super.accessibilityHint } } set { - _accessibilityHint = newValue + super.accessibilityHint = newValue } } - private var _accessibilityValue: String? open override var accessibilityValue: String? { get { var block: AXStringReturnBlock? @@ -182,11 +178,11 @@ open class View: UIView, ViewProtocol, UserInfoable { if let block{ return block() } else { - return _accessibilityValue + return super.accessibilityValue } } set { - _accessibilityValue = newValue + super.accessibilityValue = newValue } } diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index 720fca36..eebf3a68 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -114,11 +114,6 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { titleLabel?.adjustsFontSizeToFitWidth = false titleLabel?.lineBreakMode = .byTruncatingTail titleLabel?.numberOfLines = 1 - - bridge_accessibilityLabelBlock = { [weak self] in - guard let self else { return nil } - return text - } } open func updateView() { @@ -182,7 +177,6 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { //-------------------------------------------------- open var accessibilityAction: ((ButtonBase) -> Void)? - private var _isAccessibilityElement: Bool = false open override var isAccessibilityElement: Bool { get { var block: AXBoolReturnBlock? @@ -198,15 +192,14 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { if let block { return block() } else { - return _isAccessibilityElement + return super.isAccessibilityElement } } set { - _isAccessibilityElement = newValue + super.isAccessibilityElement = newValue } } - private var _accessibilityLabel: String? open override var accessibilityLabel: String? { get { var block: AXStringReturnBlock? @@ -221,15 +214,14 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { if let block { return block() } else { - return _accessibilityLabel + return super.accessibilityLabel } } set { - _accessibilityLabel = newValue + super.accessibilityLabel = newValue } } - private var _accessibilityHint: String? open override var accessibilityHint: String? { get { var block: AXStringReturnBlock? @@ -244,15 +236,14 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { if let block { return block() } else { - return _accessibilityHint + return super.accessibilityHint } } set { - _accessibilityHint = newValue + super.accessibilityHint = newValue } } - private var _accessibilityValue: String? open override var accessibilityValue: String? { get { var block: AXStringReturnBlock? @@ -268,11 +259,11 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { if let block{ return block() } else { - return _accessibilityValue + return super.accessibilityValue } } set { - _accessibilityValue = newValue + super.accessibilityValue = newValue } } diff --git a/VDS/Components/DatePicker/DatePicker.swift b/VDS/Components/DatePicker/DatePicker.swift index cb2b5b88..17ededf4 100644 --- a/VDS/Components/DatePicker/DatePicker.swift +++ b/VDS/Components/DatePicker/DatePicker.swift @@ -370,7 +370,7 @@ extension DatePicker { var popoverY: CGFloat = 0 // Calculate horizontal position - if sourceFrameInParent.width < popoverWidth { + if sourceFrameInParent.width <= popoverWidth { if sourceFrameInParent.midX - popoverWidth / 2 < 0 { // Align to left popoverX = sourceFrameInParent.minX @@ -382,7 +382,7 @@ extension DatePicker { popoverX = sourceFrameInParent.midX - popoverWidth / 2 } } else { - popoverX = sourceFrameInParent.midX - popoverWidth / 2 + popoverX = sourceFrameInParent.minX //sourceFrameInParent.midX - popoverWidth / 2 } // Ensure the popover is within the parent's bounds horizontally diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index dccc0353..e408953b 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -214,10 +214,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable { } open func setup() { - bridge_accessibilityLabelBlock = { [weak self] in - guard let self else { return "" } - return text - } } open func reset() { @@ -498,7 +494,6 @@ open class Label: UILabel, ViewProtocol, UserInfoable { open var accessibilityAction: ((Label) -> Void)? - private var _isAccessibilityElement: Bool = false open override var isAccessibilityElement: Bool { get { var block: AXBoolReturnBlock? @@ -514,15 +509,14 @@ open class Label: UILabel, ViewProtocol, UserInfoable { if let block { return block() } else { - return _isAccessibilityElement + return super.isAccessibilityElement } } set { - _isAccessibilityElement = newValue + super.isAccessibilityElement = newValue } } - private var _accessibilityLabel: String? open override var accessibilityLabel: String? { get { var block: AXStringReturnBlock? @@ -537,15 +531,14 @@ open class Label: UILabel, ViewProtocol, UserInfoable { if let block { return block() } else { - return _accessibilityLabel + return super.accessibilityLabel } } set { - _accessibilityLabel = newValue + super.accessibilityLabel = newValue } } - private var _accessibilityHint: String? open override var accessibilityHint: String? { get { var block: AXStringReturnBlock? @@ -560,15 +553,14 @@ open class Label: UILabel, ViewProtocol, UserInfoable { if let block { return block() } else { - return _accessibilityHint + return super.accessibilityHint } } set { - _accessibilityHint = newValue + super.accessibilityHint = newValue } } - private var _accessibilityValue: String? open override var accessibilityValue: String? { get { var block: AXStringReturnBlock? @@ -584,11 +576,11 @@ open class Label: UILabel, ViewProtocol, UserInfoable { if let block{ return block() } else { - return _accessibilityValue + return super.accessibilityValue } } set { - _accessibilityValue = newValue + super.accessibilityValue = newValue } } diff --git a/VDS/Components/TextFields/InputField/TextField.swift b/VDS/Components/TextFields/InputField/TextField.swift index 0108c874..05dc30f2 100644 --- a/VDS/Components/TextFields/InputField/TextField.swift +++ b/VDS/Components/TextFields/InputField/TextField.swift @@ -236,7 +236,6 @@ open class TextField: UITextField, ViewProtocol, Errorable { //-------------------------------------------------- open var accessibilityAction: ((TextField) -> Void)? - private var _isAccessibilityElement: Bool = false open override var isAccessibilityElement: Bool { get { var block: AXBoolReturnBlock? @@ -252,15 +251,14 @@ open class TextField: UITextField, ViewProtocol, Errorable { if let block { return block() } else { - return _isAccessibilityElement + return super.isAccessibilityElement } } set { - _isAccessibilityElement = newValue + super.isAccessibilityElement = newValue } } - private var _accessibilityLabel: String? open override var accessibilityLabel: String? { get { var block: AXStringReturnBlock? @@ -275,15 +273,14 @@ open class TextField: UITextField, ViewProtocol, Errorable { if let block { return block() } else { - return _accessibilityLabel + return super.accessibilityLabel } } set { - _accessibilityLabel = newValue + super.accessibilityLabel = newValue } } - private var _accessibilityHint: String? open override var accessibilityHint: String? { get { var block: AXStringReturnBlock? @@ -298,15 +295,14 @@ open class TextField: UITextField, ViewProtocol, Errorable { if let block { return block() } else { - return _accessibilityHint + return super.accessibilityHint } } set { - _accessibilityHint = newValue + super.accessibilityHint = newValue } } - private var _accessibilityValue: String? open override var accessibilityValue: String? { get { var block: AXStringReturnBlock? @@ -322,11 +318,11 @@ open class TextField: UITextField, ViewProtocol, Errorable { if let block{ return block() } else { - return _accessibilityValue + return super.accessibilityValue } } set { - _accessibilityValue = newValue + super.accessibilityValue = newValue } } diff --git a/VDS/Components/TextFields/TextArea/TextView.swift b/VDS/Components/TextFields/TextArea/TextView.swift index ed622f90..54fa9452 100644 --- a/VDS/Components/TextFields/TextArea/TextView.swift +++ b/VDS/Components/TextFields/TextArea/TextView.swift @@ -151,7 +151,6 @@ open class TextView: UITextView, ViewProtocol, Errorable { //-------------------------------------------------- open var accessibilityAction: ((TextView) -> Void)? - private var _isAccessibilityElement: Bool = false open override var isAccessibilityElement: Bool { get { var block: AXBoolReturnBlock? @@ -167,15 +166,14 @@ open class TextView: UITextView, ViewProtocol, Errorable { if let block { return block() } else { - return _isAccessibilityElement + return super.isAccessibilityElement } } set { - _isAccessibilityElement = newValue + super.isAccessibilityElement = newValue } } - private var _accessibilityLabel: String? open override var accessibilityLabel: String? { get { var block: AXStringReturnBlock? @@ -190,15 +188,14 @@ open class TextView: UITextView, ViewProtocol, Errorable { if let block { return block() } else { - return _accessibilityLabel + return super.accessibilityLabel } } set { - _accessibilityLabel = newValue + super.accessibilityLabel = newValue } } - private var _accessibilityHint: String? open override var accessibilityHint: String? { get { var block: AXStringReturnBlock? @@ -213,15 +210,14 @@ open class TextView: UITextView, ViewProtocol, Errorable { if let block { return block() } else { - return _accessibilityHint + return super.accessibilityHint } } set { - _accessibilityHint = newValue + super.accessibilityHint = newValue } } - private var _accessibilityValue: String? open override var accessibilityValue: String? { get { var block: AXStringReturnBlock? @@ -237,11 +233,11 @@ open class TextView: UITextView, ViewProtocol, Errorable { if let block{ return block() } else { - return _accessibilityValue + return super.accessibilityValue } } set { - _accessibilityValue = newValue + super.accessibilityValue = newValue } } diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 546d8d05..6eeaf316 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -44,6 +44,7 @@ open class TileContainer: TileContainerBase { } open class TileContainerBase: Control where PaddingType.ValueType == CGFloat { + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -109,8 +110,13 @@ open class TileContainerBase: Control where Padding $0.clipsToBounds = true } - internal var containerView = View() - + open var containerView = View().with { + $0.setContentHuggingPriority(.defaultLow, for: .horizontal) + $0.setContentHuggingPriority(.defaultLow, for: .vertical) + $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + $0.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -179,12 +185,7 @@ open class TileContainerBase: Control where Padding //-------------------------------------------------- internal var widthConstraint: NSLayoutConstraint? internal var heightConstraint: NSLayoutConstraint? - internal var heightGreaterThanConstraint: NSLayoutConstraint? - internal var containerTopConstraint: NSLayoutConstraint? - internal var containerBottomConstraint: NSLayoutConstraint? - internal var containerLeadingConstraint: NSLayoutConstraint? - internal var containerTrailingConstraint: NSLayoutConstraint? - + //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- @@ -222,28 +223,20 @@ open class TileContainerBase: Control where Padding super.setup() isAccessibilityElement = false - let layoutGuide = UILayoutGuide() - addLayoutGuide(layoutGuide) - layoutGuide - .pinTop() - .pinLeading() - .pinTrailing(0, .defaultHigh) - .pinBottom(0, .defaultHigh) - addSubview(backgroundImageView) addSubview(containerView) - containerView.addSubview(contentView) - addSubview(highlightView) - containerView.pinToSuperView() - widthConstraint = layoutGuide.widthAnchor.constraint(equalToConstant: 0) - - heightGreaterThanConstraint = layoutGuide.heightAnchor.constraint(greaterThanOrEqualToConstant: 44.0) - heightGreaterThanConstraint?.isActive = false - - heightConstraint = layoutGuide.heightAnchor.constraint(equalToConstant: 0) - + containerView.addSubview(backgroundImageView) backgroundImageView.pinToSuperView() + + containerView.addSubview(contentView) + contentView.pinToSuperView() + + containerView.addSubview(highlightView) + highlightView.pinToSuperView() + + widthConstraint = widthAnchor.constraint(equalToConstant: 0).deactivate() + heightConstraint = heightAnchor.constraint(equalToConstant: 0).deactivate() backgroundImageView.setContentHuggingPriority(.defaultLow, for: .horizontal) backgroundImageView.setContentHuggingPriority(.defaultLow, for: .vertical) @@ -252,24 +245,27 @@ open class TileContainerBase: Control where Padding backgroundImageView.isUserInteractionEnabled = false backgroundImageView.isHidden = true - containerTopConstraint = contentView.pinTop(anchor: layoutGuide.topAnchor, constant: padding.value) - containerBottomConstraint = layoutGuide.pinBottom(anchor: contentView.bottomAnchor, constant: padding.value) - containerLeadingConstraint = contentView.pinLeading(anchor: layoutGuide.leadingAnchor, constant: padding.value) - containerTrailingConstraint = layoutGuide.pinTrailing(anchor: contentView.trailingAnchor, constant: padding.value) - - highlightView.pin(layoutGuide) highlightView.isHidden = true highlightView.backgroundColor = .clear //corner radius - layer.cornerRadius = cornerRadius + containerView.layer.cornerRadius = cornerRadius backgroundImageView.layer.cornerRadius = cornerRadius highlightView.layer.cornerRadius = cornerRadius - clipsToBounds = true + containerView.clipsToBounds = true containerView.bridge_isAccessibilityElementBlock = { [weak self] in self?.onClickSubscriber != nil } containerView.accessibilityHint = "Double tap to open." containerView.accessibilityLabel = nil + + NotificationCenter.default + .publisher(for: UIDevice.orientationDidChangeNotification) + .sink() { [weak self] _ in + DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { [weak self] in + guard let self else { return } + setNeedsUpdate() + } + }.store(in: &subscribers) } @@ -286,7 +282,7 @@ open class TileContainerBase: Control where Padding super.reset() shouldUpdateView = false color = .white - aspectRatio = .ratio1x1 + aspectRatio = .none imageFallbackColor = .light width = nil height = nil @@ -303,46 +299,15 @@ open class TileContainerBase: Control where Padding highlightView.backgroundColor = hightLightViewColorConfiguration.getColor(self) highlightView.isHidden = !isHighlighted - layer.borderColor = borderColorConfiguration.getColor(self).cgColor - layer.borderWidth = showBorder ? VDSFormControls.borderWidth : 0 + containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor + containerView.layer.borderWidth = showBorder ? VDSFormControls.borderWidth : 0 - containerTopConstraint?.constant = padding.value - containerLeadingConstraint?.constant = padding.value - containerBottomConstraint?.constant = padding.value - containerTrailingConstraint?.constant = padding.value - - if let width, aspectRatio == .none && height == nil{ - widthConstraint?.constant = width - widthConstraint?.isActive = true - heightConstraint?.isActive = false - heightGreaterThanConstraint?.isActive = true - } else if let height, let width { - widthConstraint?.constant = width - heightConstraint?.constant = height - heightConstraint?.isActive = true - widthConstraint?.isActive = true - heightGreaterThanConstraint?.isActive = false - } else if let width { - let size = ratioSize(for: width) - widthConstraint?.constant = size.width - heightConstraint?.constant = size.height - widthConstraint?.isActive = true - heightConstraint?.isActive = true - heightGreaterThanConstraint?.isActive = false - } else { - widthConstraint?.isActive = false - heightConstraint?.isActive = false - } - - applyBackgroundEffects() + contentView.removeConstraints() + contentView.pinToSuperView(.uniform(padding.value)) - if showDropShadow, surface == .light { - addDropShadow(dropShadowConfiguration) - } else { - removeDropShadows() - } + updateContainerView() } - + open override var accessibilityElements: [Any]? { get { var items = [Any]() @@ -369,13 +334,6 @@ open class TileContainerBase: Control where Padding set {} } - /// Used to update frames for the added CAlayers to our view - open override func layoutSubviews() { - super.layoutSubviews() - dropShadowLayers?.forEach { $0.frame = bounds } - gradientLayers?.forEach { $0.frame = bounds } - } - //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- @@ -400,25 +358,25 @@ open class TileContainerBase: Control where Padding switch backgroundEffect { case .transparency: alphaConfiguration = 0.8 - removeGradientLayer() + containerView.removeGradientLayer() case .gradient(let firstColor, let secondColor): alphaConfiguration = 1.0 - addGradientLayer(with: firstColor, secondColor: secondColor) + containerView.addGradientLayer(with: firstColor, secondColor: secondColor) backgroundImageView.isHidden = true backgroundImageView.alpha = 1.0 case .none: alphaConfiguration = 1.0 - removeGradientLayer() + containerView.removeGradientLayer() } if let backgroundImage { backgroundImageView.image = backgroundImage backgroundImageView.isHidden = false backgroundImageView.alpha = alphaConfiguration - backgroundColor = imageFallbackColor.withAlphaComponent(alphaConfiguration) + containerView.backgroundColor = imageFallbackColor.withAlphaComponent(alphaConfiguration) } else { backgroundImageView.isHidden = true backgroundImageView.alpha = 1.0 - backgroundColor = color.withAlphaComponent(alphaConfiguration) + containerView.backgroundColor = color.withAlphaComponent(alphaConfiguration) } } @@ -451,7 +409,77 @@ open class TileContainerBase: Control where Padding return CGSize(width: width, height: height) } + + private func sizeContainerView(width: CGFloat? = nil, height: CGFloat? = nil) { + if let width, width > 0 { + widthConstraint?.constant = width + widthConstraint?.activate() + } + + if let height, height > 0 { + heightConstraint?.constant = height + heightConstraint?.activate() + } + } + + private func updateContainerView() { + applyBackgroundEffects() + + widthConstraint?.deactivate() + heightConstraint?.deactivate() + if showDropShadow, surface == .light { + containerView.addDropShadow(dropShadowConfiguration) + } else { + containerView.removeDropShadows() + } + + containerView.dropShadowLayers?.forEach { $0.frame = containerView.bounds } + containerView.gradientLayers?.forEach { $0.frame = containerView.bounds } + + if width != nil || height != nil { + var containerViewWidth: CGFloat? + var containerViewHeight: CGFloat? + //run logic to determine which to activate + if let width, aspectRatio == .none && height == nil{ + containerViewWidth = width + + } else if let height, aspectRatio == .none && width == nil{ + containerViewHeight = height + + } else if let height, let width { + containerViewWidth = width + containerViewHeight = height + + } else if let width { + let size = ratioSize(for: width) + containerViewWidth = size.width + containerViewHeight = size.height + + } else if let height { + let size = ratioSize(for: height) + containerViewWidth = size.width + containerViewHeight = size.height + } + sizeContainerView(width: containerViewWidth, height: containerViewHeight) + } else { + if let parentSize = horizontalPinnedSize() { + + var containerViewWidth: CGFloat? + var containerViewHeight: CGFloat? + + let size = ratioSize(for: parentSize.width) + if aspectRatio == .none { + containerViewWidth = size.width + } else { + containerViewWidth = size.width + containerViewHeight = size.height + } + + sizeContainerView(width: containerViewWidth, height: containerViewHeight) + } + } + } } extension TileContainerBase { diff --git a/VDS/Components/Tilelet/Tilelet.swift b/VDS/Components/Tilelet/Tilelet.swift index 95acf895..0da20709 100644 --- a/VDS/Components/Tilelet/Tilelet.swift +++ b/VDS/Components/Tilelet/Tilelet.swift @@ -302,8 +302,9 @@ open class Tilelet: TileContainerBase { /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. open override func setup() { super.setup() - aspectRatio = .none color = .black + aspectRatio = .none + addContentView(stackView) //badge @@ -386,6 +387,7 @@ open class Tilelet: TileContainerBase { /// Resets to default settings. open override func reset() { shouldUpdateView = false + super.reset() aspectRatio = .none color = .black //models @@ -405,11 +407,7 @@ open class Tilelet: TileContainerBase { updateBadge() updateTitleLockup() updateIcons() - ///Content-driven height Tilelets - Minimum height is configurable. - ///if width != nil && (aspectRatio != .none || height != nil) then tilelet is not self growing, so we can apply text position alignments. - if width != nil && (aspectRatio != .none || height != nil) { - updateTextPositionAlignment() - } + updateTextPositionAlignment() setNeedsLayout() } @@ -584,6 +582,7 @@ open class Tilelet: TileContainerBase { } private func updateTextPositionAlignment() { + guard width != nil && (aspectRatio != .none || height != nil) else { return } switch textPostion { case .top: titleLockupTopConstraint?.activate() diff --git a/VDS/Components/Toggle/ToggleView.swift b/VDS/Components/Toggle/ToggleView.swift index 935ed519..dc5e9570 100644 --- a/VDS/Components/Toggle/ToggleView.swift +++ b/VDS/Components/Toggle/ToggleView.swift @@ -219,7 +219,7 @@ open class ToggleView: Control, Changeable, FormFieldable { } knobTrailingConstraint?.isActive = true knobLeadingConstraint?.isActive = true - setNeedsLayout() + layoutIfNeeded() } private func updateToggle() { diff --git a/VDS/Protocols/LayoutConstraintable.swift b/VDS/Protocols/LayoutConstraintable.swift index 3e40885b..ba08e3c2 100644 --- a/VDS/Protocols/LayoutConstraintable.swift +++ b/VDS/Protocols/LayoutConstraintable.swift @@ -631,6 +631,195 @@ extension LayoutConstraintable { return centerYAnchor.constraint(greaterThanOrEqualTo: found, constant: -constant).with { $0.priority = priority; $0.isActive = true } } } + +// alignment +public enum LayoutAlignment: String, CaseIterable { + case fill + case leading + case top + case center + case trailing + case bottom +} + +public enum LayoutDistribution: String, CaseIterable { + case fill + case fillProportionally +} + +extension LayoutConstraintable { + public func removeConstraints() { + guard let view = self as? UIView, let superview = view.superview else { return } + + // Remove all existing constraints on the containerView + let superviewConstraints = superview.constraints + for constraint in superviewConstraints { + if constraint.firstItem as? UIView == view + || constraint.secondItem as? UIView == view { + superview.removeConstraint(constraint) + } + } + } + + public func applyAlignment(_ alignment: LayoutAlignment, edges: UIEdgeInsets = UIEdgeInsets.zero) { + guard let superview = superview else { return } + + removeConstraints() + + switch alignment { + case .fill: + pinToSuperView(edges) + + case .leading: + pinTop(edges.top) + pinLeading(edges.left) + pinTrailingLessThanOrEqualTo(anchor: superview.trailingAnchor, constant: edges.right) + pinBottom(edges.bottom) + + case .trailing: + pinTop(edges.top) + pinLeadingGreaterThanOrEqualTo(anchor: superview.leadingAnchor, constant: edges.left) + pinTrailing(edges.right) + pinBottom(edges.bottom) + + case .top: + pinTop(edges.top) + pinLeadingGreaterThanOrEqualTo(anchor: superview.leadingAnchor, constant: edges.left) + pinTrailingLessThanOrEqualTo(anchor: superview.trailingAnchor, constant: edges.right) + pinBottomLessThanOrEqualTo(anchor: superview.bottomAnchor, constant: edges.bottom) + + case .bottom: + pinTopGreaterThanOrEqualTo(anchor: superview.topAnchor, constant: edges.top) + pinLeadingGreaterThanOrEqualTo(anchor: superview.leadingAnchor, constant: edges.left) + pinTrailingLessThanOrEqualTo(anchor: superview.trailingAnchor, constant: edges.right) + pinBottom(edges.bottom) + + case .center: + pinCenterX() + pinTop(edges.top) + pinLeadingGreaterThanOrEqualTo(anchor: superview.leadingAnchor, constant: edges.left) + pinTrailingLessThanOrEqualTo(anchor: superview.trailingAnchor, constant: edges.right) + pinBottom(edges.bottom) + + } + } + + // Method to check if the view is pinned to its superview + public func isPinnedToSuperview() -> Bool { + isPinnedVerticallyToSuperview() && isPinnedHorizontallyToSuperview() + } + + public func horizontalPinnedSize() -> CGSize? { + guard let view = self as? UIView, let superview = view.superview else { return nil } + let constraints = superview.constraints + + var leadingPinnedObject: AnyObject? + var trailingPinnedObject: AnyObject? + + for constraint in constraints { + if (constraint.firstItem === view && (constraint.firstAttribute == .leading || constraint.firstAttribute == .left)) { + leadingPinnedObject = constraint.secondItem as AnyObject? + } else if (constraint.secondItem === view && (constraint.secondAttribute == .leading || constraint.secondAttribute == .left)) { + leadingPinnedObject = constraint.firstItem as AnyObject? + } else if (constraint.firstItem === view && (constraint.firstAttribute == .trailing || constraint.firstAttribute == .right)) { + trailingPinnedObject = constraint.secondItem as AnyObject? + } else if (constraint.secondItem === view && (constraint.secondAttribute == .trailing || constraint.secondAttribute == .right)) { + trailingPinnedObject = constraint.firstItem as AnyObject? + } + } + + // Ensure both leading and trailing pinned objects are identified + if let leadingObject = leadingPinnedObject, let trailingObject = trailingPinnedObject { + + // Calculate the size based on the pinned objects + if let leadingView = leadingObject as? UIView, let trailingView = trailingObject as? UIView { + let leadingPosition = leadingView.convert(leadingView.bounds.origin, to: superview).x + let trailingPosition = trailingView.convert(trailingView.bounds.origin, to: superview).x + trailingView.bounds.width + return CGSize(width: trailingPosition - leadingPosition, height: view.bounds.size.height) + + } else if let leadingGuide = leadingObject as? UILayoutGuide, let trailingGuide = trailingObject as? UILayoutGuide { + let leadingPosition = leadingGuide.layoutFrame.minX + let trailingPosition = trailingGuide.layoutFrame.maxX + return CGSize(width: trailingPosition - leadingPosition, height: view.bounds.size.height) + + } else if let leadingView = leadingObject as? UIView, let trailingGuide = trailingObject as? UILayoutGuide { + let leadingPosition = leadingView.convert(leadingView.bounds.origin, to: superview).x + let trailingPosition = trailingGuide.layoutFrame.maxX + return CGSize(width: trailingPosition - leadingPosition, height: view.bounds.size.height) + + } else if let leadingGuide = leadingObject as? UILayoutGuide, let trailingView = trailingObject as? UIView { + let leadingPosition = leadingGuide.layoutFrame.minX + let trailingPosition = trailingView.convert(trailingView.bounds.origin, to: superview).x + trailingView.bounds.width + return CGSize(width: trailingPosition - leadingPosition, height: view.bounds.size.height) + } + + } else if let pinnedObject = leadingPinnedObject { + if let view = pinnedObject as? UIView { + return view.bounds.size + } else if let layoutGuide = pinnedObject as? UILayoutGuide { + return layoutGuide.layoutFrame.size + } + + } else if let pinnedObject = trailingPinnedObject { + if let view = pinnedObject as? UIView { + return view.bounds.size + } else if let layoutGuide = pinnedObject as? UILayoutGuide { + return layoutGuide.layoutFrame.size + } + + } + + return nil + } + + public func isPinnedHorizontallyToSuperview() -> Bool { + guard let view = self as? UIView, let superview = view.superview else { return false } + let constraints = superview.constraints + var leadingPinned = false + var trailingPinned = false + + for constraint in constraints { + if (constraint.firstItem as? UIView == view && constraint.firstAttribute == .leading && constraint.relation == .equal) || + (constraint.secondItem as? UIView == view && constraint.secondAttribute == .leading && constraint.relation == .equal) || + (constraint.firstItem as? UIView == view && constraint.firstAttribute == .left && constraint.relation == .equal) || + (constraint.secondItem as? UIView == view && constraint.secondAttribute == .left && constraint.relation == .equal) { + leadingPinned = true + } + if (constraint.firstItem as? UIView == view && constraint.firstAttribute == .trailing && constraint.relation == .equal) || + (constraint.secondItem as? UIView == view && constraint.secondAttribute == .trailing && constraint.relation == .equal) || + (constraint.firstItem as? UIView == view && constraint.firstAttribute == .right && constraint.relation == .equal) || + (constraint.secondItem as? UIView == view && constraint.secondAttribute == .right && constraint.relation == .equal) { + trailingPinned = true + } + } + + return leadingPinned && trailingPinned + } + + public func isPinnedVerticallyToSuperview() -> Bool { + guard let view = self as? UIView, let superview = view.superview else { return false } + let constraints = superview.constraints + var topPinned = false + var bottomPinned = false + + for constraint in constraints { + if (constraint.firstItem as? UIView == view && constraint.firstAttribute == .top && constraint.relation == .equal) || + (constraint.secondItem as? UIView == view && constraint.secondAttribute == .top && constraint.relation == .equal) { + topPinned = true + } + if (constraint.firstItem as? UIView == view && constraint.firstAttribute == .bottom && constraint.relation == .equal) || + (constraint.secondItem as? UIView == view && constraint.secondAttribute == .bottom && constraint.relation == .equal) { + bottomPinned = true + } + } + + return topPinned && bottomPinned + } + + +} + + //-------------------------------------------------- // MARK: - Implementations //--------------------------------------------------