Skip to content

Instantly share code, notes, and snippets.

@grigorevp
Last active October 14, 2021 10:10
Show Gist options
  • Save grigorevp/c83f3154959c8efa8d3b547315f732fe to your computer and use it in GitHub Desktop.
Save grigorevp/c83f3154959c8efa8d3b547315f732fe to your computer and use it in GitHub Desktop.
Automatic keyboard appearance handling for UIKit
// UIScreenAdjuster.swift
// Created by Petr Grigorev
// This code could be used for automatic keyboard appearance handling.
// It automatically moves screen whenever a new text editing session from
// UITextField or UITextView is recognized.
//
// To start using it, just declare a constant inside your AppDelegate, like:
//
// private let screenAdjuster = UIScreenAdjuster()
import UserNotifications
import UIKit
class UIScreenAdjuster {
private let animationDuration: TimeInterval = 0.4
private let safeZoneRatio: CGFloat = 0.2
private var isKeyboardVisible = false
private var firstResponder: UIView?
private var topViewController: UIViewController? {
get {
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
if var topController = keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
return nil
}
}
private var currentOffset: CGFloat = 0.0
@objc private func keyboardWillShow(_ notification: Notification) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: { [weak self] in
if let self = self {
if let vc = self.topViewController, let firstResponder = self.firstResponder, let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if let frame = firstResponder.superview?.convert(firstResponder.frame, to: vc.view) {
let availableScreenHeight = vc.view.frame.height - keyboardSize.height
let minSafeY = availableScreenHeight * self.safeZoneRatio
let maxSafeY = availableScreenHeight - availableScreenHeight * self.safeZoneRatio
let tempCurrentOffset = self.currentOffset
let maxYDifference = frame.maxY - maxSafeY
if maxYDifference > 0 {
self.currentOffset -= maxYDifference
}
let minYDifference = minSafeY - frame.minY
if minYDifference > 0 {
self.currentOffset += minYDifference
}
self.currentOffset > 0 ? self.currentOffset = 0 : nil
self.currentOffset < -keyboardSize.height ? self.currentOffset = -keyboardSize.height : nil
UIView.animate(withDuration: self.animationDuration, animations: {
for view in vc.view.subviews {
view.frame.origin = CGPoint(x: view.frame.origin.x, y: view.frame.origin.y - tempCurrentOffset + self.currentOffset)
}
})
self.isKeyboardVisible = true
}
}
}
})
}
@objc private func keyboardWillHide(_ notification: Notification) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: { [weak self] in
if let self = self {
if let vc = self.topViewController {
UIView.animate(withDuration: self.animationDuration, animations: {
for view in vc.view.subviews {
view.frame.origin = CGPoint(x: view.frame.origin.x, y: view.frame.origin.y - self.currentOffset)
}
// vc.view.frame.origin = CGPoint(x: 0, y: self.currentOffset)
})
self.currentOffset = 0
// UIView.animate(withDuration: duration, animations: {
// vc.view.frame.origin = .zero
// })
//
self.isKeyboardVisible = false
}
}
})
}
@objc private func textDidBeginEditing(_ notification: Notification) {
if let view = notification.object as? UIView {
firstResponder = view
}
}
init() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(textDidBeginEditing(_:)), name: UITextField.textDidBeginEditingNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(textDidBeginEditing(_:)), name: UITextView.textDidBeginEditingNotification, object: nil)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment