144 lines
5.7 KiB
Swift
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
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|