Back/forward buttons, share button on iPad
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user