Nicer toolbar buttons
This commit is contained in:
72
App/Common UI/GradientView.swift
Normal file
72
App/Common UI/GradientView.swift
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// GradientView.swift
|
||||
// App
|
||||
//
|
||||
// Created by James Magahern on 8/14/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class GradientView: UIImageView
|
||||
{
|
||||
enum Direction {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
|
||||
var direction: Direction = .horizontal {
|
||||
didSet { image = nil; setNeedsLayout() }
|
||||
}
|
||||
|
||||
var colors: [UIColor] = [] {
|
||||
didSet { image = nil; setNeedsLayout() }
|
||||
}
|
||||
|
||||
private var generatedImageSize: CGSize?
|
||||
|
||||
convenience init(direction: Direction, colors: [UIColor]) {
|
||||
self.init(image: nil)
|
||||
self.direction = direction
|
||||
self.colors = colors
|
||||
}
|
||||
|
||||
private func gradientImage(forSize size: CGSize) -> UIImage? {
|
||||
var image: UIImage? = nil
|
||||
|
||||
UIGraphicsBeginImageContext(size)
|
||||
if let context = UIGraphicsGetCurrentContext() {
|
||||
let gradientColorsArray = self.colors.map { $0.cgColor }
|
||||
|
||||
if let gradient = CGGradient(colorsSpace: nil, colors: gradientColorsArray as CFArray, locations: nil) {
|
||||
if direction == .horizontal {
|
||||
context.drawLinearGradient(gradient, start: .zero, end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
|
||||
} else if direction == .vertical {
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: size.width / 2, y: 0), end: CGPoint(x: size.width / 2, y: size.height), options: CGGradientDrawingOptions())
|
||||
}
|
||||
}
|
||||
|
||||
image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
var needsNewImage: Bool = image == nil
|
||||
if let generatedImageSize = generatedImageSize {
|
||||
if generatedImageSize != bounds.size {
|
||||
needsNewImage = true
|
||||
}
|
||||
} else {
|
||||
needsNewImage = true
|
||||
}
|
||||
|
||||
if needsNewImage {
|
||||
image = gradientImage(forSize: bounds.size)
|
||||
generatedImageSize = bounds.size
|
||||
}
|
||||
}
|
||||
}
|
||||
130
App/Common UI/ReliefButton.swift
Normal file
130
App/Common UI/ReliefButton.swift
Normal file
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// ReliefButton.swift
|
||||
// App
|
||||
//
|
||||
// Created by James Magahern on 8/14/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ReliefButton: UIButton
|
||||
{
|
||||
internal let shadowView = UIView(frame: .zero)
|
||||
internal let backgroundView = GradientView(direction: .vertical, colors: ReliefButton.gradientColors(inverted: false, darkMode: false))
|
||||
|
||||
static let padding = CGFloat(24.0)
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
setBackgroundInverted(isHighlighted)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
|
||||
let cornerRadius = CGFloat(4.0)
|
||||
|
||||
self.tintColor = .init(dynamicProvider: { traitCollection -> UIColor in
|
||||
if traitCollection.userInterfaceStyle == .light {
|
||||
return .init(white: 0.15, alpha: 1.0)
|
||||
} else {
|
||||
return .white
|
||||
}
|
||||
})
|
||||
|
||||
shadowView.alpha = 0.28
|
||||
shadowView.backgroundColor = UIColor(white: 0.0, alpha: 1.0)
|
||||
shadowView.isUserInteractionEnabled = false
|
||||
shadowView.layer.shadowColor = UIColor.black.cgColor
|
||||
shadowView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
||||
shadowView.layer.shadowRadius = 1.0
|
||||
shadowView.layer.shadowOpacity = 1.0
|
||||
shadowView.layer.cornerRadius = cornerRadius
|
||||
shadowView.layer.masksToBounds = false
|
||||
shadowView.layer.shouldRasterize = true
|
||||
shadowView.layer.rasterizationScale = UIScreen.main.scale
|
||||
addSubview(shadowView)
|
||||
|
||||
backgroundView.layer.cornerRadius = cornerRadius
|
||||
backgroundView.isUserInteractionEnabled = false
|
||||
backgroundView.layer.masksToBounds = true
|
||||
backgroundView.layer.borderWidth = 1.0
|
||||
addSubview(backgroundView)
|
||||
|
||||
traitCollectionDidChange(nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
static func gradientColors(inverted: Bool, darkMode: Bool) -> [UIColor] {
|
||||
if darkMode {
|
||||
if !inverted {
|
||||
return [
|
||||
UIColor(white: 0.30, alpha: 1.0),
|
||||
UIColor(white: 0.10, alpha: 1.0)
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
UIColor(white: 0.10, alpha: 1.0),
|
||||
UIColor(white: 0.30, alpha: 1.0)
|
||||
]
|
||||
}
|
||||
} else {
|
||||
if !inverted {
|
||||
return [
|
||||
UIColor(white: 0.98, alpha: 1.0),
|
||||
UIColor(white: 0.93, alpha: 1.0)
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
UIColor(white: 0.88, alpha: 1.0),
|
||||
UIColor(white: 0.93, alpha: 1.0)
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
setBackgroundInverted(isHighlighted)
|
||||
backgroundView.layer.borderColor = { traitCollection -> UIColor in
|
||||
if traitCollection.userInterfaceStyle == .dark {
|
||||
return .init(white: 0.3, alpha: 1.0)
|
||||
} else {
|
||||
return .white
|
||||
}
|
||||
}(traitCollection).cgColor
|
||||
}
|
||||
|
||||
internal func setBackgroundInverted(_ inverted: Bool) {
|
||||
let darkMode: Bool = (traitCollection.userInterfaceStyle == .dark)
|
||||
backgroundView.colors = Self.gradientColors(inverted: inverted, darkMode: darkMode)
|
||||
}
|
||||
|
||||
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
|
||||
let inset = CGFloat(7.0)
|
||||
return contentRect.insetBy(dx: inset, dy: inset)
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
let ourSize = super.sizeThatFits(size)
|
||||
return CGSize(width: ourSize.width + Self.padding, height: ourSize.height)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
self.imageView?.contentMode = .scaleAspectFit
|
||||
|
||||
super.layoutSubviews()
|
||||
|
||||
sendSubviewToBack(backgroundView)
|
||||
sendSubviewToBack(shadowView)
|
||||
|
||||
let backgroundDimension = bounds.height - 1.0
|
||||
backgroundView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundDimension, height: backgroundDimension))
|
||||
backgroundView.frame = backgroundView.frame.centeredX(inRect: bounds)
|
||||
shadowView.frame = backgroundView.frame
|
||||
}
|
||||
}
|
||||
57
App/Common UI/SegmentedReliefButton.swift
Normal file
57
App/Common UI/SegmentedReliefButton.swift
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// SegmentedReliefButton.swift
|
||||
// App
|
||||
//
|
||||
// Created by James Magahern on 8/14/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SegmentedReliefButton: ReliefButton
|
||||
{
|
||||
var children: [ReliefButton] = [] {
|
||||
willSet { children.forEach { $0.removeFromSuperview() } }
|
||||
didSet { children.forEach { addSubview($0) }; setNeedsLayout() }
|
||||
}
|
||||
|
||||
init(children: [ReliefButton]) {
|
||||
super.init()
|
||||
self.children = children
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
let width: CGFloat = children.reduce(0.0) { (result, button) -> CGFloat in
|
||||
return result + button.sizeThatFits(size).width
|
||||
}
|
||||
|
||||
return CGSize(width: width, height: size.height)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
backgroundView.frame = bounds
|
||||
shadowView.frame = bounds
|
||||
|
||||
var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height))
|
||||
for child in children {
|
||||
child.shadowView.isHidden = true
|
||||
child.backgroundView.layer.borderWidth = 0
|
||||
bringSubviewToFront(child)
|
||||
|
||||
let childSize = child.sizeThatFits(bounds.size)
|
||||
buttonRect.size = CGSize(width: childSize.width, height: bounds.height)
|
||||
child.frame = buttonRect
|
||||
|
||||
buttonRect.origin.x += buttonRect.width
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user