more changes

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2023-05-16 15:31:52 -05:00
parent e91465f423
commit facafa308b

View File

@ -7,6 +7,7 @@
import Foundation import Foundation
import UIKit import UIKit
import VDSColorTokens
@objc(VDSButtonIcon) @objc(VDSButtonIcon)
open class ButtonIcon: Control { open class ButtonIcon: Control {
@ -19,7 +20,7 @@ open class ButtonIcon: Control {
public enum Kind: String, CaseIterable { public enum Kind: String, CaseIterable {
case ghost, lowContrast, highContrast case ghost, lowContrast, highContrast
} }
public enum SurfaceType: String, CaseIterable { public enum SurfaceType: String, CaseIterable {
case colorFill, media case colorFill, media
} }
@ -47,24 +48,85 @@ open class ButtonIcon: Control {
private var centerYConstraint: NSLayoutConstraint? private var centerYConstraint: NSLayoutConstraint?
private var layoutGuideWidthConstraint: NSLayoutConstraint? private var layoutGuideWidthConstraint: NSLayoutConstraint?
private var layoutGuideHeightConstraint: NSLayoutConstraint? private var layoutGuideHeightConstraint: NSLayoutConstraint?
private var currentIconName: Icon.Name? {
if let selectedIconName {
return selectedIconName
} else {
return iconName
}
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Public Properties // MARK: - Public Properties
//-------------------------------------------------- //--------------------------------------------------
open var icon = Icon() open var icon = Icon()
open var kind: Kind = .ghost { didSet { setNeedsUpdate() } } open var kind: Kind = .ghost { didSet { setNeedsUpdate() } }
open var surfaceType: SurfaceType = .colorFill { didSet { setNeedsUpdate() } } open var surfaceType: SurfaceType = .colorFill { didSet { setNeedsUpdate() } }
open var iconName: Icon.Name? { didSet { setNeedsUpdate() } } open var iconName: Icon.Name? { didSet { setNeedsUpdate() } }
open var selectedIconName: Icon.Name? { didSet { setNeedsUpdate() } }
open var size: Size = .large { didSet { setNeedsUpdate() } } open var size: Size = .large { didSet { setNeedsUpdate() } }
open var customSize: Int? { didSet { setNeedsUpdate() }} open var customSize: Int? { didSet { setNeedsUpdate() }}
open var floating: Bool = false { didSet { setNeedsUpdate() } } open var floating: Bool = false { didSet { setNeedsUpdate() } }
open var fitToIcon: Bool = false { didSet { setNeedsUpdate() } }
open var hideBorder: Bool = true { didSet { setNeedsUpdate() } } open var hideBorder: Bool = true { didSet { setNeedsUpdate() } }
open var iconOffset: CGPoint = .init(x: 0, y: 0) { didSet { setNeedsUpdate() } } open var iconOffset: CGPoint = .init(x: 0, y: 0) { didSet { setNeedsUpdate() } }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Configuration // MARK: - Configuration
//-------------------------------------------------- //--------------------------------------------------
private var iconColorConfig: AnyColorable {
if selectedIconName != nil {
return selectedIconColorConfig
} else {
if kind == .highContrast {
return highContrastIconColorConfig
} else {
return standardIconColorConfig
}
}
}
private var currentConfiguration: any Configuration {
switch kind {
case .ghost:
return GhostConfiguration()
case .lowContrast:
if surfaceType == .colorFill {
return floating ? LowContrastColorFillFloatingConfiguration() : LowContrastColorFillConfiguration()
} else {
return floating ? LowContrastMediaFloatingConfiguration() : LowContrastMediaConfiguration()
}
case .highContrast:
return floating ? HighContrastFloatingConfiguration() : HighContrastConfiguration()
}
}
private var standardIconColorConfig: AnyColorable = {
return ControlColorConfiguration().with {
$0.setSurfaceColors(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark, forState: .normal)
$0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted)
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
}.eraseToAnyColorable()
}()
private var highContrastIconColorConfig: AnyColorable = {
return ControlColorConfiguration().with {
$0.setSurfaceColors(VDSColor.elementsPrimaryOndark, VDSColor.elementsPrimaryOnlight, forState: .normal)
$0.setSurfaceColors(VDSColor.interactiveActiveOndark, VDSColor.interactiveActiveOnlight, forState: .highlighted)
$0.setSurfaceColors(VDSColor.interactiveDisabledOndark, VDSColor.interactiveDisabledOnlight, forState: .disabled)
}.eraseToAnyColorable()
}()
private var selectedIconColorConfig: AnyColorable = {
return ControlColorConfiguration().with {
$0.setSurfaceColors(VDSColor.elementsBrandhighlight, VDSColor.elementsPrimaryOndark, forState: .normal)
$0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted)
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
}.eraseToAnyColorable()
}()
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
@ -84,25 +146,24 @@ open class ButtonIcon: Control {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
//-------------------------------------------------- //--------------------------------------------------
open override func setup() { open override func setup() {
super.setup() super.setup()
//create a layoutGuide for the icon to key off of //create a layoutGuide for the icon to key off of
let iconLayoutGuide = UILayoutGuide() let iconLayoutGuide = UILayoutGuide()
addLayoutGuide(iconLayoutGuide) addLayoutGuide(iconLayoutGuide)
//add the icon //add the icon
addSubview(icon) addSubview(icon)
//determines the height/width of the icon //determines the height/width of the icon
layoutGuideWidthConstraint = iconLayoutGuide.widthAnchor.constraint(equalToConstant: size.containerSize) layoutGuideWidthConstraint = iconLayoutGuide.widthAnchor.constraint(equalToConstant: size.containerSize)
layoutGuideHeightConstraint = iconLayoutGuide.heightAnchor.constraint(equalToConstant: size.containerSize) layoutGuideHeightConstraint = iconLayoutGuide.heightAnchor.constraint(equalToConstant: size.containerSize)
//determines the center point of the icon //determines the center point of the icon
centerXConstraint = icon.centerXAnchor.constraint(equalTo: iconLayoutGuide.centerXAnchor, constant: 0) centerXConstraint = icon.centerXAnchor.constraint(equalTo: iconLayoutGuide.centerXAnchor, constant: 0)
centerYConstraint = icon.centerYAnchor.constraint(equalTo: iconLayoutGuide.centerYAnchor, constant: 0) centerYConstraint = icon.centerYAnchor.constraint(equalTo: iconLayoutGuide.centerYAnchor, constant: 0)
//activate the constraints //activate the constraints
NSLayoutConstraint.activate([layoutGuideWidthConstraint!, NSLayoutConstraint.activate([layoutGuideWidthConstraint!,
layoutGuideHeightConstraint!, layoutGuideHeightConstraint!,
@ -130,10 +191,11 @@ open class ButtonIcon: Control {
open override func updateView() { open override func updateView() {
super.updateView() super.updateView()
//ensure there is an icon to set //ensure there is an icon to set
if let iconName { if let currentIconName {
icon.name = iconName icon.name = currentIconName
icon.color = iconColorConfig.getColor(self)
icon.size = size.value icon.size = size.value
icon.surface = surface icon.surface = surface
icon.disabled = disabled icon.disabled = disabled
@ -141,13 +203,8 @@ open class ButtonIcon: Control {
} else { } else {
icon.reset() icon.reset()
} }
// colors icon.backgroundColor = .green
let bgColor = UIColor.red //backgroundColorConfiguration.getColor(self)
let borderColor = UIColor.green// borderColorConfiguration.getColor(self)
backgroundColor = bgColor
layer.borderColor = borderColor.cgColor
icon.layer.borderColor = UIColor.purple.cgColor
setNeedsLayout() setNeedsLayout()
} }
@ -155,30 +212,140 @@ open class ButtonIcon: Control {
open override func layoutSubviews() { open override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
let borderWidth = 2.0 let currentConfig = currentConfiguration
let cornerRadius = min(frame.width, frame.height) / 2.0
backgroundColor = currentConfig.backgroundColorConfig.getColor(self)
// calculate center point for child view with offset // calculate center point for child view with offset
let childCenter = CGPoint(x: center.x + iconOffset.x, y: center.y + iconOffset.y) let childCenter = CGPoint(x: center.x + iconOffset.x, y: center.y + iconOffset.y)
centerXConstraint?.constant = childCenter.x - center.x centerXConstraint?.constant = childCenter.x - center.x
centerYConstraint?.constant = childCenter.y - center.y centerYConstraint?.constant = childCenter.y - center.y
// calculate the icon's container size ensuring the padding //updating current container size
var iconLayoutSize = size.containerSize var iconLayoutSize = size.containerSize
if let customSize { if let customSize {
iconLayoutSize = CGFloat(customSize) iconLayoutSize = CGFloat(customSize)
} }
// check to see if this is fitToIcon
if fitToIcon && kind == .ghost {
iconLayoutSize = size.value.dimensions.width
layer.cornerRadius = 0
} else {
layer.cornerRadius = min(frame.width, frame.height) / 2.0
}
layoutGuideWidthConstraint?.constant = iconLayoutSize layoutGuideWidthConstraint?.constant = iconLayoutSize
layoutGuideHeightConstraint?.constant = iconLayoutSize layoutGuideHeightConstraint?.constant = iconLayoutSize
//container //border
layer.cornerRadius = cornerRadius if let borderable = currentConfig as? Borderable {
layer.borderWidth = borderWidth layer.borderColor = borderable.borderColorConfig.getColor(self).cgColor
layer.borderWidth = borderable.borderWidth
icon.layer.borderWidth = borderable.borderWidth
} else {
layer.borderColor = nil
layer.borderWidth = 0
icon.layer.borderWidth = 0
}
//icon if let dropshadowable = currentConfig as? Dropshadowable {
icon.layer.borderWidth = borderWidth layer.masksToBounds = false
layer.shadowColor = dropshadowable.shadowColorConfig.getColor(self).cgColor
layer.shadowOpacity = Float(dropshadowable.shadowOpacity)
layer.shadowOffset = dropshadowable.shadowOffset
layer.shadowRadius = dropshadowable.shadowRadius
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
} else {
layer.shadowOpacity = 0
layer.shadowRadius = 0
layer.shadowPath = nil
}
} }
private struct GhostConfiguration: Configuration {
var kind: Kind = .ghost
var surfaceType: SurfaceType = .colorFill
var floating: Bool = false
var backgroundColorConfig: AnyColorable = {
SurfaceColorConfiguration(.clear, .clear).eraseToAnyColorable()
}()
}
private struct LowContrastColorFillConfiguration: Configuration {
var kind: Kind = .lowContrast
var surfaceType: SurfaceType = .colorFill
var floating: Bool = false
var backgroundColorConfig: AnyColorable = {
SurfaceColorConfiguration(VDSColor.paletteGray44.withAlphaComponent(0.94), .clear).eraseToAnyColorable()
}()
}
private struct LowContrastColorFillFloatingConfiguration: Configuration {
var kind: Kind = .lowContrast
var surfaceType: SurfaceType = .colorFill
var floating: Bool = true
var backgroundColorConfig: AnyColorable = {
SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, .clear).eraseToAnyColorable()
}()
}
private struct LowContrastMediaConfiguration: Configuration, Borderable {
var kind: Kind = .lowContrast
var surfaceType: SurfaceType = .media
var floating: Bool = false
var backgroundColorConfig: AnyColorable = {
SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, .clear).eraseToAnyColorable()
}()
var borderWidth: CGFloat = 1.0
var borderColorConfig: AnyColorable = {
SurfaceColorConfiguration(VDSColor.elementsLowcontrastOnlight, .clear).eraseToAnyColorable()
}()
}
private struct LowContrastMediaFloatingConfiguration: Configuration, Dropshadowable {
var kind: Kind = .lowContrast
var surfaceType: SurfaceType = .media
var floating: Bool = true
var backgroundColorConfig: AnyColorable = {
SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, .clear).eraseToAnyColorable()
}()
var shadowColorConfig: AnyColorable = {
SurfaceColorConfiguration(VDSColor.backgroundPrimaryLight, .clear).eraseToAnyColorable()
}()
var shadowOpacity: CGFloat = 0.5
var shadowOffset: CGSize = .init(width: 1, height: 1)
var shadowRadius: CGFloat = 2
}
private struct HighContrastConfiguration: Configuration {
var kind: Kind = .highContrast
var surfaceType: SurfaceType = .colorFill
var floating: Bool = false
var backgroundColorConfig: AnyColorable = {
return ControlColorConfiguration().with {
$0.setSurfaceColors(VDSColor.backgroundPrimaryDark, VDSColor.backgroundPrimaryLight, forState: .normal)
$0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted)
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
}.eraseToAnyColorable()
}()
}
private struct HighContrastFloatingConfiguration: Configuration {
var kind: Kind = .highContrast
var surfaceType: SurfaceType = .colorFill
var floating: Bool = true
var backgroundColorConfig: AnyColorable = {
return ControlColorConfiguration().with {
$0.setSurfaceColors(VDSColor.backgroundPrimaryLight, VDSColor.backgroundPrimaryLight, forState: .normal)
$0.setSurfaceColors(VDSColor.interactiveActiveOnlight, VDSColor.interactiveActiveOndark, forState: .highlighted)
$0.setSurfaceColors(VDSColor.interactiveDisabledOnlight, VDSColor.interactiveDisabledOndark, forState: .disabled)
}.eraseToAnyColorable()
}()
}
} }
// MARK: AppleGuidlinesTouchable // MARK: AppleGuidlinesTouchable
@ -188,4 +355,33 @@ extension ButtonIcon: AppleGuidlinesTouchable {
Self.acceptablyOutsideBounds(point: point, bounds: bounds) Self.acceptablyOutsideBounds(point: point, bounds: bounds)
} }
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
if view == icon {
return self
}
return view
}
}
private protocol Borderable {
var borderWidth: CGFloat { get set }
var borderColorConfig: AnyColorable { get set }
}
private protocol Dropshadowable {
var shadowColorConfig: AnyColorable { get set }
var shadowOpacity: CGFloat { get set }
var shadowOffset: CGSize { get set }
var shadowRadius: CGFloat { get set }
}
private protocol Configuration {
var kind: ButtonIcon.Kind { get set }
var surfaceType: ButtonIcon.SurfaceType { get set }
var floating: Bool { get set }
var backgroundColorConfig: AnyColorable { get set }
} }