Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git into mbruce/bugfix

This commit is contained in:
Matt Bruce 2024-06-29 10:05:15 -05:00
commit f6287df8f4
3 changed files with 306 additions and 90 deletions

View File

@ -44,6 +44,7 @@ open class TileContainer: TileContainerBase<TileContainer.Padding> {
}
open class TileContainerBase<PaddingType: DefaultValuing>: Control where PaddingType.ValueType == CGFloat {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
@ -109,8 +110,13 @@ open class TileContainerBase<PaddingType: DefaultValuing>: 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<PaddingType: DefaultValuing>: 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<PaddingType: DefaultValuing>: 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<PaddingType: DefaultValuing>: 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<PaddingType: DefaultValuing>: 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<PaddingType: DefaultValuing>: 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<PaddingType: DefaultValuing>: 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<PaddingType: DefaultValuing>: 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<PaddingType: DefaultValuing>: 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 {

View File

@ -302,8 +302,9 @@ open class Tilelet: TileContainerBase<Tilelet.Padding> {
/// 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<Tilelet.Padding> {
/// 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<Tilelet.Padding> {
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<Tilelet.Padding> {
}
private func updateTextPositionAlignment() {
guard width != nil && (aspectRatio != .none || height != nil) else { return }
switch textPostion {
case .top:
titleLockupTopConstraint?.activate()

View File

@ -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
//--------------------------------------------------