Nicer toolbar buttons
This commit is contained in:
@@ -10,11 +10,14 @@ import UIKit
|
||||
class TitlebarView: UIView
|
||||
{
|
||||
private let titleLabelView = UILabel(frame: .zero)
|
||||
private let backgroundImageView = UIImageView(frame: .zero)
|
||||
private let backgroundView = GradientView(direction: .horizontal, colors: [
|
||||
UIColor(red: 0.101, green: 0.176, blue: 0.415, alpha: 1.0),
|
||||
UIColor(red: 0.153, green: 0.000, blue: 0.153, alpha: 1.0)
|
||||
])
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: .zero)
|
||||
addSubview(backgroundImageView)
|
||||
addSubview(backgroundView)
|
||||
addSubview(titleLabelView)
|
||||
|
||||
titleLabelView.textColor = .white
|
||||
@@ -23,7 +26,7 @@ class TitlebarView: UIView
|
||||
titleLabelView.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
|
||||
titleLabelView.font = UIFont.boldSystemFont(ofSize: 12.0)
|
||||
|
||||
backgroundImageView.alpha = 0.98
|
||||
backgroundView.alpha = 0.98
|
||||
}
|
||||
|
||||
func setTitle(_ title: String) {
|
||||
@@ -49,37 +52,10 @@ class TitlebarView: UIView
|
||||
}
|
||||
}
|
||||
|
||||
private func backgroundImageForSize(_ size: CGSize) -> UIImage? {
|
||||
var image: UIImage? = nil
|
||||
|
||||
UIGraphicsBeginImageContext(CGSize(width: size.width, height: 1.0))
|
||||
if let context = UIGraphicsGetCurrentContext() {
|
||||
let gradientColorsArray = [
|
||||
UIColor(red: 0.101, green: 0.176, blue: 0.415, alpha: 1.0).cgColor,
|
||||
UIColor(red: 0.153, green: 0.000, blue: 0.153, alpha: 1.0).cgColor
|
||||
]
|
||||
|
||||
if let gradient = CGGradient(colorsSpace: nil, colors: gradientColorsArray as CFArray, locations: nil) {
|
||||
context.drawLinearGradient(gradient, start: .zero, end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
|
||||
}
|
||||
|
||||
image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
backgroundImageView.frame = bounds
|
||||
backgroundView.frame = bounds
|
||||
titleLabelView.frame = bounds.avoiding(verticalInsets: safeAreaInsets).insetBy(dx: 8.0 + layoutMargins.left, dy: 0.0)
|
||||
|
||||
if let image = backgroundImageView.image, image.size == bounds.size {
|
||||
// No op
|
||||
} else {
|
||||
backgroundImageView.image = backgroundImageForSize(bounds.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
App/Titlebar and URL Bar/ToolbarButtonContainerView.swift
Normal file
47
App/Titlebar and URL Bar/ToolbarButtonContainerView.swift
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// ToolbarButtonContainerView.swift
|
||||
// App
|
||||
//
|
||||
// Created by James Magahern on 8/14/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ToolbarButtonContainerView: UIView
|
||||
{
|
||||
private var buttonViews: [UIView] = []
|
||||
|
||||
public var numberOfButtonViews: Int { buttonViews.count }
|
||||
|
||||
func addButtonView(_ button: UIView) {
|
||||
buttonViews.append(button)
|
||||
addSubview(button)
|
||||
setNeedsLayout()
|
||||
}
|
||||
|
||||
func removeAllButtonViews() {
|
||||
buttonViews.forEach { $0.removeFromSuperview() }
|
||||
buttonViews.removeAll()
|
||||
setNeedsLayout()
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
let width: CGFloat = buttonViews.reduce(0.0) { (result, button) -> CGFloat in
|
||||
return result + button.sizeThatFits(size).width
|
||||
}
|
||||
|
||||
return CGSize(width: width, height: size.height)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height))
|
||||
|
||||
for button in buttonViews {
|
||||
let buttonSize = button.sizeThatFits(bounds.size)
|
||||
buttonRect.size = CGSize(width: buttonSize.width, height: bounds.height)
|
||||
button.frame = buttonRect
|
||||
|
||||
buttonRect.origin.x += buttonRect.width
|
||||
}
|
||||
}
|
||||
}
|
||||
115
App/Titlebar and URL Bar/ToolbarView.swift
Normal file
115
App/Titlebar and URL Bar/ToolbarView.swift
Normal file
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// ToolbarView.swift
|
||||
// App
|
||||
//
|
||||
// Created by James Magahern on 8/14/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ToolbarView: UIView
|
||||
{
|
||||
var urlBar: URLBar? { didSet { containerView.addSubview(urlBar!) } }
|
||||
|
||||
var cancelButtonVisible: Bool = false { didSet { layoutSubviews() } }
|
||||
|
||||
let containerView = UIView(frame: .zero)
|
||||
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
|
||||
let cancelButton = UIButton(type: .system)
|
||||
|
||||
let leadingButtonsView = ToolbarButtonContainerView(frame: .zero)
|
||||
let trailingButtonsView = ToolbarButtonContainerView(frame: .zero)
|
||||
|
||||
convenience init()
|
||||
{
|
||||
self.init(frame: .zero)
|
||||
addSubview(backgroundView)
|
||||
addSubview(containerView)
|
||||
|
||||
containerView.addSubview(leadingButtonsView)
|
||||
containerView.addSubview(trailingButtonsView)
|
||||
|
||||
cancelButton.setTitle("Cancel", for: .normal)
|
||||
containerView.addSubview(cancelButton)
|
||||
|
||||
layer.masksToBounds = false
|
||||
layer.shadowColor = UIColor.black.cgColor
|
||||
layer.shadowOpacity = 0.2
|
||||
layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
||||
layer.shadowRadius = 1.5
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize
|
||||
{
|
||||
return CGSize(width: size.width, height: 44.0)
|
||||
}
|
||||
|
||||
override func layoutSubviews()
|
||||
{
|
||||
super.layoutSubviews()
|
||||
|
||||
backgroundView.frame = bounds
|
||||
|
||||
var containerBounds = bounds
|
||||
containerBounds.size.height -= safeAreaInsets.bottom
|
||||
containerView.frame = containerBounds
|
||||
containerView.frame = containerView.frame.insetBy(dx: 8.0, dy: 4.0)
|
||||
|
||||
let buttonContainerInset = UIEdgeInsets(top: 1.0, left: 0.0, bottom: 1.0, right: 0.0)
|
||||
|
||||
var urlBarPadding = CGFloat(8.0)
|
||||
var urlBarInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
|
||||
// Cancel button
|
||||
var cancelButtonSize = cancelButton.sizeThatFits(containerView.bounds.size)
|
||||
cancelButtonSize.width += (urlBarPadding * 2)
|
||||
cancelButton.frame = CGRect(origin: CGPoint(x: (containerView.bounds.maxX - cancelButtonSize.width), y: 0),
|
||||
size: CGSize(width: cancelButtonSize.width + urlBarPadding, height: containerView.bounds.height))
|
||||
|
||||
// Leading toolbar buttons
|
||||
if leadingButtonsView.numberOfButtonViews > 0 {
|
||||
let leadingContainerSize = leadingButtonsView.sizeThatFits(containerView.bounds.size)
|
||||
leadingButtonsView.frame = CGRect(origin: .zero, size: leadingContainerSize).inset(by: buttonContainerInset)
|
||||
urlBarInsets.left = urlBarPadding
|
||||
} else {
|
||||
leadingButtonsView.frame = .zero
|
||||
}
|
||||
|
||||
// Trailing toolbar buttons
|
||||
let trailingContainerSize = trailingButtonsView.sizeThatFits(containerView.bounds.size)
|
||||
trailingButtonsView.frame = CGRect(origin: CGPoint(x: (containerView.bounds.maxX - trailingContainerSize.width), y: 0), size: trailingContainerSize)
|
||||
trailingButtonsView.frame = trailingButtonsView.frame.inset(by: buttonContainerInset)
|
||||
urlBarInsets.right = urlBarPadding
|
||||
|
||||
var avoidingSize: CGSize = .zero
|
||||
if cancelButtonVisible {
|
||||
cancelButton.alpha = 1.0
|
||||
trailingButtonsView.alpha = 0.0
|
||||
|
||||
avoidingSize = cancelButtonSize
|
||||
} else {
|
||||
cancelButton.alpha = 0.0
|
||||
trailingButtonsView.alpha = 1.0
|
||||
|
||||
avoidingSize = trailingContainerSize
|
||||
}
|
||||
|
||||
if let urlBar = urlBar {
|
||||
let origin = CGPoint(
|
||||
x: leadingButtonsView.frame.maxX,
|
||||
y: 0.0
|
||||
)
|
||||
|
||||
urlBar.frame = CGRect(
|
||||
origin: origin,
|
||||
size: CGSize(
|
||||
width: containerView.bounds.width - avoidingSize.width - origin.x,
|
||||
height: trailingContainerSize.height
|
||||
)
|
||||
)
|
||||
|
||||
urlBar.frame = urlBar.frame.inset(by: urlBarInsets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,154 +7,20 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class ToolbarButtonView: UIView
|
||||
{
|
||||
private var buttonPadding = CGFloat(24.0)
|
||||
private var buttonViews: [UIView] = []
|
||||
|
||||
public var numberOfButtonViews: Int { buttonViews.count }
|
||||
|
||||
func addButtonView(_ button: UIView) {
|
||||
buttonViews.append(button)
|
||||
addSubview(button)
|
||||
setNeedsLayout()
|
||||
}
|
||||
|
||||
func removeAllButtonViews() {
|
||||
buttonViews.forEach { $0.removeFromSuperview() }
|
||||
buttonViews.removeAll()
|
||||
setNeedsLayout()
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
let width: CGFloat = buttonViews.reduce(buttonPadding / 2.0) { (result, button) -> CGFloat in
|
||||
return result + button.sizeThatFits(size).width + buttonPadding
|
||||
}
|
||||
|
||||
return CGSize(width: width, height: size.height)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height))
|
||||
|
||||
for button in buttonViews {
|
||||
let buttonSize = button.sizeThatFits(bounds.size)
|
||||
buttonRect.size = CGSize(width: buttonSize.width + buttonPadding, height: bounds.height)
|
||||
button.frame = buttonRect
|
||||
|
||||
buttonRect.origin.x += buttonRect.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarView: UIView
|
||||
{
|
||||
var urlBar: URLBar? { didSet { containerView.addSubview(urlBar!) } }
|
||||
|
||||
var cancelButtonVisible: Bool = false { didSet { layoutSubviews() } }
|
||||
|
||||
let containerView = UIView(frame: .zero)
|
||||
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
|
||||
let cancelButton = UIButton(type: .system)
|
||||
|
||||
let leadingButtonsView = ToolbarButtonView(frame: .zero)
|
||||
let trailingButtonsView = ToolbarButtonView(frame: .zero)
|
||||
|
||||
convenience init()
|
||||
{
|
||||
self.init(frame: .zero)
|
||||
addSubview(backgroundView)
|
||||
addSubview(containerView)
|
||||
|
||||
containerView.addSubview(leadingButtonsView)
|
||||
containerView.addSubview(trailingButtonsView)
|
||||
|
||||
cancelButton.setTitle("Cancel", for: .normal)
|
||||
containerView.addSubview(cancelButton)
|
||||
|
||||
layer.masksToBounds = false
|
||||
layer.shadowColor = UIColor.black.cgColor
|
||||
layer.shadowOpacity = 0.2
|
||||
layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
||||
layer.shadowRadius = 1.5
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize
|
||||
{
|
||||
return CGSize(width: size.width, height: 44.0)
|
||||
}
|
||||
|
||||
override func layoutSubviews()
|
||||
{
|
||||
super.layoutSubviews()
|
||||
|
||||
backgroundView.frame = bounds
|
||||
|
||||
var containerBounds = bounds
|
||||
containerBounds.size.height -= safeAreaInsets.bottom
|
||||
containerView.frame = containerBounds
|
||||
containerView.frame = containerView.frame.insetBy(dx: 8.0, dy: 4.0)
|
||||
|
||||
// Cancel button
|
||||
let urlBarPadding: CGFloat = 8.0
|
||||
var cancelButtonSize = cancelButton.sizeThatFits(containerView.bounds.size)
|
||||
cancelButtonSize.width += (urlBarPadding * 2)
|
||||
cancelButton.frame = CGRect(origin: CGPoint(x: (containerView.bounds.maxX - cancelButtonSize.width), y: 0),
|
||||
size: CGSize(width: cancelButtonSize.width + urlBarPadding, height: containerView.bounds.height))
|
||||
|
||||
// Leading toolbar buttons
|
||||
if leadingButtonsView.numberOfButtonViews > 0 {
|
||||
let leadingContainerSize = leadingButtonsView.sizeThatFits(containerView.bounds.size)
|
||||
leadingButtonsView.frame = CGRect(origin: .zero, size: leadingContainerSize)
|
||||
} else {
|
||||
leadingButtonsView.frame = .zero
|
||||
}
|
||||
|
||||
// Trailing toolbar buttons
|
||||
let trailingContainerSize = trailingButtonsView.sizeThatFits(containerView.bounds.size)
|
||||
trailingButtonsView.frame = CGRect(origin: CGPoint(x: (containerView.bounds.maxX - trailingContainerSize.width) + urlBarPadding, y: 0), size: trailingContainerSize)
|
||||
|
||||
var avoidingSize: CGSize = .zero
|
||||
if cancelButtonVisible {
|
||||
cancelButton.alpha = 1.0
|
||||
trailingButtonsView.alpha = 0.0
|
||||
|
||||
avoidingSize = cancelButtonSize
|
||||
} else {
|
||||
cancelButton.alpha = 0.0
|
||||
trailingButtonsView.alpha = 1.0
|
||||
|
||||
avoidingSize = trailingContainerSize
|
||||
}
|
||||
|
||||
if let urlBar = urlBar {
|
||||
let origin = CGPoint(
|
||||
x: leadingButtonsView.frame.maxX,
|
||||
y: 0.0
|
||||
)
|
||||
|
||||
urlBar.frame = CGRect(
|
||||
origin: origin,
|
||||
size: CGSize(
|
||||
width: containerView.bounds.width - avoidingSize.width - origin.x,
|
||||
height: containerView.bounds.height
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarViewController: UIViewController
|
||||
{
|
||||
let urlBar = URLBar()
|
||||
let toolbarView = ToolbarView()
|
||||
let scriptControllerIconView = ScriptControllerIconView()
|
||||
let shareButton = UIButton(frame: .zero)
|
||||
let darkModeButton = UIButton(frame: .zero)
|
||||
let windowButton = UIButton(frame: .zero)
|
||||
let backButton = UIButton(frame: .zero)
|
||||
let forwardButton = UIButton(frame: .zero)
|
||||
let newTabButton = UIButton(frame: .zero)
|
||||
|
||||
let shareButton = ReliefButton()
|
||||
let darkModeButton = ReliefButton()
|
||||
let windowButton = ReliefButton()
|
||||
let newTabButton = ReliefButton()
|
||||
|
||||
let backButton = ReliefButton()
|
||||
let forwardButton = ReliefButton()
|
||||
let navigationSegmentedButton = SegmentedReliefButton(children: [])
|
||||
|
||||
var darkModeEnabled: Bool = false {
|
||||
didSet {
|
||||
@@ -189,6 +55,9 @@ class ToolbarViewController: UIViewController
|
||||
// Forward button
|
||||
forwardButton.setImage(UIImage(systemName: "chevron.right"), for: .normal)
|
||||
|
||||
// Navigation control
|
||||
navigationSegmentedButton.children = [ backButton, forwardButton ]
|
||||
|
||||
// New tab button
|
||||
newTabButton.setImage(UIImage(systemName: "plus"), for: .normal)
|
||||
|
||||
@@ -214,18 +83,27 @@ class ToolbarViewController: UIViewController
|
||||
toolbarView.leadingButtonsView.removeAllButtonViews()
|
||||
toolbarView.trailingButtonsView.removeAllButtonViews()
|
||||
|
||||
let spacerView = { () -> SpacerView in
|
||||
SpacerView(space: 24.0)
|
||||
}
|
||||
|
||||
// Setup toolbar based on trait collection
|
||||
if traitCollection.horizontalSizeClass == .compact {
|
||||
toolbarView.trailingButtonsView.addButtonView(darkModeButton)
|
||||
toolbarView.trailingButtonsView.addButtonView(scriptControllerIconView)
|
||||
toolbarView.trailingButtonsView.addButtonView(windowButton)
|
||||
} else {
|
||||
toolbarView.leadingButtonsView.addButtonView(backButton)
|
||||
toolbarView.leadingButtonsView.addButtonView(forwardButton)
|
||||
toolbarView.leadingButtonsView.addButtonView(navigationSegmentedButton)
|
||||
toolbarView.leadingButtonsView.addButtonView(spacerView())
|
||||
|
||||
toolbarView.trailingButtonsView.addButtonView(shareButton)
|
||||
toolbarView.trailingButtonsView.addButtonView(spacerView())
|
||||
|
||||
toolbarView.trailingButtonsView.addButtonView(darkModeButton)
|
||||
toolbarView.trailingButtonsView.addButtonView(shareButton)
|
||||
toolbarView.trailingButtonsView.addButtonView(scriptControllerIconView)
|
||||
|
||||
toolbarView.trailingButtonsView.addButtonView(spacerView())
|
||||
|
||||
toolbarView.trailingButtonsView.addButtonView(newTabButton)
|
||||
toolbarView.trailingButtonsView.addButtonView(windowButton)
|
||||
}
|
||||
@@ -239,3 +117,16 @@ class ToolbarViewController: UIViewController
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
class SpacerView: UIView {
|
||||
internal var space: CGFloat = 0
|
||||
|
||||
convenience init(space: CGFloat) {
|
||||
self.init(frame: .zero)
|
||||
self.space = space
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
CGSize(width: space, height: size.height)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ class URLBar: UIView
|
||||
}
|
||||
|
||||
private let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
|
||||
private let shadowView = UIView(frame: .zero)
|
||||
private let progressIndicatorView = ProgressIndicatorView()
|
||||
private let fadeMaskView = UIImageView(frame: .zero)
|
||||
|
||||
@@ -40,7 +41,7 @@ class URLBar: UIView
|
||||
backgroundView.layer.masksToBounds = true
|
||||
backgroundView.layer.cornerRadius = backgroundCornerRadius
|
||||
backgroundView.layer.borderWidth = 1
|
||||
backgroundView.layer.borderColor = UIColor.systemFill.cgColor
|
||||
backgroundView.layer.borderColor = UIColor.secondarySystemFill.cgColor
|
||||
backgroundView.isUserInteractionEnabled = false
|
||||
addSubview(backgroundView)
|
||||
|
||||
@@ -166,6 +167,7 @@ class URLBar: UIView
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
backgroundView.frame = bounds
|
||||
shadowView.frame = bounds
|
||||
progressIndicatorView.frame = backgroundView.contentView.bounds
|
||||
textField.frame = bounds.insetBy(dx: 6.0, dy: 0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user