Swiftgram/Display/NativeWindowHostView.swift
2018-09-20 21:26:29 +01:00

300 lines
9.9 KiB
Swift

import Foundation
import SwiftSignalKit
private let orientationChangeDuration: Double = UIDevice.current.userInterfaceIdiom == .pad ? 0.4 : 0.3
private let defaultOrientations: UIInterfaceOrientationMask = {
if UIDevice.current.userInterfaceIdiom == .pad {
return .all
} else {
return .allButUpsideDown
}
}()
private class WindowRootViewController: UIViewController {
var presentController: ((UIViewController, PresentationSurfaceLevel, Bool, (() -> Void)?) -> Void)?
var transitionToSize: ((CGSize, Double) -> Void)?
var orientations: UIInterfaceOrientationMask = defaultOrientations {
didSet {
if oldValue != self.orientations {
if self.orientations == .portrait {
if UIDevice.current.orientation != .portrait {
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
} else {
UIViewController.attemptRotationToDeviceOrientation()
}
}
}
}
var gestureEdges: UIRectEdge = [] {
didSet {
if oldValue != self.gestureEdges {
if #available(iOSApplicationExtension 11.0, *) {
self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures()
}
}
}
}
var preferNavigationUIHidden: Bool = false {
didSet {
if oldValue != self.preferNavigationUIHidden {
if #available(iOSApplicationExtension 11.0, *) {
self.setNeedsUpdateOfHomeIndicatorAutoHidden()
}
}
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .default
}
override var prefersStatusBarHidden: Bool {
return false
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return orientations
}
override func preferredScreenEdgesDeferringSystemGestures() -> UIRectEdge {
return self.gestureEdges
}
override func prefersHomeIndicatorAutoHidden() -> Bool {
return self.preferNavigationUIHidden
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
UIView.performWithoutAnimation {
self.transitionToSize?(size, coordinator.transitionDuration)
}
}
override func loadView() {
self.view = UIView()
self.view.isOpaque = false
self.view.backgroundColor = nil
}
}
private final class NativeWindow: UIWindow, WindowHost {
var updateSize: ((CGSize) -> Void)?
var layoutSubviewsEvent: (() -> Void)?
var updateIsUpdatingOrientationLayout: ((Bool) -> Void)?
var updateToInterfaceOrientation: (() -> Void)?
var presentController: ((ViewController, PresentationSurfaceLevel) -> Void)?
var presentControllerInGlobalOverlay: ((_ controller: ViewController) -> Void)?
var hitTestImpl: ((CGPoint, UIEvent?) -> UIView?)?
var presentNativeImpl: ((UIViewController) -> Void)?
var invalidateDeferScreenEdgeGestureImpl: (() -> Void)?
var invalidatePreferNavigationUIHiddenImpl: (() -> Void)?
var cancelInteractiveKeyboardGesturesImpl: (() -> Void)?
var forEachControllerImpl: (((ViewController) -> Void) -> Void)?
override var frame: CGRect {
get {
return super.frame
} set(value) {
let sizeUpdated = super.frame.size != value.size
var frameTransition: ContainedViewLayoutTransition = .immediate
if #available(iOSApplicationExtension 9.0, *) {
let duration = UIView.inheritedAnimationDuration
if !duration.isZero {
frameTransition = .animated(duration: duration, curve: .easeInOut)
}
}
if sizeUpdated, case let .animated(duration, curve) = frameTransition {
let previousFrame = super.frame
super.frame = value
self.layer.animateFrame(from: previousFrame, to: value, duration: duration, timingFunction: curve.timingFunction)
} else {
super.frame = value
}
if sizeUpdated {
self.updateSize?(value.size)
}
}
}
override var bounds: CGRect {
get {
return super.bounds
}
set(value) {
let sizeUpdated = super.bounds.size != value.size
super.bounds = value
if sizeUpdated {
self.updateSize?(value.size)
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
if let gestureRecognizers = self.gestureRecognizers {
for recognizer in gestureRecognizers {
recognizer.delaysTouchesBegan = false
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.layoutSubviewsEvent?()
}
/*override func _update(toInterfaceOrientation arg1: Int32, duration arg2: Double, force arg3: Bool) {
self.updateIsUpdatingOrientationLayout?(true)
super._update(toInterfaceOrientation: arg1, duration: arg2, force: arg3)
self.updateIsUpdatingOrientationLayout?(false)
self.updateToInterfaceOrientation?()
}*/
func present(_ controller: ViewController, on level: PresentationSurfaceLevel) {
self.presentController?(controller, level)
}
func presentInGlobalOverlay(_ controller: ViewController) {
self.presentControllerInGlobalOverlay?(controller)
}
func presentNative(_ controller: UIViewController) {
self.presentNativeImpl?(controller)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return self.hitTestImpl?(point, event)
}
override func insertSubview(_ view: UIView, at index: Int) {
super.insertSubview(view, at: index)
}
override func addSubview(_ view: UIView) {
super.addSubview(view)
}
override func insertSubview(_ view: UIView, aboveSubview siblingSubview: UIView) {
if let transitionClass = NSClassFromString("UITransitionView"), view.isKind(of: transitionClass) {
super.insertSubview(view, aboveSubview: self.subviews.last!)
} else {
super.insertSubview(view, aboveSubview: siblingSubview)
}
}
func invalidateDeferScreenEdgeGestures() {
self.invalidateDeferScreenEdgeGestureImpl?()
}
func invalidatePreferNavigationUIHidden() {
self.invalidatePreferNavigationUIHiddenImpl?()
}
func cancelInteractiveKeyboardGestures() {
self.cancelInteractiveKeyboardGesturesImpl?()
}
func forEachController(_ f: (ViewController) -> Void) {
self.forEachControllerImpl?(f)
}
}
public func nativeWindowHostView() -> (UIWindow, WindowHostView) {
let window = NativeWindow(frame: UIScreen.main.bounds)
let rootViewController = WindowRootViewController()
window.rootViewController = rootViewController
rootViewController.viewWillAppear(false)
rootViewController.view.frame = CGRect(origin: CGPoint(), size: window.bounds.size)
rootViewController.viewDidAppear(false)
let hostView = WindowHostView(view: window, isRotating: {
return window.isRotating()
}, updateSupportedInterfaceOrientations: { orientations in
rootViewController.orientations = orientations
}, updateDeferScreenEdgeGestures: { edges in
rootViewController.gestureEdges = edges
}, updatePreferNavigationUIHidden: { value in
rootViewController.preferNavigationUIHidden = value
})
rootViewController.transitionToSize = { [weak hostView] size, duration in
hostView?.updateSize?(size, duration)
}
window.updateSize = { [weak hostView] size in
//hostView?.updateSize?(size)
assert(true)
}
window.layoutSubviewsEvent = { [weak hostView] in
hostView?.layoutSubviews?()
}
window.updateIsUpdatingOrientationLayout = { [weak hostView] value in
hostView?.isUpdatingOrientationLayout = value
}
window.updateToInterfaceOrientation = { [weak hostView] in
hostView?.updateToInterfaceOrientation?()
}
window.presentController = { [weak hostView] controller, level in
hostView?.present?(controller, level)
}
window.presentControllerInGlobalOverlay = { [weak hostView] controller in
hostView?.presentInGlobalOverlay?(controller)
}
window.presentNativeImpl = { [weak hostView] controller in
hostView?.presentNative?(controller)
}
window.hitTestImpl = { [weak hostView] point, event in
return hostView?.hitTest?(point, event)
}
window.invalidateDeferScreenEdgeGestureImpl = { [weak hostView] in
return hostView?.invalidateDeferScreenEdgeGesture?()
}
window.invalidatePreferNavigationUIHiddenImpl = { [weak hostView] in
return hostView?.invalidatePreferNavigationUIHidden?()
}
window.cancelInteractiveKeyboardGesturesImpl = { [weak hostView] in
hostView?.cancelInteractiveKeyboardGestures?()
}
window.forEachControllerImpl = { [weak hostView] f in
hostView?.forEachController?(f)
}
rootViewController.presentController = { [weak hostView] controller, level, animated, completion in
if let hostView = hostView {
hostView.present?(LegacyPresentedController(legacyController: controller, presentation: .custom), level)
completion?()
}
}
return (window, hostView)
}