Files
OrderScheduling/Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UIKeyboardNotification.swift
DDIsFriend f0e8a1709d initial
2023-08-18 17:28:57 +08:00

366 lines
15 KiB
Swift

//
// IQKeyboardManager+UIKeyboardNotification.swift
// https://github.com/hackiftekhar/IQKeyboardManager
// Copyright (c) 2013-20 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// import Foundation - UIKit contains Foundation
import UIKit
// MARK: UIKeyboard Notifications
@available(iOSApplicationExtension, unavailable)
public extension IQKeyboardManager {
typealias SizeBlock = (_ size: CGSize) -> Void
private final class KeyboardSizeObserver: NSObject {
weak var observer: NSObject?
var sizeHandler: (_ size: CGSize) -> Void
init(observer: NSObject?, sizeHandler: @escaping (_ size: CGSize) -> Void) {
self.observer = observer
self.sizeHandler = sizeHandler
}
}
private struct AssociatedKeys {
static var keyboardSizeObservers = "keyboardSizeObservers"
static var keyboardLastNotifySize = "keyboardLastNotifySize"
static var keyboardShowing = "keyboardShowing"
static var keyboardShowNotification = "keyboardShowNotification"
static var keyboardFrame = "keyboardFrame"
static var animationDuration = "animationDuration"
static var animationCurve = "animationCurve"
}
private var keyboardLastNotifySize: CGSize {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.keyboardLastNotifySize) as? CGSize ?? .zero
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.keyboardLastNotifySize, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
private var keyboardSizeObservers: [AnyHashable: SizeBlock] {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.keyboardSizeObservers) as? [AnyHashable: SizeBlock] ?? [:]
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.keyboardSizeObservers, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
@objc func registerKeyboardSizeChange(identifier: AnyHashable, sizeHandler: @escaping SizeBlock) {
keyboardSizeObservers[identifier] = sizeHandler
}
@objc func unregisterKeyboardSizeChange(identifier: AnyHashable) {
keyboardSizeObservers[identifier] = nil
}
internal func notifyKeyboardSize(size: CGSize) {
guard !size.equalTo(keyboardLastNotifySize) else {
return
}
keyboardLastNotifySize = size
for block in keyboardSizeObservers.values {
block(size)
}
}
/**
Boolean to know if keyboard is showing.
*/
@objc private(set) var keyboardShowing: Bool {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.keyboardShowing) as? Bool ?? false
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.keyboardShowing, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/** To save keyboardWillShowNotification. Needed for enable keyboard functionality. */
internal var keyboardShowNotification: Notification? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.keyboardShowNotification) as? Notification
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.keyboardShowNotification, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/** To save keyboard rame. */
@objc private(set) var keyboardFrame: CGRect {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.keyboardFrame) as? CGRect ?? .zero
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.keyboardFrame, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/** To save keyboard animation duration. */
internal var animationDuration: TimeInterval {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.animationDuration) as? TimeInterval ?? 0.25
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.animationDuration, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/** To mimic the keyboard animation */
internal var animationCurve: UIView.AnimationOptions {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.animationCurve) as? UIView.AnimationOptions ?? .curveEaseOut
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.animationCurve, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
/* UIKeyboardWillShowNotification. */
@objc internal func keyboardWillShow(_ notification: Notification) {
keyboardShowNotification = notification
// Boolean to know keyboard is showing/hiding
keyboardShowing = true
let oldKBFrame = keyboardFrame
if let info = notification.userInfo {
// Getting keyboard animation.
if let curve = info[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt {
animationCurve = UIView.AnimationOptions(rawValue: curve).union(.beginFromCurrentState)
} else {
animationCurve = UIView.AnimationOptions.curveEaseOut.union(.beginFromCurrentState)
}
// Getting keyboard animation duration
animationDuration = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0.25
// Getting UIKeyboardSize.
if let kbFrame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
keyboardFrame = kbFrame
notifyKeyboardSize(size: keyboardFrame.size)
showLog("UIKeyboard Frame: \(keyboardFrame)")
}
}
guard privateIsEnabled() else {
restorePosition()
topViewBeginOrigin = IQKeyboardManager.kIQCGPointInvalid
return
}
let startTime = CACurrentMediaTime()
showLog("⌨️>>>>> \(#function) started >>>>>", indentation: 1)
showLog("Notification Object:\(notification.object ?? "NULL")")
// (Bug ID: #5)
if let textFieldView = textFieldView, topViewBeginOrigin.equalTo(IQKeyboardManager.kIQCGPointInvalid) {
// keyboard is not showing(At the beginning only). We should save rootViewRect.
rootViewController = textFieldView.parentContainerViewController()
if let controller = rootViewController {
if rootViewControllerWhilePopGestureRecognizerActive == controller {
topViewBeginOrigin = topViewBeginOriginWhilePopGestureRecognizerActive
} else {
topViewBeginOrigin = controller.view.frame.origin
}
rootViewControllerWhilePopGestureRecognizerActive = nil
topViewBeginOriginWhilePopGestureRecognizerActive = IQKeyboardManager.kIQCGPointInvalid
self.showLog("Saving \(controller) beginning origin: \(self.topViewBeginOrigin)")
}
}
//If last restored keyboard size is different(any orientation accure), then refresh. otherwise not.
if keyboardFrame.equalTo(oldKBFrame) == false {
//If textFieldView is inside UITableViewController then let UITableViewController to handle it (Bug ID: #37) (Bug ID: #76) See note:- https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html If it is UIAlertView textField then do not affect anything (Bug ID: #70).
if keyboardShowing,
let textFieldView = textFieldView,
textFieldView.isAlertViewTextField() == false {
// keyboard is already showing. adjust position.
optimizedAdjustPosition()
}
}
let elapsedTime = CACurrentMediaTime() - startTime
showLog("⌨️<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
}
/* UIKeyboardDidShowNotification. */
@objc internal func keyboardDidShow(_ notification: Notification) {
guard privateIsEnabled(),
let textFieldView = textFieldView,
let parentController = textFieldView.parentContainerViewController(), (parentController.modalPresentationStyle == UIModalPresentationStyle.formSheet || parentController.modalPresentationStyle == UIModalPresentationStyle.pageSheet),
textFieldView.isAlertViewTextField() == false else {
return
}
let startTime = CACurrentMediaTime()
showLog("⌨️>>>>> \(#function) started >>>>>", indentation: 1)
showLog("Notification Object:\(notification.object ?? "NULL")")
self.optimizedAdjustPosition()
let elapsedTime = CACurrentMediaTime() - startTime
showLog("⌨️<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
}
/* UIKeyboardWillHideNotification. So setting rootViewController to it's default frame. */
@objc internal func keyboardWillHide(_ notification: Notification?) {
//If it's not a fake notification generated by [self setEnable:NO].
if notification != nil {
keyboardShowNotification = nil
}
// Boolean to know keyboard is showing/hiding
keyboardShowing = false
if let info = notification?.userInfo {
// Getting keyboard animation.
if let curve = info[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt {
animationCurve = UIView.AnimationOptions(rawValue: curve).union(.beginFromCurrentState)
} else {
animationCurve = UIView.AnimationOptions.curveEaseOut.union(.beginFromCurrentState)
}
// Getting keyboard animation duration
animationDuration = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0.25
}
//If not enabled then do nothing.
guard privateIsEnabled() else {
return
}
let startTime = CACurrentMediaTime()
showLog("⌨️>>>>> \(#function) started >>>>>", indentation: 1)
showLog("Notification Object:\(notification?.object ?? "NULL")")
//Commented due to #56. Added all the conditions below to handle WKWebView's textFields. (Bug ID: #56)
// We are unable to get textField object while keyboard showing on WKWebView's textField. (Bug ID: #11)
// if (_textFieldView == nil) return
//Restoring the contentOffset of the lastScrollView
if let lastScrollView = lastScrollView {
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
if lastScrollView.contentInset != self.startingContentInsets {
self.showLog("Restoring contentInset to: \(self.startingContentInsets)")
lastScrollView.contentInset = self.startingContentInsets
lastScrollView.scrollIndicatorInsets = self.startingScrollIndicatorInsets
}
if lastScrollView.shouldRestoreScrollViewContentOffset, !lastScrollView.contentOffset.equalTo(self.startingContentOffset) {
self.showLog("Restoring contentOffset to: \(self.startingContentOffset)")
let animatedContentOffset = self.textFieldView?.superviewOfClassType(UIStackView.self, belowView: lastScrollView) != nil // (Bug ID: #1365, #1508, #1541)
if animatedContentOffset {
lastScrollView.setContentOffset(self.startingContentOffset, animated: UIView.areAnimationsEnabled)
} else {
lastScrollView.contentOffset = self.startingContentOffset
}
}
// TODO: restore scrollView state
// This is temporary solution. Have to implement the save and restore scrollView state
var superScrollView: UIScrollView? = lastScrollView
while let scrollView = superScrollView {
let contentSize = CGSize(width: max(scrollView.contentSize.width, scrollView.frame.width), height: max(scrollView.contentSize.height, scrollView.frame.height))
let minimumY = contentSize.height - scrollView.frame.height
if minimumY < scrollView.contentOffset.y {
let newContentOffset = CGPoint(x: scrollView.contentOffset.x, y: minimumY)
if scrollView.contentOffset.equalTo(newContentOffset) == false {
let animatedContentOffset = self.textFieldView?.superviewOfClassType(UIStackView.self, belowView: scrollView) != nil // (Bug ID: #1365, #1508, #1541)
if animatedContentOffset {
scrollView.setContentOffset(newContentOffset, animated: UIView.areAnimationsEnabled)
} else {
scrollView.contentOffset = newContentOffset
}
self.showLog("Restoring contentOffset to: \(self.startingContentOffset)")
}
}
superScrollView = scrollView.superviewOfClassType(UIScrollView.self) as? UIScrollView
}
})
}
restorePosition()
//Reset all values
lastScrollView = nil
keyboardFrame = CGRect.zero
notifyKeyboardSize(size: keyboardFrame.size)
startingContentInsets = UIEdgeInsets()
startingScrollIndicatorInsets = UIEdgeInsets()
startingContentOffset = CGPoint.zero
// topViewBeginRect = CGRectZero //Commented due to #82
let elapsedTime = CACurrentMediaTime() - startTime
showLog("⌨️<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
}
@objc internal func keyboardDidHide(_ notification: Notification) {
let startTime = CACurrentMediaTime()
showLog("⌨️>>>>> \(#function) started >>>>>", indentation: 1)
showLog("Notification Object:\(notification.object ?? "NULL")")
topViewBeginOrigin = IQKeyboardManager.kIQCGPointInvalid
keyboardFrame = CGRect.zero
notifyKeyboardSize(size: keyboardFrame.size)
let elapsedTime = CACurrentMediaTime() - startTime
showLog("⌨️<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
}
}