From b91017068c65bc6865039490d7111fe3821475c6 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 18 Jul 2024 15:34:03 -0500 Subject: [PATCH] Missed a few "fill" types and refactored to use a multiplier Signed-off-by: Matt Bruce --- .../TileContainer/TileContainer.swift | 248 ++++++++++-------- 1 file changed, 141 insertions(+), 107 deletions(-) diff --git a/VDS/Components/TileContainer/TileContainer.swift b/VDS/Components/TileContainer/TileContainer.swift index 6eeaf316..131a62f5 100644 --- a/VDS/Components/TileContainer/TileContainer.swift +++ b/VDS/Components/TileContainer/TileContainer.swift @@ -74,7 +74,7 @@ open class TileContainerBase: Control where Padding case custom(UIColor) private var reflectedValue: String { String(reflecting: self) } - + public static func == (lhs: Self, rhs: Self) -> Bool { lhs.reflectedValue == rhs.reflectedValue } @@ -86,7 +86,7 @@ open class TileContainerBase: Control where Padding case gradient(UIColor, UIColor) case none } - + /// Enum used to describe the aspect ratios used for this component. public enum AspectRatio: String, CaseIterable { case ratio1x1 = "1:1" @@ -109,7 +109,7 @@ open class TileContainerBase: Control where Padding $0.contentMode = .scaleAspectFill $0.clipsToBounds = true } - + open var containerView = View().with { $0.setContentHuggingPriority(.defaultLow, for: .horizontal) $0.setContentHuggingPriority(.defaultLow, for: .vertical) @@ -125,27 +125,27 @@ open class TileContainerBase: Control where Padding /// This is the container in which views will be pinned. open var contentView = View() - + /// This is the view used to show the high light color for a onClick. open var highlightView = View().with { $0.isUserInteractionEnabled = false } - + /// This controls the aspect ratio for the component. open var aspectRatio: AspectRatio = .ratio1x1 { didSet { setNeedsUpdate() } } - + /// Sets the background color for the component. open var color: BackgroundColor? { didSet { setNeedsUpdate() } } /// Sets the background effect for the component. open var backgroundEffect: BackgroundEffect = .none { didSet { setNeedsUpdate() } } - + /// Sets the inside padding for the component open var padding: PaddingType = PaddingType.defaultValue { didSet { setNeedsUpdate() } } /// Applies a background color if backgroundImage prop fails or has trouble loading. open var imageFallbackColor: Surface = .light { didSet { setNeedsUpdate() } } - + private var _width: CGFloat? /// Sets the width for the component. Accepts a pixel value. open var width: CGFloat? { @@ -159,7 +159,7 @@ open class TileContainerBase: Control where Padding setNeedsUpdate() } } - + private var _height: CGFloat? /// Sets the height for the component. Accepts a pixel value. open var height: CGFloat? { @@ -179,13 +179,14 @@ open class TileContainerBase: Control where Padding /// Determines if there is a drop shadow or not. open var showDropShadow: Bool = false { didSet { setNeedsUpdate() } } - + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- internal var widthConstraint: NSLayoutConstraint? internal var heightConstraint: NSLayoutConstraint? - + internal var aspectRatioConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- @@ -228,13 +229,13 @@ open class TileContainerBase: Control where Padding 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() @@ -266,7 +267,7 @@ open class TileContainerBase: Control where Padding setNeedsUpdate() } }.store(in: &subscribers) - + } /// Overriden to take the hit if there is an onClickSubscriber and the view is not a UIControl @@ -291,7 +292,7 @@ open class TileContainerBase: Control where Padding shouldUpdateView = true setNeedsUpdate() } - + /// Used to make changes to the View based off a change events or from local properties. open override func updateView() { super.updateView() @@ -301,13 +302,14 @@ open class TileContainerBase: Control where Padding containerView.layer.borderColor = borderColorConfiguration.getColor(self).cgColor containerView.layer.borderWidth = showBorder ? VDSFormControls.borderWidth : 0 - + contentView.removeConstraints() contentView.pinToSuperView(.uniform(padding.value)) updateContainerView() + } - + open override var accessibilityElements: [Any]? { get { var items = [Any]() @@ -328,7 +330,7 @@ open class TileContainerBase: Control where Padding //append all children that are accessible items.append(contentsOf: elements) - + return items } set {} @@ -337,7 +339,7 @@ open class TileContainerBase: Control where Padding //-------------------------------------------------- // MARK: - Public Methods //-------------------------------------------------- - + /// This will place a view within the contentView of this component. public func addContentView(_ view: UIView, shouldPin: Bool = true) { view.removeFromSuperview() @@ -346,7 +348,7 @@ open class TileContainerBase: Control where Padding view.pinToSuperView() } } - + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- @@ -379,55 +381,10 @@ open class TileContainerBase: Control where Padding containerView.backgroundColor = color.withAlphaComponent(alphaConfiguration) } } - - private func ratioSize(for width: CGFloat) -> CGSize { - var height: CGFloat = width - switch aspectRatio { - case .ratio1x1: - break; - case .ratio3x4: - height = (4 / 3) * width - case .ratio4x3: - height = (3 / 4) * width - case .ratio2x3: - height = (3 / 2) * width - case .ratio3x2: - height = (2 / 3) * width - case .ratio9x16: - height = (16 / 9) * width - case .ratio16x9: - height = (9 / 16) * width - case .ratio1x2: - height = (2 / 1) * width - case .ratio2x1: - height = (1 / 2) * width - - default: - break - } - - 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 { @@ -436,50 +393,100 @@ open class TileContainerBase: Control where Padding containerView.dropShadowLayers?.forEach { $0.frame = containerView.bounds } containerView.gradientLayers?.forEach { $0.frame = containerView.bounds } + + //sizing the container with constraints + + //Set local vars + var containerViewWidth: CGFloat? = width + let containerViewHeight: CGFloat? = height + let multiplier = aspectRatio.multiplier - 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 + //turn off the constraints + aspectRatioConstraint?.deactivate() + widthConstraint?.deactivate() + heightConstraint?.deactivate() - } 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) - } + //------------------------------------------------------------------------- + //Overriding Nil Width Rules + //------------------------------------------------------------------------- + //Rule 1: + //In the scenario where we only have a height but the multiplie is nil, we + //want to set the width with the parent's width which will more or less "fill" + //the container horizontally + //- height is set + //- width is not set + //- aspectRatio is not set + if let superviewWidth, superviewWidth > 0, + containerViewHeight != nil, + containerViewWidth == nil, + multiplier == nil { + containerViewWidth = superviewWidth + } + + //Rule 2: + //In the scenario where no width and height is set, want to set the width with the + //parent's width which will more or less "fill" the container horizontally + //- height is not set + //- width is not set + else if let superviewWidth, superviewWidth > 0, + containerViewWidth == nil, + containerViewHeight == nil { + containerViewWidth = superviewWidth + } + //------------------------------------------------------------------------- + + + //------------------------------------------------------------------------- + //Width + AspectRatio Constraint - Will exit out if set + //------------------------------------------------------------------------- + if let containerViewWidth, + let multiplier, + containerViewWidth > 0, + containerViewHeight == nil { + widthConstraint?.constant = containerViewWidth + widthConstraint?.activate() + aspectRatioConstraint = heightAnchor.constraint(equalTo: widthAnchor, multiplier: multiplier) + aspectRatioConstraint?.activate() + return + } + //------------------------------------------------------------------------- + //Height + AspectRatio Constraint - Will exit out if set + //------------------------------------------------------------------------- + else if let containerViewHeight, + let multiplier, + containerViewHeight > 0, + containerViewWidth == nil { + heightConstraint?.constant = containerViewHeight + heightConstraint?.activate() + aspectRatioConstraint = widthAnchor.constraint(equalTo: heightAnchor, multiplier: multiplier) + aspectRatioConstraint?.activate() + return + } + + //------------------------------------------------------------------------- + //Width Constraint + //------------------------------------------------------------------------- + if let containerViewWidth, + containerViewWidth > 0 { + widthConstraint?.constant = containerViewWidth + widthConstraint?.activate() + } + + //------------------------------------------------------------------------- + //Height Constraint + //------------------------------------------------------------------------- + if let containerViewHeight, + containerViewHeight > 0 { + heightConstraint?.constant = containerViewHeight + heightConstraint?.activate() } } + + /// This is the size of the superview's allowed space for this container first by constrained size which would include padding/inset values an + private var superviewWidth: CGFloat? { + horizontalPinnedWidth() ?? superview?.frame.size.width + } + } extension TileContainerBase { @@ -519,3 +526,30 @@ extension TileContainerBase { } } } + +extension TileContainerBase.AspectRatio { + var multiplier: CGFloat? { + switch self { + case .ratio1x1: + return 1 + case .ratio3x4: + return 4 / 3 + case .ratio4x3: + return 3 / 4 + case .ratio2x3: + return 3 / 2 + case .ratio3x2: + return 2 / 3 + case .ratio9x16: + return 16 / 9 + case .ratio16x9: + return 9 / 16 + case .ratio1x2: + return 2 / 1 + case .ratio2x1: + return 1 / 2 + case .none: + return nil + } + } +}