vds_ios/VDS/Components/TileContainer/TileContainer.swift
Matt Bruce 56c2217b0c public to open
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
2023-08-04 16:01:38 -05:00

343 lines
11 KiB
Swift

//
// TileContainer.swift
// VDS
//
// Created by Matt Bruce on 12/16/22.
//
import Foundation
import VDSColorTokens
import VDSFormControlsTokens
import UIKit
@objc(VDSTileContainer)
open class TileContainer: Control {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init() {
super.init(frame: .zero)
}
public override init(frame: CGRect) {
super.init(frame: .zero)
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
}
//--------------------------------------------------
// MARK: - Enums
//--------------------------------------------------
public enum BackgroundColor: String, CaseIterable {
case white
case black
case gray
case transparent
}
public enum Padding: String, CaseIterable {
case padding2X
case padding4X
case padding6X
case padding8X
case padding12X
public var value: CGFloat {
switch self {
case .padding2X:
return VDSLayout.Spacing.space2X.value
case .padding4X:
return VDSLayout.Spacing.space4X.value
case .padding6X:
return VDSLayout.Spacing.space6X.value
case .padding8X:
return VDSLayout.Spacing.space8X.value
case .padding12X:
return VDSLayout.Spacing.space12X.value
}
}
}
public enum AspectRatio: String, CaseIterable {
case ratio1x1 = "1:1"
case ratio3x4 = "3:4"
case ratio4x3 = "4:3"
case ratio2x3 = "2:3"
case ratio3x2 = "3:2"
case ratio9x16 = "9:16"
case ratio16x9 = "16:9"
case ratio1x2 = "1:2"
case ratio2x1 = "2:1"
case none
}
//--------------------------------------------------
// MARK: - Public Properties
//--------------------------------------------------
open var backgroundImage: UIImage? { didSet{ setNeedsUpdate() } }
open var containerView = View().with {
$0.isUserInteractionEnabled = false
}
open var highlightView = View().with {
$0.isUserInteractionEnabled = false
}
open var color: BackgroundColor = .white { didSet{ setNeedsUpdate() } }
open var padding: Padding = .padding4X { didSet{ setNeedsUpdate() } }
open var aspectRatio: AspectRatio = .ratio1x1 { didSet{ setNeedsUpdate() } }
open var imageFallbackColor: Surface = .light { didSet{ setNeedsUpdate() } }
private var _width: CGFloat?
open var width: CGFloat? {
get { return _width }
set {
if let newValue, newValue > 100 {
_width = newValue
} else {
_width = nil
}
setNeedsUpdate()
}
}
private var _height: CGFloat?
open var height: CGFloat? {
get { return _height }
set {
if let newValue, newValue > 44 {
_height = newValue
} else {
_height = nil
}
setNeedsUpdate()
}
}
open var showBorder: Bool = false { didSet{ setNeedsUpdate() } }
open var showDropShadows: Bool = false { didSet{ setNeedsUpdate() } }
//--------------------------------------------------
// MARK: - Private Properties
//--------------------------------------------------
private var backgroundImageView = UIImageView().with {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.contentMode = .scaleAspectFill
$0.clipsToBounds = true
}
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
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: - Lifecycle
//--------------------------------------------------
open override func setup() {
super.setup()
addSubview(backgroundImageView)
addSubview(containerView)
addSubview(highlightView)
widthConstraint = widthAnchor.constraint(equalToConstant: 0)
heightGreaterThanConstraint = heightAnchor.constraint(greaterThanOrEqualToConstant: 44.0)
heightGreaterThanConstraint?.isActive = false
heightConstraint = heightAnchor.constraint(equalToConstant: 0)
backgroundImageView.pinToSuperView()
backgroundImageView.isUserInteractionEnabled = false
backgroundImageView.isHidden = true
containerView.backgroundColor = .clear
containerTopConstraint = containerView.topAnchor.constraint(equalTo: topAnchor, constant: padding.value)
containerTopConstraint?.isActive = true
containerBottomConstraint = containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: padding.value)
containerBottomConstraint?.isActive = true
containerLeadingConstraint = containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding.value)
containerLeadingConstraint?.isActive = true
containerTrailingConstraint = containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: padding.value)
containerTrailingConstraint?.isActive = true
highlightView.pinToSuperView()
highlightView.isHidden = true
highlightView.backgroundColor = .clear
//corner radius
layer.cornerRadius = cornerRadius
backgroundImageView.layer.cornerRadius = cornerRadius
highlightView.layer.cornerRadius = cornerRadius
}
/// Resets to default settings.
open override func reset() {
super.reset()
shouldUpdateView = false
color = .white
padding = .padding4X
aspectRatio = .ratio1x1
imageFallbackColor = .light
width = nil
height = nil
showBorder = false
showDropShadows = false
shouldUpdateView = true
setNeedsUpdate()
}
/// Function used to make changes to the View based off a change events or from local properties.
open override func updateView() {
super.updateView()
highlightView.backgroundColor = hightLightViewColorConfiguration.getColor(self)
highlightView.isHidden = !isHighlighted
if let backgroundImage {
backgroundImageView.image = backgroundImage
backgroundImageView.isHidden = false
backgroundColor = imageFallbackColorConfiguration.getColor(self)
} else {
backgroundImageView.isHidden = true
backgroundColor = backgroundColorConfiguration.getColor(self)
}
layer.borderColor = borderColorConfiguration.getColor(self).cgColor
layer.borderWidth = showBorder ? VDSFormControls.widthBorder : 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
}
}
//--------------------------------------------------
// MARK: - Public Functions
//--------------------------------------------------
public func addContentView(_ view: UIView, shouldPin: Bool = true) {
containerView.addSubview(view)
if shouldPin {
view.pinToSuperView()
}
}
//--------------------------------------------------
// MARK: - Configuration
//--------------------------------------------------
private let cornerRadius = VDSFormControls.borderradius * 2
private var backgroundColorConfiguration = BackgroundColorConfiguration()
private var borderColorConfiguration = SurfaceColorConfiguration().with {
$0.lightColor = VDSColor.elementsLowcontrastOnlight
$0.darkColor = VDSColor.elementsLowcontrastOndark
}
private var imageFallbackColorConfiguration = SurfaceColorConfiguration().with {
$0.lightColor = VDSColor.backgroundPrimaryLight
$0.darkColor = VDSColor.backgroundPrimaryDark
}
private var hightLightViewColorConfiguration = SurfaceColorConfiguration().with {
$0.lightColor = VDSColor.paletteWhite.withAlphaComponent(0.3)
$0.darkColor = VDSColor.paletteBlack.withAlphaComponent(0.3)
}
//--------------------------------------------------
// MARK: - Private Functions
//--------------------------------------------------
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)
}
}
extension TileContainer {
class BackgroundColorConfiguration: ObjectColorable {
typealias ObjectType = TileContainer
required init() { }
func getColor(_ object: TileContainer) -> UIColor {
switch object.color {
case .white:
return VDSColor.backgroundPrimaryLight
case .black:
return VDSColor.backgroundPrimaryDark
case .gray:
return VDSColor.backgroundSecondaryLight
case .transparent:
return UIColor.clear
}
}
}
}