Swiftgram/submodules/Display/Source/KeyboardManager.swift
Ilya Laktyushin e67b193c73 Various Fixes
2021-01-29 17:36:07 +03:00

178 lines
5.9 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import UIKitRuntimeUtils
struct KeyboardSurface {
let host: UIView
}
public extension UIResponder {
private struct Static {
static weak var responder: UIResponder?
}
static func currentFirst() -> UIResponder? {
Static.responder = nil
UIApplication.shared.sendAction(#selector(UIResponder._trap), to: nil, from: nil, for: nil)
return Static.responder
}
@objc private func _trap() {
Static.responder = self
}
}
private func getFirstResponder(_ view: UIView) -> UIView? {
if view.isFirstResponder {
return view
} else {
for subview in view.subviews {
if let result = getFirstResponder(subview) {
return result
}
}
return nil
}
}
class KeyboardManager {
private let host: StatusBarHost
private weak var previousFirstResponderView: UIView?
private var interactiveInputOffset: CGFloat = 0.0
var surfaces: [KeyboardSurface] = [] {
didSet {
self.updateSurfaces(oldValue)
}
}
init(host: StatusBarHost) {
self.host = host
}
func getCurrentKeyboardHeight() -> CGFloat {
guard let keyboardView = self.host.keyboardView else {
return 0.0
}
if !isViewVisibleInHierarchy(keyboardView) {
return 0.0
}
return keyboardView.bounds.height
}
func updateInteractiveInputOffset(_ offset: CGFloat, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
guard let keyboardView = self.host.keyboardView else {
return
}
self.interactiveInputOffset = offset
let previousBounds = keyboardView.bounds
let updatedBounds = CGRect(origin: CGPoint(x: 0.0, y: -offset), size: previousBounds.size)
keyboardView.layer.bounds = updatedBounds
if transition.isAnimated {
transition.animateOffsetAdditive(layer: keyboardView.layer, offset: previousBounds.minY - updatedBounds.minY, completion: completion)
} else {
completion()
}
//transition.updateSublayerTransformOffset(layer: keyboardView.layer, offset: CGPoint(x: 0.0, y: offset))
}
private func updateSurfaces(_ previousSurfaces: [KeyboardSurface]) {
guard let keyboardWindow = self.host.keyboardWindow else {
return
}
var firstResponderView: UIView?
var firstResponderDisableAutomaticKeyboardHandling: UIResponderDisableAutomaticKeyboardHandling = []
for surface in self.surfaces {
if let view = getFirstResponder(surface.host) {
firstResponderView = surface.host
firstResponderDisableAutomaticKeyboardHandling = view.disableAutomaticKeyboardHandling
break
}
}
if let firstResponderView = firstResponderView {
let containerOrigin = firstResponderView.convert(CGPoint(), to: nil)
var filteredTranslation = containerOrigin.x
if firstResponderDisableAutomaticKeyboardHandling.contains(.forward) {
filteredTranslation = max(0.0, filteredTranslation)
}
if firstResponderDisableAutomaticKeyboardHandling.contains(.backward) {
filteredTranslation = min(0.0, filteredTranslation)
}
let horizontalTranslation = CATransform3DMakeTranslation(filteredTranslation, 0.0, 0.0)
let currentTransform = keyboardWindow.layer.sublayerTransform
if !CATransform3DEqualToTransform(horizontalTranslation, currentTransform) {
//print("set to \(CGPoint(x: containerOrigin.x, y: self.interactiveInputOffset))")
keyboardWindow.layer.sublayerTransform = horizontalTranslation
}
} else {
keyboardWindow.layer.sublayerTransform = CATransform3DIdentity
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
}
}
private func endAnimations(view: UIView) {
view.layer.removeAllAnimations()
for subview in view.subviews {
endAnimations(view: subview)
}
}
func viewTreeContainsFirstResponder(view: UIView) -> Bool {
if view.isFirstResponder {
return true
} else {
for subview in view.subviews {
if viewTreeContainsFirstResponder(view: subview) {
return true
}
}
return false
}
}
final class KeyboardViewManager {
private let host: StatusBarHost
init(host: StatusBarHost) {
self.host = host
}
func dismissEditingWithoutAnimation(view: UIView) {
if viewTreeContainsFirstResponder(view: view) {
view.endEditing(true)
if let keyboardWindow = self.host.keyboardWindow {
for view in keyboardWindow.subviews {
endAnimations(view: view)
}
}
}
}
func update(leftEdge: CGFloat, transition: ContainedViewLayoutTransition) {
guard let keyboardWindow = self.host.keyboardWindow else {
return
}
let t = keyboardWindow.layer.sublayerTransform
let currentOffset = CGPoint(x: t.m41, y: t.m42)
transition.updateSublayerTransformOffset(layer: keyboardWindow.layer, offset: CGPoint(x: leftEdge, y: currentOffset.y), completion: { _ in
})
}
}