Files
Attractor/App/Browser View/BrowserView.swift

224 lines
8.7 KiB
Swift
Raw Normal View History

//
// BrowserView.swift
// SBrowser
//
// Created by James Magahern on 7/21/20.
//
2020-07-24 19:26:35 -07:00
import Combine
import UIKit
import WebKit
class BrowserView: UIView
{
2020-07-29 14:16:25 -07:00
let titlebarView = TitlebarView()
2020-09-30 18:06:47 -07:00
var findOnPageView: FindOnPageView? {
didSet { addSubview(findOnPageView!) }
}
2020-07-24 19:26:35 -07:00
var toolbarView: ToolbarView? {
didSet { addSubview(toolbarView!) }
}
var tabBarView: TabBarView? {
didSet { addSubview(tabBarView!) }
}
var tabBarViewVisible: Bool = true {
didSet { setNeedsLayout() }
}
2020-09-21 18:09:00 -07:00
var autocompleteView: UIView? {
didSet {
addSubview(autocompleteView!)
if let toolbarView = toolbarView {
bringSubviewToFront(toolbarView)
}
}
2020-09-21 17:56:22 -07:00
}
var webView: WKWebView? {
2020-07-24 19:26:35 -07:00
didSet {
oldValue?.removeFromSuperview()
2020-07-24 19:26:35 -07:00
if let toolbarView = toolbarView {
insertSubview(webView!, belowSubview: toolbarView)
} else {
addSubview(webView!)
}
}
}
2020-07-24 19:26:35 -07:00
2020-09-30 18:06:47 -07:00
var findOnPageVisible: Bool = false {
didSet { layoutSubviews() }
}
func setFindOnPageVisible(_ visible: Bool, animated: Bool) {
if animated {
UIView.animate(withDuration: 0.33, animations: { self.findOnPageVisible = visible })
} else {
findOnPageVisible = visible
}
}
2020-07-24 19:26:35 -07:00
var keyboardWillShowObserver: AnyCancellable?
var keyboardWillHideObserver: AnyCancellable?
var keyboardLayoutOffset: CGFloat = 0 { didSet { setNeedsLayout() } }
convenience init() {
self.init(frame: .zero)
2020-07-29 14:16:25 -07:00
addSubview(titlebarView)
keyboardWillShowObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillShowNotification).sink { [weak self] notification in
self?.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!)
2020-07-24 19:26:35 -07:00
}
keyboardWillHideObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillHideNotification).sink { [weak self] notification in
self?.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!)
2020-07-24 19:26:35 -07:00
}
}
private func adjustOffsetForKeyboardNotification(userInfo: [AnyHashable : Any]) {
guard let keyboardEndFrame = userInfo[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect else { return }
guard let animationDuration = userInfo[UIWindow.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return }
guard let animationCurve = userInfo[UIWindow.keyboardAnimationCurveUserInfoKey] as? Int else { return }
let animationOptions: UIView.AnimationOptions = { curve -> UIView.AnimationOptions in
switch UIView.AnimationCurve(rawValue: curve) {
case .easeIn: return .curveEaseIn
case .easeOut: return .curveEaseOut
case .easeInOut: return .curveEaseInOut
default: return .init()
}
}(animationCurve)
keyboardLayoutOffset = bounds.height - keyboardEndFrame.minY
UIView.animate(withDuration: animationDuration, delay: 0.0, options: animationOptions, animations: { self.layoutIfNeeded() }, completion: nil)
}
override func layoutSubviews() {
super.layoutSubviews()
2020-07-24 19:26:35 -07:00
2020-07-29 18:34:46 -07:00
var webViewContentInset = UIEdgeInsets()
2020-07-29 14:16:25 -07:00
bringSubviewToFront(titlebarView)
var titlebarHeight: CGFloat = 24.0
titlebarHeight += safeAreaInsets.top
titlebarView.frame = CGRect(origin: .zero, size: CGSize(width: bounds.width, height: titlebarHeight))
2020-07-29 18:34:46 -07:00
webViewContentInset.top += titlebarView.frame.height
if let toolbarView = toolbarView {
var toolbarSize = toolbarView.sizeThatFits(bounds.size)
// Compact: toolbar is at the bottom
if traitCollection.horizontalSizeClass == .compact {
var bottomOffset: CGFloat = 0.0
if keyboardLayoutOffset == 0 {
toolbarSize.height += safeAreaInsets.bottom
} else if toolbarView.urlBar?.textField.isFirstResponder ?? false {
bottomOffset = keyboardLayoutOffset
}
toolbarView.bounds = CGRect(origin: .zero, size: toolbarSize)
toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - bottomOffset)
webViewContentInset.bottom += toolbarView.frame.height
2020-09-30 18:06:47 -07:00
if findOnPageVisible {
// Hide off the bottom
toolbarView.center = CGPoint(x: toolbarView.center.x, y: toolbarView.center.y + toolbarView.frame.height)
}
2020-07-29 18:34:46 -07:00
} else {
// Regular: toolbar is at the top
2020-07-29 19:24:05 -07:00
toolbarView.frame = CGRect(origin: CGPoint(x: 0.0, y: titlebarView.frame.maxY), size: toolbarSize)
2020-07-29 18:34:46 -07:00
webViewContentInset.top += toolbarView.frame.height
}
}
2020-07-29 14:16:25 -07:00
if let tabBarView = tabBarView {
bringSubviewToFront(tabBarView)
if tabBarViewVisible {
tabBarView.isHidden = false
let tabViewSize = tabBarView.sizeThatFits(bounds.size)
tabBarView.frame = CGRect(
x: 0.0, y: webViewContentInset.top,
width: bounds.width, height: tabViewSize.height
)
webViewContentInset.top += tabBarView.frame.height
} else {
tabBarView.isHidden = true
}
}
2020-07-29 14:16:25 -07:00
// Fix web view content insets
if let webView = webView {
2020-09-22 14:33:00 -07:00
webView.scrollView.layer.masksToBounds = true
webView.frame = bounds.inset(by: webViewContentInset)
}
2020-09-21 17:56:22 -07:00
// Autocomplete view
if let autocompleteView = autocompleteView {
// Compact: autocomplete view takes the space of the webview
autocompleteView.frame = bounds.inset(by: webViewContentInset)
2020-09-21 18:09:00 -07:00
2020-09-21 17:56:22 -07:00
if traitCollection.horizontalSizeClass == .regular {
// Regular: shows up just underneath the url bar
autocompleteView.layer.shadowColor = UIColor.black.cgColor
autocompleteView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
2020-09-21 18:09:00 -07:00
autocompleteView.layer.shadowRadius = 8.0
autocompleteView.layer.shadowOpacity = 0.6
2020-09-21 17:56:22 -07:00
autocompleteView.layer.cornerRadius = 8.0
2020-09-21 18:09:00 -07:00
autocompleteView.layer.masksToBounds = true
2020-09-21 17:56:22 -07:00
let shadowPath = UIBezierPath(roundedRect: autocompleteView.bounds,
cornerRadius: autocompleteView.layer.cornerRadius)
autocompleteView.layer.shadowPath = shadowPath.cgPath
2020-09-21 17:56:22 -07:00
if let toolbarView = toolbarView, let urlBar = toolbarView.urlBar {
var yOffset = toolbarView.frame.maxY + 3.0
if let tabBarView = tabBarView, tabBarViewVisible {
yOffset += tabBarView.frame.height
}
2020-09-21 17:56:22 -07:00
let urlFrame = self.convert(urlBar.frame, from: urlBar.superview)
autocompleteView.frame = CGRect(
x: urlFrame.minX,
y: yOffset,
2020-09-21 17:56:22 -07:00
width: urlFrame.width,
height: bounds.height / 2.5
)
}
} else {
autocompleteView.layer.cornerRadius = 0.0
autocompleteView.layer.shadowOpacity = 0.0
2020-09-21 17:56:22 -07:00
}
}
2020-09-30 18:06:47 -07:00
// Find on page view
if let findOnPageView = findOnPageView {
var bottomOffset: CGFloat = 0.0
var findOnPageSize = CGSize(width: bounds.width, height: 54.0)
if keyboardLayoutOffset == 0 {
findOnPageSize.height += safeAreaInsets.bottom
} else if findOnPageView.textField.isFirstResponder {
bottomOffset = keyboardLayoutOffset
}
findOnPageView.bounds = CGRect(origin: .zero, size: findOnPageSize)
findOnPageView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (findOnPageView.bounds.height / 2) - bottomOffset)
bringSubviewToFront(findOnPageView)
if !findOnPageVisible {
// Hide off the bottom
findOnPageView.center = CGPoint(x: findOnPageView.center.x, y: findOnPageView.center.y + findOnPageView.frame.height)
}
}
}
}