Skip to content

Instantly share code, notes, and snippets.

@Zeta611
Created August 14, 2019 06:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zeta611/89d2a7419b9153ce008274a02d073d98 to your computer and use it in GitHub Desktop.
Save Zeta611/89d2a7419b9153ce008274a02d073d98 to your computer and use it in GitHub Desktop.
[KeyboardAvoiding] #protocol #iOS
//
// KeyboardAvoiding.swift
//
// Created by Jay Lee on 08/14/2019.
// Copyright © 2019 Jay Lee <jaeho.lee@snu.ac.kr>
// This work is free. You can redistribute it and/or modify it under the
// terms of the Do What The Fuck You Want To Public License, Version 2,
// as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
//
import UIKit
protocol KeyboardAvoiding: UIViewController {
var keyboardAvoidingScrollView: UIScrollView { get }
var footerView: UIView? { get }
var activeField: UITextField? { get }
var activeFieldIsSubviewed: Bool { get }
var notificationCenter: NotificationCenter { get }
var keyboardObservers: [NSObjectProtocol] { get set }
}
extension KeyboardAvoiding {
var activeFieldIsSubviewed: Bool { return false }
var footerView: UIView? { return nil }
var notificationCenter: NotificationCenter { return .default }
func registerForKeyboardNotifications() {
keyboardObservers = [
notificationCenter.addObserver(
forName: UIResponder.keyboardWillShowNotification,
object: nil,
queue: nil,
using: keyboardWillShow),
notificationCenter.addObserver(
forName: UIResponder.keyboardDidShowNotification,
object: nil,
queue: nil,
using: keyboardDidShow),
notificationCenter.addObserver(
forName: UIResponder.keyboardWillHideNotification,
object: nil,
queue: nil,
using: keyboardWillHide),
]
}
func unregisterForKeyboardNotifications() {
keyboardObservers.forEach { notificationCenter.removeObserver($0) }
}
func keyboardWillShow(_ notification: Notification) {
guard
let userInfo = notification.userInfo as NSDictionary?,
let value = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
else { return }
let keyboardHeight = value.cgRectValue.size.height
let contentInset = UIEdgeInsets(
top: 0,
left: 0,
bottom: keyboardHeight - (footerView?.frame.height ?? 0),
right: 0)
keyboardAvoidingScrollView.contentInset = contentInset
keyboardAvoidingScrollView.scrollIndicatorInsets = contentInset
}
func keyboardDidShow(_ notification: Notification) {
guard
let userInfo = notification.userInfo as NSDictionary?,
let value = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue,
let activeField = activeField
else { return }
let keyboardHeight = value.cgRectValue.size.height
// If active text field is hidden by keyboard, scroll it so it's visible
var frame = view.frame
frame.size.height -= keyboardHeight - (footerView?.frame.height ?? 0)
let activeFieldOrigin: CGPoint
// If activeField is not a direct subview of the `view`, convert the
// `frame.origin` with respect to the `view`
if activeFieldIsSubviewed {
activeFieldOrigin = activeField.superview?
.convert(activeField.frame.origin, to: view) ?? activeField.frame.origin
} else {
activeFieldOrigin = activeField
.convert(activeField.frame.origin, to: view)
}
if !frame.contains(activeFieldOrigin) {
keyboardAvoidingScrollView
.scrollRectToVisible(activeField.frame, animated: true)
}
}
func keyboardWillHide(_ notification: Notification) {
keyboardAvoidingScrollView.contentInset = UIEdgeInsets.zero
keyboardAvoidingScrollView.scrollIndicatorInsets = UIEdgeInsets.zero
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment