more changes
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
e91465f423
commit
facafa308b
@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import VDSColorTokens
|
||||
|
||||
@objc(VDSButtonIcon)
|
||||
open class ButtonIcon: Control {
|
||||
@ -19,7 +20,7 @@ open class ButtonIcon: Control {
|
||||
public enum Kind: String, CaseIterable {
|
||||
case ghost, lowContrast, highContrast
|
||||
}
|
||||
|
||||
|
||||
public enum SurfaceType: String, CaseIterable {
|
||||
case colorFill, media
|
||||
}
|
||||
@ -47,24 +48,85 @@ open class ButtonIcon: Control {
|
||||
private var centerYConstraint: NSLayoutConstraint?
|
||||
private var layoutGuideWidthConstraint: NSLayoutConstraint?
|
||||
private var layoutGuideHeightConstraint: NSLayoutConstraint?
|
||||
private var currentIconName: Icon.Name? {
|
||||
if let selectedIconName {
|
||||
return selectedIconName
|
||||
} else {
|
||||
return iconName
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public Properties
|
||||
//--------------------------------------------------
|
||||
open var icon = Icon()
|
||||
|
||||
|
||||
open var kind: Kind = .ghost { didSet { setNeedsUpdate() } }
|
||||
open var surfaceType: SurfaceType = .colorFill { 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 customSize: Int? { 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 iconOffset: CGPoint = .init(x: 0, y: 0) { didSet { setNeedsUpdate() } }
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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
|
||||
@ -84,25 +146,24 @@ open class ButtonIcon: Control {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func setup() {
|
||||
super.setup()
|
||||
|
||||
|
||||
//create a layoutGuide for the icon to key off of
|
||||
let iconLayoutGuide = UILayoutGuide()
|
||||
addLayoutGuide(iconLayoutGuide)
|
||||
|
||||
|
||||
//add the icon
|
||||
addSubview(icon)
|
||||
|
||||
|
||||
//determines the height/width of the icon
|
||||
layoutGuideWidthConstraint = iconLayoutGuide.widthAnchor.constraint(equalToConstant: size.containerSize)
|
||||
layoutGuideHeightConstraint = iconLayoutGuide.heightAnchor.constraint(equalToConstant: size.containerSize)
|
||||
|
||||
|
||||
//determines the center point of the icon
|
||||
centerXConstraint = icon.centerXAnchor.constraint(equalTo: iconLayoutGuide.centerXAnchor, constant: 0)
|
||||
centerYConstraint = icon.centerYAnchor.constraint(equalTo: iconLayoutGuide.centerYAnchor, constant: 0)
|
||||
|
||||
|
||||
//activate the constraints
|
||||
NSLayoutConstraint.activate([layoutGuideWidthConstraint!,
|
||||
layoutGuideHeightConstraint!,
|
||||
@ -130,10 +191,11 @@ open class ButtonIcon: Control {
|
||||
|
||||
open override func updateView() {
|
||||
super.updateView()
|
||||
|
||||
|
||||
//ensure there is an icon to set
|
||||
if let iconName {
|
||||
icon.name = iconName
|
||||
if let currentIconName {
|
||||
icon.name = currentIconName
|
||||
icon.color = iconColorConfig.getColor(self)
|
||||
icon.size = size.value
|
||||
icon.surface = surface
|
||||
icon.disabled = disabled
|
||||
@ -141,13 +203,8 @@ open class ButtonIcon: Control {
|
||||
} else {
|
||||
icon.reset()
|
||||
}
|
||||
|
||||
// colors
|
||||
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
|
||||
|
||||
icon.backgroundColor = .green
|
||||
|
||||
setNeedsLayout()
|
||||
}
|
||||
@ -155,30 +212,140 @@ open class ButtonIcon: Control {
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
let borderWidth = 2.0
|
||||
let cornerRadius = min(frame.width, frame.height) / 2.0
|
||||
let currentConfig = currentConfiguration
|
||||
|
||||
backgroundColor = currentConfig.backgroundColorConfig.getColor(self)
|
||||
|
||||
// calculate center point for child view with offset
|
||||
let childCenter = CGPoint(x: center.x + iconOffset.x, y: center.y + iconOffset.y)
|
||||
centerXConstraint?.constant = childCenter.x - center.x
|
||||
centerYConstraint?.constant = childCenter.y - center.y
|
||||
|
||||
// calculate the icon's container size ensuring the padding
|
||||
//updating current container size
|
||||
var iconLayoutSize = size.containerSize
|
||||
if let 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
|
||||
layoutGuideHeightConstraint?.constant = iconLayoutSize
|
||||
|
||||
//container
|
||||
layer.cornerRadius = cornerRadius
|
||||
layer.borderWidth = borderWidth
|
||||
//border
|
||||
if let borderable = currentConfig as? Borderable {
|
||||
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
|
||||
icon.layer.borderWidth = borderWidth
|
||||
if let dropshadowable = currentConfig as? Dropshadowable {
|
||||
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
|
||||
@ -188,4 +355,33 @@ extension ButtonIcon: AppleGuidlinesTouchable {
|
||||
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 }
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user