Swiftgram/Display/ViewController.swift
2017-02-11 17:02:35 +03:00

247 lines
8.2 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
private func findCurrentResponder(_ view: UIView) -> UIResponder? {
if view.isFirstResponder {
return view
} else {
for subview in view.subviews {
if let result = findCurrentResponder(subview) {
return result
}
}
return nil
}
}
public enum ViewControllerPresentationAnimation {
case none
case modalSheet
}
open class ViewControllerPresentationArguments {
public let presentationAnimation: ViewControllerPresentationAnimation
public init(presentationAnimation: ViewControllerPresentationAnimation) {
self.presentationAnimation = presentationAnimation
}
}
@objc open class ViewController: UIViewController, ContainableController {
private var containerLayout = ContainerViewLayout()
private let presentationContext: PresentationContext
public private(set) var presentationArguments: Any?
private var _displayNode: ASDisplayNode?
public final var displayNode: ASDisplayNode {
get {
if let value = self._displayNode {
return value
}
else {
self.loadDisplayNode()
if self._displayNode == nil {
fatalError("displayNode should be initialized after loadDisplayNode()")
}
return self._displayNode!
}
}
set(value) {
self._displayNode = value
}
}
public final var isNodeLoaded: Bool {
return self._displayNode != nil
}
public let statusBar: StatusBar
public let navigationBar: NavigationBar
public var displayNavigationBar = true
private weak var activeInputViewCandidate: UIResponder?
private weak var activeInputView: UIResponder?
open var navigationHeight: CGFloat {
return self.navigationBar.frame.maxY
}
private let _ready = Promise<Bool>(true)
open var ready: Promise<Bool> {
return self._ready
}
private var scrollToTopView: ScrollToTopView?
public var scrollToTop: (() -> Void)? {
didSet {
if self.isViewLoaded {
self.updateScrollToTopView()
}
}
}
private func updateScrollToTopView() {
if self.scrollToTop != nil {
if let displayNode = self._displayNode , self.scrollToTopView == nil {
let scrollToTopView = ScrollToTopView(frame: CGRect(x: 0.0, y: -1.0, width: displayNode.frame.size.width, height: 1.0))
scrollToTopView.action = { [weak self] in
if let scrollToTop = self?.scrollToTop {
scrollToTop()
}
}
self.scrollToTopView = scrollToTopView
self.view.addSubview(scrollToTopView)
}
} else if let scrollToTopView = self.scrollToTopView {
scrollToTopView.removeFromSuperview()
self.scrollToTopView = nil
}
}
public init(navigationBar: NavigationBar = NavigationBar()) {
self.statusBar = StatusBar()
self.navigationBar = navigationBar
self.presentationContext = PresentationContext()
super.init(nibName: nil, bundle: nil)
self.navigationBar.backPressed = { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
self.navigationBar.item = self.navigationItem
self.automaticallyAdjustsScrollViewInsets = false
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
}
open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.containerLayout = layout
if !self.isViewLoaded {
self.loadView()
}
self.view.frame = CGRect(origin: self.view.frame.origin, size: layout.size)
if let _ = layout.statusBarHeight {
self.statusBar.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: 40.0))
}
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: max(0.0, statusBarHeight - 20.0)), size: CGSize(width: layout.size.width, height: 64.0))
if statusBarHeight.isLessThanOrEqualTo(0.0) {
navigationBarFrame.origin.y -= 20.0
navigationBarFrame.size.height = 20.0 + 32.0
}
if !self.displayNavigationBar {
navigationBarFrame.origin.y = -navigationBarFrame.size.height
}
transition.updateFrame(node: self.navigationBar, frame: navigationBarFrame)
self.presentationContext.containerLayoutUpdated(layout, transition: transition)
if let scrollToTopView = self.scrollToTopView {
scrollToTopView.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 10.0)
}
}
open override func loadView() {
self.view = self.displayNode.view
self.displayNode.addSubnode(self.navigationBar)
self.view.addSubview(self.statusBar.view)
self.presentationContext.view = self.view
}
open func loadDisplayNode() {
self.displayNode = ASDisplayNode()
self.displayNodeDidLoad()
}
open func displayNodeDidLoad() {
self.updateScrollToTopView()
}
public func requestLayout(transition: ContainedViewLayoutTransition) {
if self.isViewLoaded {
self.containerLayoutUpdated(self.containerLayout, transition: transition)
}
}
public func setDisplayNavigationBar(_ displayNavigtionBar: Bool, transition: ContainedViewLayoutTransition = .immediate) {
if displayNavigtionBar != self.displayNavigationBar {
self.displayNavigationBar = displayNavigtionBar
if let parent = self.parent as? TabBarController {
if parent.currentController === self {
parent.displayNavigationBar = displayNavigationBar
parent.requestLayout(transition: transition)
}
} else {
self.requestLayout(transition: transition)
}
}
}
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
preconditionFailure("use present(_:in)")
}
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
if let navigationController = self.navigationController as? NavigationController {
navigationController.dismiss(animated: flag, completion: completion)
} else {
super.dismiss(animated: flag, completion: completion)
}
}
private var window: Window? {
if let window = self.view.window as? Window {
return window
} else if let superwindow = self.view.window {
for subview in superwindow.subviews {
if let subview = subview as? Window {
return subview
}
}
}
return nil
}
public func present(_ controller: ViewController, in context: PresentationContextType, with arguments: Any? = nil) {
controller.presentationArguments = arguments
switch context {
case .current:
self.presentationContext.present(controller)
case .window:
self.window?.present(controller)
}
}
open override func viewWillDisappear(_ animated: Bool) {
self.activeInputViewCandidate = findCurrentResponder(self.view)
super.viewWillDisappear(animated)
}
open override func viewDidDisappear(_ animated: Bool) {
self.activeInputView = self.activeInputViewCandidate
super.viewDidDisappear(animated)
}
open override func viewDidAppear(_ animated: Bool) {
self.activeInputView = nil
super.viewDidAppear(animated)
}
}