Files
Attractor/App/Browser View/BrowserView.swift
2020-09-21 18:09:00 -07:00

144 lines
5.7 KiB
Swift

//
// BrowserView.swift
// SBrowser
//
// Created by James Magahern on 7/21/20.
//
import Combine
import UIKit
import WebKit
class BrowserView: UIView
{
let titlebarView = TitlebarView()
var toolbarView: ToolbarView? {
didSet { addSubview(toolbarView!) }
}
var autocompleteView: UIView? {
didSet {
addSubview(autocompleteView!)
if let toolbarView = toolbarView {
bringSubviewToFront(toolbarView)
}
}
}
var webView: WKWebView? {
didSet {
oldValue?.removeFromSuperview()
if let toolbarView = toolbarView {
insertSubview(webView!, belowSubview: toolbarView)
} else {
addSubview(webView!)
}
}
}
var keyboardWillShowObserver: AnyCancellable?
var keyboardWillHideObserver: AnyCancellable?
var keyboardLayoutOffset: CGFloat = 0 { didSet { setNeedsLayout() } }
convenience init() {
self.init(frame: .zero)
addSubview(titlebarView)
keyboardWillShowObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillShowNotification).sink { [weak self] notification in
self?.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!)
}
keyboardWillHideObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillHideNotification).sink { [weak self] notification in
self?.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!)
}
}
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()
var webViewContentInset = UIEdgeInsets()
bringSubviewToFront(titlebarView)
var titlebarHeight: CGFloat = 24.0
titlebarHeight += safeAreaInsets.top
titlebarView.frame = CGRect(origin: .zero, size: CGSize(width: bounds.width, height: titlebarHeight))
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
} else {
// Regular: toolbar is at the top
toolbarView.frame = CGRect(origin: CGPoint(x: 0.0, y: titlebarView.frame.maxY), size: toolbarSize)
webViewContentInset.top += toolbarView.frame.height
}
}
// Fix web view content insets
if let webView = webView {
webView.scrollView.layer.masksToBounds = false // allow content to draw under titlebar/toolbar
webView.frame = bounds.inset(by: webViewContentInset)
}
// Autocomplete view
if let autocompleteView = autocompleteView {
// Compact: autocomplete view takes the space of the webview
autocompleteView.frame = bounds.inset(by: webViewContentInset)
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)
autocompleteView.layer.shadowRadius = 8.0
autocompleteView.layer.shadowOpacity = 0.6
autocompleteView.layer.cornerRadius = 8.0
autocompleteView.layer.masksToBounds = true
if let toolbarView = toolbarView, let urlBar = toolbarView.urlBar {
let urlFrame = self.convert(urlBar.frame, from: urlBar.superview)
autocompleteView.frame = CGRect(
x: urlFrame.minX,
y: toolbarView.frame.maxY + 3.0,
width: urlFrame.width,
height: bounds.height / 2.5
)
}
}
}
}
}