Swiftgram/Display/KeyboardManager.swift
2017-03-21 19:58:45 +03:00

148 lines
6.0 KiB
Swift

import Foundation
import AsyncDisplayKit
struct KeyboardSurface {
let host: UIView
}
private func hasFirstResponder(_ view: UIView) -> Bool {
if view.isFirstResponder {
return true
} else {
for subview in view.subviews {
if hasFirstResponder(subview) {
return true
}
}
return false
}
}
private func findKeyboardBackdrop(_ view: UIView) -> UIView? {
if NSStringFromClass(type(of: view)) == "UIKBInputBackdropView" {
return view
}
for subview in view.subviews {
if let result = findKeyboardBackdrop(subview) {
return result
}
}
return nil
}
class KeyboardManager {
private let host: StatusBarHost
private weak var previousPositionAnimationMirrorSource: CATracingLayer?
private weak var previousFirstResponderView: UIView?
var gestureRecognizer: MinimizeKeyboardGestureRecognizer? = nil
var minimized: Bool = false
var minimizedUpdated: (() -> Void)?
var updatedMinimizedBackdrop = false
var surfaces: [KeyboardSurface] = [] {
didSet {
self.updateSurfaces(oldValue)
}
}
init(host: StatusBarHost) {
self.host = host
}
private func updateSurfaces(_ previousSurfaces: [KeyboardSurface]) {
guard let keyboardWindow = self.host.keyboardWindow else {
return
}
if let keyboardView = self.host.keyboardView {
if self.minimized {
let normalizedHeight = floor(0.85 * keyboardView.frame.size.height)
let factor = normalizedHeight / keyboardView.frame.size.height
let scaleTransform = CATransform3DMakeScale(factor, factor, 1.0)
let horizontalOffset = (keyboardView.frame.size.width - keyboardView.frame.size.width * factor) / 2.0
let verticalOffset = (keyboardView.frame.size.height - keyboardView.frame.size.height * factor) / 2.0
let translate = CATransform3DMakeTranslation(horizontalOffset, verticalOffset, 0.0)
keyboardView.layer.sublayerTransform = CATransform3DConcat(scaleTransform, translate)
self.updatedMinimizedBackdrop = false
if let backdrop = findKeyboardBackdrop(keyboardView) {
let scale = CATransform3DMakeScale(1.0 / factor, 1.0, 0.0)
let translate = CATransform3DMakeTranslation(-horizontalOffset * (1.0 / factor), 0.0, 0.0)
backdrop.layer.sublayerTransform = CATransform3DConcat(scale, translate)
}
} else {
keyboardView.layer.sublayerTransform = CATransform3DIdentity
if !self.updatedMinimizedBackdrop {
if let backdrop = findKeyboardBackdrop(keyboardView) {
backdrop.layer.sublayerTransform = CATransform3DIdentity
}
self.updatedMinimizedBackdrop = true
}
}
}
if let gestureRecognizer = self.gestureRecognizer {
if keyboardWindow.gestureRecognizers == nil || !keyboardWindow.gestureRecognizers!.contains(gestureRecognizer) {
keyboardWindow.addGestureRecognizer(gestureRecognizer)
}
} else {
let gestureRecognizer = MinimizeKeyboardGestureRecognizer(target: self, action: #selector(self.minimizeGesture(_:)))
self.gestureRecognizer = gestureRecognizer
keyboardWindow.addGestureRecognizer(gestureRecognizer)
}
var firstResponderView: UIView?
for surface in surfaces {
if hasFirstResponder(surface.host) {
firstResponderView = surface.host
break
}
}
if let firstResponderView = firstResponderView {
let containerOrigin = firstResponderView.convert(CGPoint(), to: nil)
let horizontalTranslation = CATransform3DMakeTranslation(containerOrigin.x, 0.0, 0.0)
keyboardWindow.layer.sublayerTransform = horizontalTranslation
if let tracingLayer = firstResponderView.layer as? CATracingLayer {
if let previousPositionAnimationMirrorSource = self.previousPositionAnimationMirrorSource, previousPositionAnimationMirrorSource !== tracingLayer {
previousPositionAnimationMirrorSource.setPositionAnimationMirrorTarget(nil)
}
tracingLayer.setPositionAnimationMirrorTarget(keyboardWindow.layer)
self.previousPositionAnimationMirrorSource = tracingLayer
} else if let previousPositionAnimationMirrorSource = self.previousPositionAnimationMirrorSource {
previousPositionAnimationMirrorSource.setPositionAnimationMirrorTarget(nil)
self.previousPositionAnimationMirrorSource = nil
}
} else {
keyboardWindow.layer.sublayerTransform = CATransform3DIdentity
if let previousPositionAnimationMirrorSource = self.previousPositionAnimationMirrorSource {
previousPositionAnimationMirrorSource.setPositionAnimationMirrorTarget(nil)
self.previousPositionAnimationMirrorSource = nil
}
if let previousFirstResponderView = previousFirstResponderView {
if previousFirstResponderView.window == nil {
keyboardWindow.isHidden = true
keyboardWindow.layer.cancelAnimationsRecursive(key: "position")
keyboardWindow.layer.cancelAnimationsRecursive(key: "bounds")
keyboardWindow.isHidden = false
}
}
}
self.previousFirstResponderView = firstResponderView
}
@objc func minimizeGesture(_ recognizer: UISwipeGestureRecognizer) {
if case .ended = recognizer.state {
self.minimized = !self.minimized
self.minimizedUpdated?()
}
}
}