SegmentedReliefButton: actually show background states
This commit is contained in:
@@ -13,18 +13,29 @@ class ReliefButton: UIButton
|
|||||||
internal let backgroundView = GradientView(direction: .vertical, colors: ReliefButton.gradientColors(inverted: false, darkMode: false))
|
internal let backgroundView = GradientView(direction: .vertical, colors: ReliefButton.gradientColors(inverted: false, darkMode: false))
|
||||||
|
|
||||||
static let padding = CGFloat(24.0)
|
static let padding = CGFloat(24.0)
|
||||||
|
static let cornerRadius = CGFloat(4.0)
|
||||||
|
static let borderWidth = CGFloat(1.0)
|
||||||
|
|
||||||
override var isHighlighted: Bool {
|
override var isHighlighted: Bool {
|
||||||
didSet {
|
get {
|
||||||
|
super.isHighlighted || remainsPressed
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
super.isHighlighted = newValue
|
||||||
setBackgroundInverted(isHighlighted)
|
setBackgroundInverted(isHighlighted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var remainsPressed: Bool = false {
|
||||||
|
didSet {
|
||||||
|
self.isHighlighted = remainsPressed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
|
|
||||||
let cornerRadius = CGFloat(4.0)
|
|
||||||
|
|
||||||
self.tintColor = .init(dynamicProvider: { traitCollection -> UIColor in
|
self.tintColor = .init(dynamicProvider: { traitCollection -> UIColor in
|
||||||
if traitCollection.userInterfaceStyle == .light {
|
if traitCollection.userInterfaceStyle == .light {
|
||||||
return .init(white: 0.15, alpha: 1.0)
|
return .init(white: 0.15, alpha: 1.0)
|
||||||
@@ -40,16 +51,16 @@ class ReliefButton: UIButton
|
|||||||
shadowView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
shadowView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
||||||
shadowView.layer.shadowRadius = 1.0
|
shadowView.layer.shadowRadius = 1.0
|
||||||
shadowView.layer.shadowOpacity = 1.0
|
shadowView.layer.shadowOpacity = 1.0
|
||||||
shadowView.layer.cornerRadius = cornerRadius
|
shadowView.layer.cornerRadius = Self.cornerRadius
|
||||||
shadowView.layer.masksToBounds = false
|
shadowView.layer.masksToBounds = false
|
||||||
shadowView.layer.shouldRasterize = true
|
shadowView.layer.shouldRasterize = true
|
||||||
shadowView.layer.rasterizationScale = UIScreen.main.scale
|
shadowView.layer.rasterizationScale = UIScreen.main.scale
|
||||||
addSubview(shadowView)
|
addSubview(shadowView)
|
||||||
|
|
||||||
backgroundView.layer.cornerRadius = cornerRadius
|
backgroundView.layer.cornerRadius = Self.cornerRadius
|
||||||
backgroundView.isUserInteractionEnabled = false
|
backgroundView.isUserInteractionEnabled = false
|
||||||
backgroundView.layer.masksToBounds = true
|
backgroundView.layer.masksToBounds = true
|
||||||
backgroundView.layer.borderWidth = 1.0
|
backgroundView.layer.borderWidth = Self.borderWidth
|
||||||
addSubview(backgroundView)
|
addSubview(backgroundView)
|
||||||
|
|
||||||
traitCollectionDidChange(nil)
|
traitCollectionDidChange(nil)
|
||||||
@@ -131,5 +142,9 @@ class ReliefButton: UIButton
|
|||||||
backgroundView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundDimension, height: backgroundDimension))
|
backgroundView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundDimension, height: backgroundDimension))
|
||||||
backgroundView.frame = backgroundView.frame.centeredX(inRect: bounds)
|
backgroundView.frame = backgroundView.frame.centeredX(inRect: bounds)
|
||||||
shadowView.frame = backgroundView.frame
|
shadowView.frame = backgroundView.frame
|
||||||
|
|
||||||
|
// Offset by a small amount. Visual illusion caused by the shadow
|
||||||
|
backgroundView.frame = backgroundView.frame.offsetBy(dx: 0.0, dy: 1.0)
|
||||||
|
shadowView.frame = shadowView.frame.offsetBy(dx: 0.0, dy: 1.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,22 @@ class SegmentedReliefButton: ReliefButton
|
|||||||
didSet { children.forEach { addSubview($0) }; setNeedsLayout() }
|
didSet { children.forEach { addSubview($0) }; setNeedsLayout() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let backgroundMaskView = UIView(frame: .zero)
|
||||||
|
private let backgroundsContainerView = UIView(frame: .zero)
|
||||||
|
private var childrenHighlighObservations: [NSKeyValueObservation] = []
|
||||||
|
|
||||||
init(children: [ReliefButton]) {
|
init(children: [ReliefButton]) {
|
||||||
super.init()
|
super.init()
|
||||||
self.children = children
|
self.children = children
|
||||||
|
|
||||||
|
backgroundMaskView.backgroundColor = .black
|
||||||
|
backgroundMaskView.layer.masksToBounds = true
|
||||||
|
backgroundMaskView.layer.cornerRadius = Self.cornerRadius - Self.borderWidth
|
||||||
|
|
||||||
|
backgroundsContainerView.clipsToBounds = true
|
||||||
|
backgroundsContainerView.mask = backgroundMaskView
|
||||||
|
|
||||||
|
addSubview(backgroundsContainerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
override var isHighlighted: Bool {
|
override var isHighlighted: Bool {
|
||||||
@@ -28,23 +41,36 @@ class SegmentedReliefButton: ReliefButton
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||||
|
let ourSize = super.sizeThatFits(size)
|
||||||
let width: CGFloat = children.reduce(0.0) { (result, button) -> CGFloat in
|
let width: CGFloat = children.reduce(0.0) { (result, button) -> CGFloat in
|
||||||
return result + button.sizeThatFits(size).width
|
return result + button.sizeThatFits(size).width
|
||||||
}
|
}
|
||||||
|
|
||||||
return CGSize(width: width, height: size.height)
|
return CGSize(width: width, height: ourSize.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setBackgroundInverted(_ inverted: Bool) {
|
||||||
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
override func layoutSubviews() {
|
override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
backgroundView.colors = [ .clear ]
|
||||||
backgroundView.frame = bounds
|
backgroundView.frame = bounds
|
||||||
shadowView.frame = bounds
|
shadowView.frame = bounds
|
||||||
|
|
||||||
|
backgroundMaskView.frame = backgroundView.frame.insetBy(dx: 1.0, dy: 1.0)
|
||||||
|
backgroundsContainerView.frame = backgroundView.frame
|
||||||
|
|
||||||
|
childrenHighlighObservations.removeAll()
|
||||||
|
backgroundsContainerView.subviews.forEach { $0.removeFromSuperview() }
|
||||||
|
|
||||||
|
let darkMode = self.traitCollection.userInterfaceStyle == .dark
|
||||||
var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height))
|
var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height))
|
||||||
for child in children {
|
for (i, child) in children.enumerated() {
|
||||||
child.shadowView.isHidden = true
|
child.shadowView.isHidden = true
|
||||||
child.backgroundView.layer.borderWidth = 0
|
child.backgroundView.isHidden = true
|
||||||
bringSubviewToFront(child)
|
bringSubviewToFront(child)
|
||||||
|
|
||||||
let childSize = child.sizeThatFits(bounds.size)
|
let childSize = child.sizeThatFits(bounds.size)
|
||||||
@@ -52,6 +78,26 @@ class SegmentedReliefButton: ReliefButton
|
|||||||
child.frame = buttonRect
|
child.frame = buttonRect
|
||||||
|
|
||||||
buttonRect.origin.x += buttonRect.width
|
buttonRect.origin.x += buttonRect.width
|
||||||
|
|
||||||
|
// Background
|
||||||
|
let backgroundView = GradientView(direction: .vertical, colors: Self.gradientColors(inverted: false, darkMode: darkMode))
|
||||||
|
backgroundView.frame = child.frame
|
||||||
|
backgroundsContainerView.insertSubview(backgroundView, at: 0)
|
||||||
|
childrenHighlighObservations.append(child.observe(\.isHighlighted) { [backgroundView] (button, changeEvent) in
|
||||||
|
backgroundView.colors = Self.gradientColors(inverted: button.isHighlighted, darkMode: darkMode)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
if i < children.count - 1 {
|
||||||
|
let separatorView = UIView(frame: CGRect(
|
||||||
|
x: child.frame.maxX,
|
||||||
|
y: 0,
|
||||||
|
width: 1.0,
|
||||||
|
height: bounds.height
|
||||||
|
))
|
||||||
|
separatorView.backgroundColor = .systemFill
|
||||||
|
backgroundsContainerView.addSubview(separatorView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,19 +28,19 @@ class ScriptPolicyControl: UIControl
|
|||||||
segmentContainer.children = [ allowButton, denyButton ]
|
segmentContainer.children = [ allowButton, denyButton ]
|
||||||
addSubview(segmentContainer)
|
addSubview(segmentContainer)
|
||||||
|
|
||||||
|
allowButton.showsTouchWhenHighlighted = false
|
||||||
allowButton.addAction(UIAction(handler: { [unowned self] _ in
|
allowButton.addAction(UIAction(handler: { [unowned self] _ in
|
||||||
self.policyStatus = .allowed
|
self.policyStatus = .allowed
|
||||||
self.sendActions(for: .valueChanged)
|
self.sendActions(for: .valueChanged)
|
||||||
}), for: .touchUpInside)
|
}), for: .touchUpInside)
|
||||||
allowButton.imageView?.contentMode = .scaleAspectFit
|
allowButton.imageView?.contentMode = .scaleAspectFit
|
||||||
addSubview(allowButton)
|
|
||||||
|
|
||||||
|
denyButton.showsTouchWhenHighlighted = false
|
||||||
denyButton.addAction(UIAction(handler: { [unowned self] _ in
|
denyButton.addAction(UIAction(handler: { [unowned self] _ in
|
||||||
self.policyStatus = .blocked
|
self.policyStatus = .blocked
|
||||||
self.sendActions(for: .valueChanged)
|
self.sendActions(for: .valueChanged)
|
||||||
}), for: .touchUpInside)
|
}), for: .touchUpInside)
|
||||||
denyButton.imageView?.contentMode = .scaleAspectFit
|
denyButton.imageView?.contentMode = .scaleAspectFit
|
||||||
addSubview(denyButton)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
override var intrinsicContentSize: CGSize {
|
||||||
@@ -54,15 +54,19 @@ class ScriptPolicyControl: UIControl
|
|||||||
|
|
||||||
if policyStatus == .allowed {
|
if policyStatus == .allowed {
|
||||||
allowButton.tintColor = .blue
|
allowButton.tintColor = .blue
|
||||||
|
allowButton.remainsPressed = true
|
||||||
allowButton.setImage(UIImage(systemName: "play.circle.fill"), for: .normal)
|
allowButton.setImage(UIImage(systemName: "play.circle.fill"), for: .normal)
|
||||||
|
|
||||||
denyButton.tintColor = .darkGray
|
denyButton.tintColor = nil
|
||||||
|
denyButton.remainsPressed = false
|
||||||
denyButton.setImage(UIImage(systemName: "stop.circle"), for: .normal)
|
denyButton.setImage(UIImage(systemName: "stop.circle"), for: .normal)
|
||||||
} else {
|
} else {
|
||||||
allowButton.tintColor = .darkGray
|
allowButton.tintColor = nil
|
||||||
|
allowButton.remainsPressed = false
|
||||||
allowButton.setImage(UIImage(systemName: "play.circle"), for: .normal)
|
allowButton.setImage(UIImage(systemName: "play.circle"), for: .normal)
|
||||||
|
|
||||||
denyButton.tintColor = .red
|
denyButton.tintColor = .red
|
||||||
|
denyButton.remainsPressed = true
|
||||||
denyButton.setImage(UIImage(systemName: "stop.circle.fill"), for: .normal)
|
denyButton.setImage(UIImage(systemName: "stop.circle.fill"), for: .normal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user