Back/forward buttons, share button on iPad

This commit is contained in:
James Magahern
2020-07-31 14:08:10 -07:00
parent 6baf543da3
commit ad85c3dc23
6 changed files with 133 additions and 64 deletions

View File

@@ -6,6 +6,7 @@
//
import UIKit
import UniformTypeIdentifiers
class BrowserViewController: UIViewController, WKNavigationDelegate,
UITextFieldDelegate, ScriptPolicyViewControllerDelegate,
@@ -24,6 +25,8 @@ class BrowserViewController: UIViewController, WKNavigationDelegate,
private var titleObservation: NSKeyValueObservation?
private var loadingObservation: NSKeyValueObservation?
private var backButtonObservation: NSKeyValueObservation?
private var forwardButtonObservation: NSKeyValueObservation?
override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent }
@@ -41,18 +44,49 @@ class BrowserViewController: UIViewController, WKNavigationDelegate,
browserView.toolbarView = toolbarController.toolbarView
// Refresh button
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { [weak self] action in
guard let self = self else { return }
if self.webView.isLoading {
self.webView.stopLoading()
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { [webView] action in
if webView.isLoading {
webView.stopLoading()
} else {
self.webView.reload()
webView.reload()
}
}), for: .touchUpInside)
// Back button
toolbarController.backButton.addAction(UIAction(handler: { [webView] _ in
webView.goBack()
}), for: .touchUpInside)
// Forward button
toolbarController.forwardButton.addAction(UIAction(handler: { [webView] _ in
webView.goForward()
}), for: .touchUpInside)
// Share button
toolbarController.shareButton.addAction(UIAction(handler: { [unowned self, webView, toolbarController] _ in
if let url = webView.url {
let itemProvider = NSItemProvider(item: url as NSURL, typeIdentifier: UTType.url.identifier)
let config = UIActivityItemsConfiguration(itemProviders: [ itemProvider ])
config.metadataProvider = { metadataKey in
switch metadataKey {
case .title: return webView.title
case .messageBody: return webView.title
default: return nil
}
}
config.previewProvider = { index, intent, suggestedSize in
NSItemProvider(item: self.tab.favicon, typeIdentifier: UTType.image.identifier)
}
let activityController = UIActivityViewController(activityItemsConfiguration: config)
activityController.popoverPresentationController?.sourceView = toolbarController.shareButton
self.present(activityController, animated: true, completion: nil)
}
}), for: .touchUpInside)
// Script button
toolbarController.scriptControllerIconView.addAction(UIAction(handler: { [weak self] action in
guard let self = self else { return }
toolbarController.scriptControllerIconView.addAction(UIAction(handler: { [unowned self] action in
let hostOrigin = self.webView.url?.host ?? ""
let loadedScripts = self.tab.allowedScriptOrigins.union(self.tab.blockedScriptOrigins)
let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager,
@@ -70,15 +104,13 @@ class BrowserViewController: UIViewController, WKNavigationDelegate,
}), for: .touchUpInside)
// Dark mode button
toolbarController.darkModeButton.addAction(UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.tab.bridge.darkModeEnabled = !self.tab.bridge.darkModeEnabled
self.toolbarController.darkModeEnabled = self.tab.bridge.darkModeEnabled
toolbarController.darkModeButton.addAction(UIAction(handler: { [tab, toolbarController] _ in
tab.bridge.darkModeEnabled = !tab.bridge.darkModeEnabled
toolbarController.darkModeEnabled = tab.bridge.darkModeEnabled
}), for: .touchUpInside)
// Tabs button
toolbarController.windowButton.addAction(UIAction(handler: { [weak self] _ in
guard let self = self else { return }
toolbarController.windowButton.addAction(UIAction(handler: { [unowned self] _ in
let tabPickerController = TabPickerViewController(tabController: self.tabController)
tabPickerController.delegate = self
tabPickerController.selectedTab = self.tab
@@ -125,14 +157,25 @@ class BrowserViewController: UIViewController, WKNavigationDelegate,
// Load progress
updateLoadProgress(forWebView: webView)
loadingObservation = webView.observe(\.estimatedProgress) { [weak self] (webView, observedChange) in
self?.updateLoadProgress(forWebView: webView)
loadingObservation = webView.observe(\.estimatedProgress) { [unowned self] (webView, observedChange) in
self.updateLoadProgress(forWebView: webView)
}
// Title observer
updateTitleAndURL(forWebView: webView)
titleObservation = webView.observe(\.title, changeHandler: { [weak self] (webView, observedChange) in
self?.updateTitleAndURL(forWebView: webView)
titleObservation = webView.observe(\.title, changeHandler: { [unowned self] (webView, observedChange) in
self.updateTitleAndURL(forWebView: webView)
})
// Back/forward observer
toolbarController.backButton.isEnabled = webView.canGoBack
backButtonObservation = webView.observe(\.canGoBack, changeHandler: { [toolbarController] (webView, observedChange) in
toolbarController.backButton.isEnabled = webView.canGoBack
})
toolbarController.forwardButton.isEnabled = webView.canGoForward
forwardButtonObservation = webView.observe(\.canGoForward, changeHandler: { [toolbarController] (webView, observedChange) in
toolbarController.forwardButton.isEnabled = webView.canGoForward
})
// Script blocker button

View File

@@ -30,16 +30,16 @@ class ScriptPolicyControl: UIControl
convenience init() {
self.init(frame: .zero)
allowButton.addAction(UIAction(handler: { [weak self] _ in
self?.policyStatus = .allowed
self?.sendActions(for: .valueChanged)
allowButton.addAction(UIAction(handler: { [unowned self] _ in
self.policyStatus = .allowed
self.sendActions(for: .valueChanged)
}), for: .touchUpInside)
allowButton.imageView?.contentMode = .scaleAspectFit
addSubview(allowButton)
denyButton.addAction(UIAction(handler: { [weak self] _ in
self?.policyStatus = .blocked
self?.sendActions(for: .valueChanged)
denyButton.addAction(UIAction(handler: { [unowned self] _ in
self.policyStatus = .blocked
self.sendActions(for: .valueChanged)
}), for: .touchUpInside)
denyButton.imageView?.contentMode = .scaleAspectFit
addSubview(denyButton)

View File

@@ -102,8 +102,7 @@ class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate
let otherOriginScripts = loadedScripts.subtracting([ hostOrigin ])
let originItems = [ hostOrigin ] + otherOriginScripts
let switchCellRegistry = UICollectionView.CellRegistration<SwitchListCell, String> { [weak self] (listCell, indexPath, item) in
guard let self = self else { return }
let switchCellRegistry = UICollectionView.CellRegistration<SwitchListCell, String> { [unowned self] (listCell, indexPath, item) in
var config = listCell.defaultContentConfiguration()
if item == Self.enableScriptsForTabItem {
config.text = "Allow for Tab"
@@ -133,8 +132,7 @@ class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate
listCell.contentConfiguration = config
}
let scriptPolicyRegistry = UICollectionView.CellRegistration<ScriptPolicyControlListCell, String> { [weak self] (listCell, indexPath, item) in
guard let self = self else { return }
let scriptPolicyRegistry = UICollectionView.CellRegistration<ScriptPolicyControlListCell, String> { [unowned self] (listCell, indexPath, item) in
var config = listCell.defaultContentConfiguration()
config.text = item
@@ -196,8 +194,7 @@ class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate
self.collectionView = collectionView
title = "Script Origin Policy"
navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .done, primaryAction: UIAction(handler: { [weak self] action in
guard let self = self else { return }
navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .done, primaryAction: UIAction(handler: { [unowned self] action in
if self.didChangeScriptPolicy {
self.delegate?.didChangeScriptPolicy()
self.delegate?.setScriptsEnabledForTab(self.allowScriptsForTab)

View File

@@ -38,13 +38,12 @@ class TabPickerViewController: UIViewController, UICollectionViewDelegate
override func loadView() {
var listConfig = UICollectionLayoutListConfiguration(appearance: .grouped)
listConfig.trailingSwipeActionsConfigurationProvider = { [weak self] indexPath in
if self?.dataSource?.snapshot().numberOfItems ?? 0 <= 1 {
listConfig.trailingSwipeActionsConfigurationProvider = { [unowned self] indexPath in
if self.dataSource?.snapshot().numberOfItems ?? 0 <= 1 {
return nil
}
return UISwipeActionsConfiguration(actions: [ UIContextualAction(style: .destructive, title: "Close", handler: { [weak self] (action, view, completionHandler) in
guard let self = self else { return }
return UISwipeActionsConfiguration(actions: [ UIContextualAction(style: .destructive, title: "Close", handler: { [unowned self] (action, view, completionHandler) in
if let item = self.dataSource?.itemIdentifier(for: indexPath), var snapshot = self.dataSource?.snapshot() {
if let tab = self.tabController.tab(forIdentifier: item) {
self.delegate?.tabPicker(self, willCloseTab: tab)
@@ -60,8 +59,7 @@ class TabPickerViewController: UIViewController, UICollectionViewDelegate
let listLayout = UICollectionViewCompositionalLayout.list(using: listConfig)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout)
let registry = UICollectionView.CellRegistration<UICollectionViewListCell, TabID> { [weak self] (listCell, indexPath, item) in
guard let self = self else { return }
let registry = UICollectionView.CellRegistration<UICollectionViewListCell, TabID> { [unowned self] (listCell, indexPath, item) in
var config = listCell.defaultContentConfiguration()
if let tab = self.tabController.tab(forIdentifier: item) {
@@ -108,9 +106,7 @@ class TabPickerViewController: UIViewController, UICollectionViewDelegate
self.collectionView = collectionView
self.view = self.collectionView
let newTabButton = UIBarButtonItem(systemItem: .add, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
let newTabButton = UIBarButtonItem(systemItem: .add, primaryAction: UIAction(handler: { [unowned self] _ in
let newTab = self.tabController.createNewTab()
self.delegate?.tabPicker(self, didSelectTab: newTab)
}), menu: nil)

View File

@@ -12,6 +12,8 @@ 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)
@@ -54,16 +56,19 @@ class ToolbarView: UIView
let containerView = UIView(frame: .zero)
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
let buttonsView = ToolbarButtonView(frame: .zero)
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(buttonsView)
containerView.addSubview(leadingButtonsView)
containerView.addSubview(trailingButtonsView)
cancelButton.setTitle("Cancel", for: .normal)
containerView.addSubview(cancelButton)
@@ -97,25 +102,44 @@ class ToolbarView: UIView
cancelButton.frame = CGRect(origin: CGPoint(x: (containerView.bounds.maxX - cancelButtonSize.width), y: 0),
size: CGSize(width: cancelButtonSize.width, height: containerView.bounds.height))
// Toolbar buttons
let buttonContainerSize = buttonsView.sizeThatFits(containerView.bounds.size)
buttonsView.frame = CGRect(origin: CGPoint(x: (containerView.bounds.maxX - buttonContainerSize.width) + urlBarPadding, y: 0), size: buttonContainerSize)
// 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
buttonsView.alpha = 0.0
trailingButtonsView.alpha = 0.0
avoidingSize = cancelButtonSize
} else {
cancelButton.alpha = 0.0
buttonsView.alpha = 1.0
trailingButtonsView.alpha = 1.0
avoidingSize = buttonContainerSize
avoidingSize = trailingContainerSize
}
if let urlBar = urlBar {
urlBar.frame = CGRect(origin: .zero, size: CGSize(width: containerView.bounds.width - avoidingSize.width - urlBarPadding, height: containerView.bounds.height))
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
)
)
}
}
}
@@ -128,6 +152,8 @@ class ToolbarViewController: UIViewController
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)
var darkModeEnabled: Bool = false {
didSet {
@@ -156,16 +182,21 @@ class ToolbarViewController: UIViewController
// Window button
windowButton.setImage(UIImage(systemName: "rectangle.on.rectangle"), for: .normal)
// Back button
backButton.setImage(UIImage(systemName: "chevron.left"), for: .normal)
// Forward button
forwardButton.setImage(UIImage(systemName: "chevron.right"), for: .normal)
let toolbarAnimationDuration: TimeInterval = 0.3
urlBar.textField.addAction(.init(handler: { [weak self] _ in
guard let self = self else { return }
urlBar.textField.addAction(.init(handler: { [toolbarView, urlBar] _ in
UIView.animate(withDuration: toolbarAnimationDuration) {
self.toolbarView.cancelButtonVisible = self.urlBar.textField.isFirstResponder
toolbarView.cancelButtonVisible = urlBar.textField.isFirstResponder
}
}), for: [ .editingDidBegin, .editingDidEnd ])
toolbarView.cancelButton.addAction(.init(handler: { [weak self] action in
self?.urlBar.textField.resignFirstResponder()
toolbarView.cancelButton.addAction(.init(handler: { [urlBar] action in
urlBar.textField.resignFirstResponder()
}), for: .touchUpInside)
traitCollectionDidChange(nil)
@@ -174,18 +205,22 @@ class ToolbarViewController: UIViewController
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
toolbarView.buttonsView.removeAllButtonViews()
toolbarView.leadingButtonsView.removeAllButtonViews()
toolbarView.trailingButtonsView.removeAllButtonViews()
// Setup toolbar based on trait collection
if traitCollection.horizontalSizeClass == .compact {
toolbarView.buttonsView.addButtonView(darkModeButton)
toolbarView.buttonsView.addButtonView(scriptControllerIconView)
toolbarView.buttonsView.addButtonView(windowButton)
toolbarView.trailingButtonsView.addButtonView(darkModeButton)
toolbarView.trailingButtonsView.addButtonView(scriptControllerIconView)
toolbarView.trailingButtonsView.addButtonView(windowButton)
} else {
toolbarView.buttonsView.addButtonView(darkModeButton)
toolbarView.buttonsView.addButtonView(shareButton)
toolbarView.buttonsView.addButtonView(scriptControllerIconView)
toolbarView.buttonsView.addButtonView(windowButton)
toolbarView.leadingButtonsView.addButtonView(backButton)
toolbarView.leadingButtonsView.addButtonView(forwardButton)
toolbarView.trailingButtonsView.addButtonView(darkModeButton)
toolbarView.trailingButtonsView.addButtonView(shareButton)
toolbarView.trailingButtonsView.addButtonView(scriptControllerIconView)
toolbarView.trailingButtonsView.addButtonView(windowButton)
}
}

View File

@@ -54,10 +54,8 @@ class URLBar: UIView
textField.clearButtonMode = .whileEditing
addSubview(textField)
textField.addAction(.init(handler: { [weak self] _ in
if let self = self {
self.refreshButton.isHidden = self.textField.isFirstResponder
}
textField.addAction(.init(handler: { [textField, refreshButton] _ in
refreshButton.isHidden = textField.isFirstResponder
}), for: [ .editingDidBegin, .editingDidEnd ])
refreshButton.tintColor = .secondaryLabel