initial
This commit is contained in:
57
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift
generated
Normal file
57
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQNSArray+Sort.swift
generated
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// IQNSArray+Sort.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
|
||||
|
||||
/**
|
||||
UIView.subviews sorting category.
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
internal extension Array where Element: UIView {
|
||||
|
||||
/**
|
||||
Returns the array by sorting the UIView's by their tag property.
|
||||
*/
|
||||
func sortedArrayByTag() -> [Element] {
|
||||
|
||||
return sorted(by: { (obj1: Element, obj2: Element) -> Bool in
|
||||
|
||||
return (obj1.tag < obj2.tag)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the array by sorting the UIView's by their tag property.
|
||||
*/
|
||||
func sortedArrayByPosition() -> [Element] {
|
||||
|
||||
return sorted(by: { (obj1: Element, obj2: Element) -> Bool in
|
||||
if obj1.frame.minY != obj2.frame.minY {
|
||||
return obj1.frame.minY < obj2.frame.minY
|
||||
} else {
|
||||
return obj1.frame.minX < obj2.frame.minX
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
117
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift
generated
Normal file
117
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIScrollView+Additions.swift
generated
Normal file
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// IQUIScrollView+Additions.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
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public extension UIScrollView {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var shouldIgnoreScrollingAdjustment = "shouldIgnoreScrollingAdjustment"
|
||||
static var shouldIgnoreContentInsetAdjustment = "shouldIgnoreContentInsetAdjustment"
|
||||
static var shouldRestoreScrollViewContentOffset = "shouldRestoreScrollViewContentOffset"
|
||||
}
|
||||
|
||||
/**
|
||||
If YES, then scrollview will ignore scrolling (simply not scroll it) for adjusting textfield position. Default is NO.
|
||||
*/
|
||||
var shouldIgnoreScrollingAdjustment: Bool {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.shouldIgnoreScrollingAdjustment) as? Bool ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.shouldIgnoreScrollingAdjustment, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
If YES, then scrollview will ignore content inset adjustment (simply not updating it) when keyboard is shown. Default is NO.
|
||||
*/
|
||||
var shouldIgnoreContentInsetAdjustment: Bool {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.shouldIgnoreContentInsetAdjustment) as? Bool ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.shouldIgnoreContentInsetAdjustment, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
To set customized distance from keyboard for textField/textView. Can't be less than zero
|
||||
*/
|
||||
var shouldRestoreScrollViewContentOffset: Bool {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.shouldRestoreScrollViewContentOffset) as? Bool ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.shouldRestoreScrollViewContentOffset, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
internal extension UITableView {
|
||||
|
||||
func previousIndexPath(of indexPath: IndexPath) -> IndexPath? {
|
||||
var previousRow = indexPath.row - 1
|
||||
var previousSection = indexPath.section
|
||||
|
||||
//Fixing indexPath
|
||||
if previousRow < 0 {
|
||||
previousSection -= 1
|
||||
if previousSection >= 0 {
|
||||
previousRow = self.numberOfRows(inSection: previousSection) - 1
|
||||
}
|
||||
}
|
||||
|
||||
if previousRow >= 0, previousSection >= 0 {
|
||||
return IndexPath(row: previousRow, section: previousSection)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
internal extension UICollectionView {
|
||||
|
||||
func previousIndexPath(of indexPath: IndexPath) -> IndexPath? {
|
||||
var previousRow = indexPath.row - 1
|
||||
var previousSection = indexPath.section
|
||||
|
||||
//Fixing indexPath
|
||||
if previousRow < 0 {
|
||||
previousSection -= 1
|
||||
if previousSection >= 0 {
|
||||
previousRow = self.numberOfItems(inSection: previousSection) - 1
|
||||
}
|
||||
}
|
||||
|
||||
if previousRow >= 0, previousSection >= 0 {
|
||||
return IndexPath(item: previousRow, section: previousSection)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
93
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift
generated
Normal file
93
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUITextFieldView+Additions.swift
generated
Normal file
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// IQUITextFieldView+Additions.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
|
||||
|
||||
/**
|
||||
Uses default keyboard distance for textField.
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
public let kIQUseDefaultKeyboardDistance = CGFloat.greatestFiniteMagnitude
|
||||
|
||||
/**
|
||||
UIView category for managing UITextField/UITextView
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public extension UIView {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var keyboardDistanceFromTextField = "keyboardDistanceFromTextField"
|
||||
static var ignoreSwitchingByNextPrevious = "ignoreSwitchingByNextPrevious"
|
||||
static var enableMode = "enableMode"
|
||||
static var shouldResignOnTouchOutsideMode = "shouldResignOnTouchOutsideMode"
|
||||
}
|
||||
|
||||
/**
|
||||
To set customized distance from keyboard for textField/textView. Can't be less than zero
|
||||
*/
|
||||
var keyboardDistanceFromTextField: CGFloat {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.keyboardDistanceFromTextField) as? CGFloat ?? kIQUseDefaultKeyboardDistance
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.keyboardDistanceFromTextField, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
If shouldIgnoreSwitchingByNextPrevious is true then library will ignore this textField/textView while moving to other textField/textView using keyboard toolbar next previous buttons. Default is false
|
||||
*/
|
||||
var ignoreSwitchingByNextPrevious: Bool {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.ignoreSwitchingByNextPrevious) as? Bool ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.ignoreSwitchingByNextPrevious, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Override Enable/disable managing distance between keyboard and textField behaviour for this particular textField.
|
||||
*/
|
||||
var enableMode: IQEnableMode {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.enableMode) as? IQEnableMode ?? .default
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.enableMode, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Override resigns Keyboard on touching outside of UITextField/View behaviour for this particular textField.
|
||||
*/
|
||||
var shouldResignOnTouchOutsideMode: IQEnableMode {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.shouldResignOnTouchOutsideMode) as? IQEnableMode ?? .default
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.shouldResignOnTouchOutsideMode, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
}
|
||||
324
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift
generated
Normal file
324
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIView+Hierarchy.swift
generated
Normal file
@@ -0,0 +1,324 @@
|
||||
//
|
||||
// IQUIView+Hierarchy.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 UIKit
|
||||
|
||||
/**
|
||||
UIView hierarchy category.
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public extension UIView {
|
||||
|
||||
// MARK: viewControllers
|
||||
|
||||
/**
|
||||
Returns the UIViewController object that manages the receiver.
|
||||
*/
|
||||
func viewContainingController() -> UIViewController? {
|
||||
|
||||
var nextResponder: UIResponder? = self
|
||||
|
||||
repeat {
|
||||
nextResponder = nextResponder?.next
|
||||
|
||||
if let viewController = nextResponder as? UIViewController {
|
||||
return viewController
|
||||
}
|
||||
|
||||
} while nextResponder != nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the topMost UIViewController object in hierarchy.
|
||||
*/
|
||||
func topMostController() -> UIViewController? {
|
||||
|
||||
var controllersHierarchy = [UIViewController]()
|
||||
|
||||
if var topController = window?.rootViewController {
|
||||
controllersHierarchy.append(topController)
|
||||
|
||||
while let presented = topController.presentedViewController {
|
||||
|
||||
topController = presented
|
||||
|
||||
controllersHierarchy.append(presented)
|
||||
}
|
||||
|
||||
var matchController: UIResponder? = viewContainingController()
|
||||
|
||||
while let mController = matchController as? UIViewController, controllersHierarchy.contains(mController) == false {
|
||||
|
||||
repeat {
|
||||
matchController = matchController?.next
|
||||
|
||||
} while matchController != nil && matchController is UIViewController == false
|
||||
}
|
||||
|
||||
return matchController as? UIViewController
|
||||
|
||||
} else {
|
||||
return viewContainingController()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the UIViewController object that is actually the parent of this object. Most of the time it's the viewController object which actually contains it, but result may be different if it's viewController is added as childViewController of another viewController.
|
||||
*/
|
||||
func parentContainerViewController() -> UIViewController? {
|
||||
|
||||
var matchController = viewContainingController()
|
||||
var parentContainerViewController: UIViewController?
|
||||
|
||||
if var navController = matchController?.navigationController {
|
||||
|
||||
while let parentNav = navController.navigationController {
|
||||
navController = parentNav
|
||||
}
|
||||
|
||||
var parentController: UIViewController = navController
|
||||
|
||||
while let parent = parentController.parent,
|
||||
(parent.isKind(of: UINavigationController.self) == false &&
|
||||
parent.isKind(of: UITabBarController.self) == false &&
|
||||
parent.isKind(of: UISplitViewController.self) == false) {
|
||||
|
||||
parentController = parent
|
||||
}
|
||||
|
||||
if navController == parentController {
|
||||
parentContainerViewController = navController.topViewController
|
||||
} else {
|
||||
parentContainerViewController = parentController
|
||||
}
|
||||
} else if let tabController = matchController?.tabBarController {
|
||||
|
||||
if let navController = tabController.selectedViewController as? UINavigationController {
|
||||
parentContainerViewController = navController.topViewController
|
||||
} else {
|
||||
parentContainerViewController = tabController.selectedViewController
|
||||
}
|
||||
} else {
|
||||
while let parentController = matchController?.parent,
|
||||
(parentController.isKind(of: UINavigationController.self) == false &&
|
||||
parentController.isKind(of: UITabBarController.self) == false &&
|
||||
parentController.isKind(of: UISplitViewController.self) == false) {
|
||||
|
||||
matchController = parentController
|
||||
}
|
||||
|
||||
parentContainerViewController = matchController
|
||||
}
|
||||
|
||||
let finalController = parentContainerViewController?.parentIQContainerViewController() ?? parentContainerViewController
|
||||
|
||||
return finalController
|
||||
|
||||
}
|
||||
|
||||
// MARK: Superviews/Subviews/Siglings
|
||||
|
||||
/**
|
||||
Returns the superView of provided class type.
|
||||
|
||||
|
||||
@param classType class type of the object which is to be search in above hierarchy and return
|
||||
|
||||
@param belowView view object in upper hierarchy where method should stop searching and return nil
|
||||
*/
|
||||
func superviewOfClassType(_ classType: UIView.Type, belowView: UIView? = nil) -> UIView? {
|
||||
|
||||
var superView = superview
|
||||
|
||||
while let unwrappedSuperView = superView {
|
||||
|
||||
if unwrappedSuperView.isKind(of: classType) {
|
||||
|
||||
//If it's UIScrollView, then validating for special cases
|
||||
if unwrappedSuperView.isKind(of: UIScrollView.self) {
|
||||
|
||||
let classNameString: String = "\(type(of: unwrappedSuperView.self))"
|
||||
|
||||
// If it's not UITableViewWrapperView class, this is internal class which is actually manage in UITableview. The speciality of this class is that it's superview is UITableView.
|
||||
// If it's not UITableViewCellScrollView class, this is internal class which is actually manage in UITableviewCell. The speciality of this class is that it's superview is UITableViewCell.
|
||||
//If it's not _UIQueuingScrollView class, actually we validate for _ prefix which usually used by Apple internal classes
|
||||
if unwrappedSuperView.superview?.isKind(of: UITableView.self) == false,
|
||||
unwrappedSuperView.superview?.isKind(of: UITableViewCell.self) == false,
|
||||
classNameString.hasPrefix("_") == false {
|
||||
return superView
|
||||
}
|
||||
} else {
|
||||
return superView
|
||||
}
|
||||
} else if unwrappedSuperView == belowView {
|
||||
return nil
|
||||
}
|
||||
|
||||
superView = unwrappedSuperView.superview
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
Returns all siblings of the receiver which canBecomeFirstResponder.
|
||||
*/
|
||||
internal func responderSiblings() -> [UIView] {
|
||||
|
||||
//Array of (UITextField/UITextView's).
|
||||
var tempTextFields = [UIView]()
|
||||
|
||||
// Getting all siblings
|
||||
if let siblings = superview?.subviews {
|
||||
for textField in siblings {
|
||||
if (textField == self || textField.ignoreSwitchingByNextPrevious == false), textField.IQcanBecomeFirstResponder() {
|
||||
tempTextFields.append(textField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempTextFields
|
||||
}
|
||||
|
||||
/**
|
||||
Returns all deep subViews of the receiver which canBecomeFirstResponder.
|
||||
*/
|
||||
internal func deepResponderViews() -> [UIView] {
|
||||
|
||||
//Array of (UITextField/UITextView's).
|
||||
var textfields = [UIView]()
|
||||
|
||||
for textField in subviews {
|
||||
|
||||
if (textField == self || textField.ignoreSwitchingByNextPrevious == false), textField.IQcanBecomeFirstResponder() {
|
||||
textfields.append(textField)
|
||||
}
|
||||
//Sometimes there are hidden or disabled views and textField inside them still recorded, so we added some more validations here (Bug ID: #458)
|
||||
//Uncommented else (Bug ID: #625)
|
||||
else if textField.subviews.count != 0, isUserInteractionEnabled, !isHidden, alpha != 0.0 {
|
||||
for deepView in textField.deepResponderViews() {
|
||||
textfields.append(deepView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//subviews are returning in opposite order. Sorting according the frames 'y'.
|
||||
return textfields.sorted(by: { (view1: UIView, view2: UIView) -> Bool in
|
||||
|
||||
let frame1 = view1.convert(view1.bounds, to: self)
|
||||
let frame2 = view2.convert(view2.bounds, to: self)
|
||||
|
||||
if frame1.minY != frame2.minY {
|
||||
return frame1.minY < frame2.minY
|
||||
} else {
|
||||
return frame1.minX < frame2.minX
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func IQcanBecomeFirstResponder() -> Bool {
|
||||
|
||||
var IQcanBecomeFirstResponder = false
|
||||
|
||||
if self.conforms(to: UITextInput.self) {
|
||||
// Setting toolbar to keyboard.
|
||||
if let textView = self as? UITextView {
|
||||
IQcanBecomeFirstResponder = textView.isEditable
|
||||
} else if let textField = self as? UITextField {
|
||||
IQcanBecomeFirstResponder = textField.isEnabled
|
||||
}
|
||||
}
|
||||
|
||||
if IQcanBecomeFirstResponder {
|
||||
IQcanBecomeFirstResponder = isUserInteractionEnabled && !isHidden && alpha != 0.0 && !isAlertViewTextField() && textFieldSearchBar() == nil
|
||||
}
|
||||
|
||||
return IQcanBecomeFirstResponder
|
||||
}
|
||||
|
||||
// MARK: Special TextFields
|
||||
|
||||
/**
|
||||
Returns searchBar if receiver object is UISearchBarTextField, otherwise return nil.
|
||||
*/
|
||||
internal func textFieldSearchBar() -> UISearchBar? {
|
||||
|
||||
var responder: UIResponder? = self.next
|
||||
|
||||
while let bar = responder {
|
||||
|
||||
if let searchBar = bar as? UISearchBar {
|
||||
return searchBar
|
||||
} else if bar is UIViewController {
|
||||
break
|
||||
}
|
||||
|
||||
responder = bar.next
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
Returns YES if the receiver object is UIAlertSheetTextField, otherwise return NO.
|
||||
*/
|
||||
internal func isAlertViewTextField() -> Bool {
|
||||
|
||||
var alertViewController: UIResponder? = viewContainingController()
|
||||
|
||||
var isAlertViewTextField = false
|
||||
|
||||
while let controller = alertViewController, !isAlertViewTextField {
|
||||
|
||||
if controller.isKind(of: UIAlertController.self) {
|
||||
isAlertViewTextField = true
|
||||
break
|
||||
}
|
||||
|
||||
alertViewController = controller.next
|
||||
}
|
||||
|
||||
return isAlertViewTextField
|
||||
}
|
||||
|
||||
private func depth() -> Int {
|
||||
var depth: Int = 0
|
||||
|
||||
if let superView = superview {
|
||||
depth = superView.depth()+1
|
||||
}
|
||||
|
||||
return depth
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
extension NSObject {
|
||||
|
||||
internal func _IQDescription() -> String {
|
||||
return "<\(self) \(Unmanaged.passUnretained(self).toOpaque())>"
|
||||
}
|
||||
}
|
||||
54
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift
generated
Normal file
54
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Categories/IQUIViewController+Additions.swift
generated
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// IQUIViewController+Additions.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 UIKit
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc extension UIViewController {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var IQLayoutGuideConstraint = "IQLayoutGuideConstraint"
|
||||
}
|
||||
|
||||
/**
|
||||
This method is provided to override by viewController's if the library lifts a viewController which you doesn't want to lift . This may happen if you have implemented side menu feature in your app and the library try to lift the side menu controller. Overriding this method in side menu class to return correct controller should fix the problem.
|
||||
*/
|
||||
open func parentIQContainerViewController() -> UIViewController? {
|
||||
return self
|
||||
}
|
||||
|
||||
/**
|
||||
To set customized distance from keyboard for textField/textView. Can't be less than zero
|
||||
|
||||
@deprecated Due to change in core-logic of handling distance between textField and keyboard distance, this layout contraint tweak is no longer needed and things will just work out of the box regardless of constraint pinned with safeArea/layoutGuide/superview
|
||||
*/
|
||||
@available(*, deprecated, message: "Due to change in core-logic of handling distance between textField and keyboard distance, this layout contraint tweak is no longer needed and things will just work out of the box regardless of constraint pinned with safeArea/layoutGuide/superview.")
|
||||
@IBOutlet public var IQLayoutGuideConstraint: NSLayoutConstraint? {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.IQLayoutGuideConstraint) as? NSLayoutConstraint
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.IQLayoutGuideConstraint, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift
generated
Normal file
150
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/Constants/IQKeyboardManagerConstants.swift
generated
Normal file
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// IQKeyboardManagerConstants.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
|
||||
|
||||
// MARK: IQAutoToolbarManageBehaviour
|
||||
|
||||
/**
|
||||
`IQAutoToolbarBySubviews`
|
||||
Creates Toolbar according to subview's hirarchy of Textfield's in view.
|
||||
|
||||
`IQAutoToolbarByTag`
|
||||
Creates Toolbar according to tag property of TextField's.
|
||||
|
||||
`IQAutoToolbarByPosition`
|
||||
Creates Toolbar according to the y,x position of textField in it's superview coordinate.
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public enum IQAutoToolbarManageBehaviour: Int {
|
||||
case bySubviews
|
||||
case byTag
|
||||
case byPosition
|
||||
}
|
||||
|
||||
/**
|
||||
`IQPreviousNextDisplayModeDefault`
|
||||
Show NextPrevious when there are more than 1 textField otherwise hide.
|
||||
|
||||
`IQPreviousNextDisplayModeAlwaysHide`
|
||||
Do not show NextPrevious buttons in any case.
|
||||
|
||||
`IQPreviousNextDisplayModeAlwaysShow`
|
||||
Always show nextPrevious buttons, if there are more than 1 textField then both buttons will be visible but will be shown as disabled.
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public enum IQPreviousNextDisplayMode: Int {
|
||||
case `default`
|
||||
case alwaysHide
|
||||
case alwaysShow
|
||||
}
|
||||
|
||||
/**
|
||||
`IQEnableModeDefault`
|
||||
Pick default settings.
|
||||
|
||||
`IQEnableModeEnabled`
|
||||
setting is enabled.
|
||||
|
||||
`IQEnableModeDisabled`
|
||||
setting is disabled.
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public enum IQEnableMode: Int {
|
||||
case `default`
|
||||
case enabled
|
||||
case disabled
|
||||
}
|
||||
|
||||
/*
|
||||
/---------------------------------------------------------------------------------------------------\
|
||||
\---------------------------------------------------------------------------------------------------/
|
||||
| iOS Notification Mechanism |
|
||||
/---------------------------------------------------------------------------------------------------\
|
||||
\---------------------------------------------------------------------------------------------------/
|
||||
|
||||
------------------------------------------------------------
|
||||
When UITextField become first responder
|
||||
------------------------------------------------------------
|
||||
- UITextFieldTextDidBeginEditingNotification (UITextField)
|
||||
- UIKeyboardWillShowNotification
|
||||
- UIKeyboardDidShowNotification
|
||||
|
||||
------------------------------------------------------------
|
||||
When UITextView become first responder
|
||||
------------------------------------------------------------
|
||||
- UIKeyboardWillShowNotification
|
||||
- UITextViewTextDidBeginEditingNotification (UITextView)
|
||||
- UIKeyboardDidShowNotification
|
||||
|
||||
------------------------------------------------------------
|
||||
When switching focus from UITextField to another UITextField
|
||||
------------------------------------------------------------
|
||||
- UITextFieldTextDidEndEditingNotification (UITextField1)
|
||||
- UITextFieldTextDidBeginEditingNotification (UITextField2)
|
||||
- UIKeyboardWillShowNotification
|
||||
- UIKeyboardDidShowNotification
|
||||
|
||||
------------------------------------------------------------
|
||||
When switching focus from UITextView to another UITextView
|
||||
------------------------------------------------------------
|
||||
- UITextViewTextDidEndEditingNotification: (UITextView1)
|
||||
- UIKeyboardWillShowNotification
|
||||
- UITextViewTextDidBeginEditingNotification: (UITextView2)
|
||||
- UIKeyboardDidShowNotification
|
||||
|
||||
------------------------------------------------------------
|
||||
When switching focus from UITextField to UITextView
|
||||
------------------------------------------------------------
|
||||
- UITextFieldTextDidEndEditingNotification (UITextField)
|
||||
- UIKeyboardWillShowNotification
|
||||
- UITextViewTextDidBeginEditingNotification (UITextView)
|
||||
- UIKeyboardDidShowNotification
|
||||
|
||||
------------------------------------------------------------
|
||||
When switching focus from UITextView to UITextField
|
||||
------------------------------------------------------------
|
||||
- UITextViewTextDidEndEditingNotification (UITextView)
|
||||
- UITextFieldTextDidBeginEditingNotification (UITextField)
|
||||
- UIKeyboardWillShowNotification
|
||||
- UIKeyboardDidShowNotification
|
||||
|
||||
------------------------------------------------------------
|
||||
When opening/closing UIKeyboard Predictive bar
|
||||
------------------------------------------------------------
|
||||
- UIKeyboardWillShowNotification
|
||||
- UIKeyboardDidShowNotification
|
||||
|
||||
------------------------------------------------------------
|
||||
On orientation change
|
||||
------------------------------------------------------------
|
||||
- UIApplicationWillChangeStatusBarOrientationNotification
|
||||
- UIKeyboardWillHideNotification
|
||||
- UIKeyboardDidHideNotification
|
||||
- UIApplicationDidChangeStatusBarOrientationNotification
|
||||
- UIKeyboardWillShowNotification
|
||||
- UIKeyboardDidShowNotification
|
||||
- UIKeyboardWillShowNotification
|
||||
- UIKeyboardDidShowNotification
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// IQKeyboardManagerConstantsInternal.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 UIKit
|
||||
111
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Debug.swift
generated
Normal file
111
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Debug.swift
generated
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// IQKeyboardManager+Debug.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: Debugging & Developer options
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
public extension IQKeyboardManager {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var enableDebugging = "enableDebugging"
|
||||
}
|
||||
|
||||
@objc var enableDebugging: Bool {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.enableDebugging) as? Bool ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.enableDebugging, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@warning Use below methods to completely enable/disable notifications registered by library internally.
|
||||
Please keep in mind that library is totally dependent on NSNotification of UITextField, UITextField, Keyboard etc.
|
||||
If you do unregisterAllNotifications then library will not work at all. You should only use below methods if you want to completedly disable all library functions.
|
||||
You should use below methods at your own risk.
|
||||
*/
|
||||
@objc func registerAllNotifications() {
|
||||
|
||||
// Registering for keyboard notification.
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow(_:)), name: UIResponder.keyboardDidShowNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide(_:)), name: UIResponder.keyboardDidHideNotification, object: nil)
|
||||
|
||||
// Registering for UITextField notification.
|
||||
registerTextFieldViewClass(UITextField.self, didBeginEditingNotificationName: UITextField.textDidBeginEditingNotification.rawValue, didEndEditingNotificationName: UITextField.textDidEndEditingNotification.rawValue)
|
||||
|
||||
// Registering for UITextView notification.
|
||||
registerTextFieldViewClass(UITextView.self, didBeginEditingNotificationName: UITextView.textDidBeginEditingNotification.rawValue, didEndEditingNotificationName: UITextView.textDidEndEditingNotification.rawValue)
|
||||
|
||||
// Registering for orientation changes notification
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.willChangeStatusBarOrientation(_:)), name: UIApplication.willChangeStatusBarOrientationNotification, object: UIApplication.shared)
|
||||
}
|
||||
|
||||
@objc func unregisterAllNotifications() {
|
||||
|
||||
// Unregistering for keyboard notification.
|
||||
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
|
||||
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidShowNotification, object: nil)
|
||||
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
|
||||
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidHideNotification, object: nil)
|
||||
|
||||
// Unregistering for UITextField notification.
|
||||
unregisterTextFieldViewClass(UITextField.self, didBeginEditingNotificationName: UITextField.textDidBeginEditingNotification.rawValue, didEndEditingNotificationName: UITextField.textDidEndEditingNotification.rawValue)
|
||||
|
||||
// Unregistering for UITextView notification.
|
||||
unregisterTextFieldViewClass(UITextView.self, didBeginEditingNotificationName: UITextView.textDidBeginEditingNotification.rawValue, didEndEditingNotificationName: UITextView.textDidEndEditingNotification.rawValue)
|
||||
|
||||
// Unregistering for orientation changes notification
|
||||
NotificationCenter.default.removeObserver(self, name: UIApplication.willChangeStatusBarOrientationNotification, object: UIApplication.shared)
|
||||
}
|
||||
|
||||
struct Static {
|
||||
static var indentation = 0
|
||||
}
|
||||
|
||||
internal func showLog(_ logString: String, indentation: Int = 0) {
|
||||
|
||||
guard enableDebugging else {
|
||||
return
|
||||
}
|
||||
|
||||
if indentation < 0 {
|
||||
Static.indentation = max(0, Static.indentation + indentation)
|
||||
}
|
||||
|
||||
var preLog = "IQKeyboardManager"
|
||||
for _ in 0 ... Static.indentation {
|
||||
preLog += "|\t"
|
||||
}
|
||||
|
||||
print(preLog + logString)
|
||||
|
||||
if indentation > 0 {
|
||||
Static.indentation += indentation
|
||||
}
|
||||
}
|
||||
}
|
||||
193
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Internal.swift
generated
Normal file
193
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Internal.swift
generated
Normal file
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// IQKeyboardManager+Internal.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
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
internal extension IQKeyboardManager {
|
||||
|
||||
/** Get all UITextField/UITextView siblings of textFieldView. */
|
||||
func responderViews() -> [UIView]? {
|
||||
|
||||
var superConsideredView: UIView?
|
||||
|
||||
//If find any consider responderView in it's upper hierarchy then will get deepResponderView.
|
||||
for disabledClass in toolbarPreviousNextAllowedClasses {
|
||||
superConsideredView = textFieldView?.superviewOfClassType(disabledClass)
|
||||
if superConsideredView != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//If there is a superConsideredView in view's hierarchy, then fetching all it's subview that responds. No sorting for superConsideredView, it's by subView position. (Enhancement ID: #22)
|
||||
if let view = superConsideredView {
|
||||
return view.deepResponderViews()
|
||||
} else { //Otherwise fetching all the siblings
|
||||
|
||||
guard let textFields = textFieldView?.responderSiblings() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
//Sorting textFields according to behaviour
|
||||
switch toolbarManageBehaviour {
|
||||
//If autoToolbar behaviour is bySubviews, then returning it.
|
||||
case .bySubviews: return textFields
|
||||
|
||||
//If autoToolbar behaviour is by tag, then sorting it according to tag property.
|
||||
case .byTag: return textFields.sortedArrayByTag()
|
||||
|
||||
//If autoToolbar behaviour is by tag, then sorting it according to tag property.
|
||||
case .byPosition: return textFields.sortedArrayByPosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func privateIsEnabled() -> Bool {
|
||||
|
||||
var isEnabled = enable
|
||||
|
||||
let enableMode = textFieldView?.enableMode
|
||||
|
||||
if enableMode == .enabled {
|
||||
isEnabled = true
|
||||
} else if enableMode == .disabled {
|
||||
isEnabled = false
|
||||
} else if var textFieldViewController = textFieldView?.viewContainingController() {
|
||||
|
||||
//If it is searchBar textField embedded in Navigation Bar
|
||||
if textFieldView?.textFieldSearchBar() != nil, let navController = textFieldViewController as? UINavigationController, let topController = navController.topViewController {
|
||||
textFieldViewController = topController
|
||||
}
|
||||
|
||||
//If viewController is kind of enable viewController class, then assuming it's enabled.
|
||||
if !isEnabled, enabledDistanceHandlingClasses.contains(where: { textFieldViewController.isKind(of: $0) }) {
|
||||
isEnabled = true
|
||||
}
|
||||
|
||||
if isEnabled {
|
||||
|
||||
//If viewController is kind of disabled viewController class, then assuming it's disabled.
|
||||
if disabledDistanceHandlingClasses.contains(where: { textFieldViewController.isKind(of: $0) }) {
|
||||
isEnabled = false
|
||||
}
|
||||
|
||||
//Special Controllers
|
||||
if isEnabled {
|
||||
|
||||
let classNameString: String = "\(type(of: textFieldViewController.self))"
|
||||
|
||||
//_UIAlertControllerTextFieldViewController
|
||||
if classNameString.contains("UIAlertController"), classNameString.hasSuffix("TextFieldViewController") {
|
||||
isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isEnabled
|
||||
}
|
||||
|
||||
func privateIsEnableAutoToolbar() -> Bool {
|
||||
|
||||
guard var textFieldViewController = textFieldView?.viewContainingController() else {
|
||||
return enableAutoToolbar
|
||||
}
|
||||
|
||||
//If it is searchBar textField embedded in Navigation Bar
|
||||
if textFieldView?.textFieldSearchBar() != nil, let navController = textFieldViewController as? UINavigationController, let topController = navController.topViewController {
|
||||
textFieldViewController = topController
|
||||
}
|
||||
|
||||
var enableToolbar = enableAutoToolbar
|
||||
|
||||
if !enableToolbar, enabledToolbarClasses.contains(where: { textFieldViewController.isKind(of: $0) }) {
|
||||
enableToolbar = true
|
||||
}
|
||||
|
||||
if enableToolbar {
|
||||
|
||||
//If found any toolbar disabled classes then return.
|
||||
if disabledToolbarClasses.contains(where: { textFieldViewController.isKind(of: $0) }) {
|
||||
enableToolbar = false
|
||||
}
|
||||
|
||||
//Special Controllers
|
||||
if enableToolbar {
|
||||
|
||||
let classNameString: String = "\(type(of: textFieldViewController.self))"
|
||||
|
||||
//_UIAlertControllerTextFieldViewController
|
||||
if classNameString.contains("UIAlertController"), classNameString.hasSuffix("TextFieldViewController") {
|
||||
enableToolbar = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return enableToolbar
|
||||
}
|
||||
|
||||
func privateShouldResignOnTouchOutside() -> Bool {
|
||||
|
||||
var shouldResign = shouldResignOnTouchOutside
|
||||
|
||||
let enableMode = textFieldView?.shouldResignOnTouchOutsideMode
|
||||
|
||||
if enableMode == .enabled {
|
||||
shouldResign = true
|
||||
} else if enableMode == .disabled {
|
||||
shouldResign = false
|
||||
} else if var textFieldViewController = textFieldView?.viewContainingController() {
|
||||
|
||||
//If it is searchBar textField embedded in Navigation Bar
|
||||
if textFieldView?.textFieldSearchBar() != nil, let navController = textFieldViewController as? UINavigationController, let topController = navController.topViewController {
|
||||
textFieldViewController = topController
|
||||
}
|
||||
|
||||
//If viewController is kind of enable viewController class, then assuming shouldResignOnTouchOutside is enabled.
|
||||
if !shouldResign, enabledTouchResignedClasses.contains(where: { textFieldViewController.isKind(of: $0) }) {
|
||||
shouldResign = true
|
||||
}
|
||||
|
||||
if shouldResign {
|
||||
|
||||
//If viewController is kind of disable viewController class, then assuming shouldResignOnTouchOutside is disable.
|
||||
if disabledTouchResignedClasses.contains(where: { textFieldViewController.isKind(of: $0) }) {
|
||||
shouldResign = false
|
||||
}
|
||||
|
||||
//Special Controllers
|
||||
if shouldResign {
|
||||
|
||||
let classNameString: String = "\(type(of: textFieldViewController.self))"
|
||||
|
||||
//_UIAlertControllerTextFieldViewController
|
||||
if classNameString.contains("UIAlertController"), classNameString.hasSuffix("TextFieldViewController") {
|
||||
shouldResign = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return shouldResign
|
||||
}
|
||||
}
|
||||
77
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+OrientationNotification.swift
generated
Normal file
77
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+OrientationNotification.swift
generated
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// IQKeyboardManager+OrientationNotification.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: UIStatusBar Notification methods
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
internal extension IQKeyboardManager {
|
||||
|
||||
/** UIApplicationWillChangeStatusBarOrientationNotification. Need to set the textView to it's original position. If any frame changes made. (Bug ID: #92)*/
|
||||
@objc func willChangeStatusBarOrientation(_ notification: Notification) {
|
||||
|
||||
let currentStatusBarOrientation: UIInterfaceOrientation
|
||||
#if swift(>=5.1)
|
||||
if #available(iOS 13, *) {
|
||||
currentStatusBarOrientation = keyWindow()?.windowScene?.interfaceOrientation ?? UIInterfaceOrientation.unknown
|
||||
} else {
|
||||
currentStatusBarOrientation = UIApplication.shared.statusBarOrientation
|
||||
}
|
||||
#else
|
||||
currentStatusBarOrientation = UIApplication.shared.statusBarOrientation
|
||||
#endif
|
||||
|
||||
guard let statusBarOrientation = notification.userInfo?[UIApplication.statusBarOrientationUserInfoKey] as? Int, currentStatusBarOrientation.rawValue != statusBarOrientation else {
|
||||
return
|
||||
}
|
||||
|
||||
let startTime = CACurrentMediaTime()
|
||||
showLog("📱>>>>> \(#function) started >>>>>", indentation: 1)
|
||||
showLog("Notification Object:\(notification.object ?? "NULL")")
|
||||
|
||||
//If textViewContentInsetChanged is saved then restore it.
|
||||
if let textView = textFieldView as? UIScrollView, textView.responds(to: #selector(getter: UITextView.isEditable)) {
|
||||
|
||||
if isTextViewContentInsetChanged {
|
||||
self.isTextViewContentInsetChanged = false
|
||||
if textView.contentInset != self.startingTextViewContentInsets {
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
self.showLog("Restoring textView.contentInset to: \(self.startingTextViewContentInsets)")
|
||||
|
||||
//Setting textField to it's initial contentInset
|
||||
textView.contentInset = self.startingTextViewContentInsets
|
||||
textView.scrollIndicatorInsets = self.startingTextViewScrollIndicatorInsets
|
||||
|
||||
}, completion: { (_) -> Void in })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restorePosition()
|
||||
|
||||
let elapsedTime = CACurrentMediaTime() - startTime
|
||||
showLog("📱<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
|
||||
}
|
||||
}
|
||||
717
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Position.swift
generated
Normal file
717
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Position.swift
generated
Normal file
@@ -0,0 +1,717 @@
|
||||
//
|
||||
// IQKeyboardManager+Position.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
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
public extension IQKeyboardManager {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var movedDistance = "movedDistance"
|
||||
static var movedDistanceChanged = "movedDistanceChanged"
|
||||
static var lastScrollView = "lastScrollView"
|
||||
static var startingContentOffset = "startingContentOffset"
|
||||
static var startingScrollIndicatorInsets = "startingScrollIndicatorInsets"
|
||||
static var startingContentInsets = "startingContentInsets"
|
||||
static var startingTextViewContentInsets = "startingTextViewContentInsets"
|
||||
static var startingTextViewScrollIndicatorInsets = "startingTextViewScrollIndicatorInsets"
|
||||
static var isTextViewContentInsetChanged = "isTextViewContentInsetChanged"
|
||||
static var hasPendingAdjustRequest = "hasPendingAdjustRequest"
|
||||
}
|
||||
|
||||
/**
|
||||
moved distance to the top used to maintain distance between keyboard and textField. Most of the time this will be a positive value.
|
||||
*/
|
||||
@objc private(set) var movedDistance: CGFloat {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.movedDistance) as? CGFloat ?? 0.0
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.movedDistance, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
movedDistanceChanged?(movedDistance)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Will be called then movedDistance will be changed
|
||||
*/
|
||||
@objc var movedDistanceChanged: ((CGFloat) -> Void)? {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.movedDistanceChanged) as? ((CGFloat) -> Void)
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.movedDistanceChanged, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
movedDistanceChanged?(movedDistance)
|
||||
}
|
||||
}
|
||||
|
||||
/** Variable to save lastScrollView that was scrolled. */
|
||||
internal weak var lastScrollView: UIScrollView? {
|
||||
get {
|
||||
return (objc_getAssociatedObject(self, &AssociatedKeys.lastScrollView) as? WeakObjectContainer)?.object as? UIScrollView
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.lastScrollView, WeakObjectContainer(object: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** LastScrollView's initial contentOffset. */
|
||||
internal var startingContentOffset: CGPoint {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.startingContentOffset) as? CGPoint ?? IQKeyboardManager.kIQCGPointInvalid
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.startingContentOffset, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** LastScrollView's initial scrollIndicatorInsets. */
|
||||
internal var startingScrollIndicatorInsets: UIEdgeInsets {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.startingScrollIndicatorInsets) as? UIEdgeInsets ?? .init()
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.startingScrollIndicatorInsets, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** LastScrollView's initial contentInsets. */
|
||||
internal var startingContentInsets: UIEdgeInsets {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.startingContentInsets) as? UIEdgeInsets ?? .init()
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.startingContentInsets, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** used to adjust contentInset of UITextView. */
|
||||
internal var startingTextViewContentInsets: UIEdgeInsets {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.startingTextViewContentInsets) as? UIEdgeInsets ?? .init()
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.startingTextViewContentInsets, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** used to adjust scrollIndicatorInsets of UITextView. */
|
||||
internal var startingTextViewScrollIndicatorInsets: UIEdgeInsets {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.startingTextViewScrollIndicatorInsets) as? UIEdgeInsets ?? .init()
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.startingTextViewScrollIndicatorInsets, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** used with textView to detect a textFieldView contentInset is changed or not. (Bug ID: #92)*/
|
||||
internal var isTextViewContentInsetChanged: Bool {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.isTextViewContentInsetChanged) as? Bool ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.isTextViewContentInsetChanged, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** To know if we have any pending request to adjust view position. */
|
||||
private var hasPendingAdjustRequest: Bool {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.hasPendingAdjustRequest) as? Bool ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.hasPendingAdjustRequest, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
internal func optimizedAdjustPosition() {
|
||||
if !hasPendingAdjustRequest {
|
||||
hasPendingAdjustRequest = true
|
||||
OperationQueue.main.addOperation {
|
||||
self.adjustPosition()
|
||||
self.hasPendingAdjustRequest = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjusting RootViewController's frame according to interface orientation. */
|
||||
private func adjustPosition() {
|
||||
|
||||
// We are unable to get textField object while keyboard showing on WKWebView's textField. (Bug ID: #11)
|
||||
guard hasPendingAdjustRequest,
|
||||
let textFieldView = textFieldView,
|
||||
let rootController = textFieldView.parentContainerViewController(),
|
||||
let window = keyWindow(),
|
||||
let textFieldViewRectInWindow = textFieldView.superview?.convert(textFieldView.frame, to: window),
|
||||
let textFieldViewRectInRootSuperview = textFieldView.superview?.convert(textFieldView.frame, to: rootController.view?.superview) else {
|
||||
return
|
||||
}
|
||||
|
||||
let startTime = CACurrentMediaTime()
|
||||
showLog(">>>>> \(#function) started >>>>>", indentation: 1)
|
||||
|
||||
// Getting RootViewOrigin.
|
||||
var rootViewOrigin = rootController.view.frame.origin
|
||||
|
||||
//Maintain keyboardDistanceFromTextField
|
||||
var specialKeyboardDistanceFromTextField = textFieldView.keyboardDistanceFromTextField
|
||||
|
||||
if let searchBar = textFieldView.textFieldSearchBar() {
|
||||
specialKeyboardDistanceFromTextField = searchBar.keyboardDistanceFromTextField
|
||||
}
|
||||
|
||||
let newKeyboardDistanceFromTextField = (specialKeyboardDistanceFromTextField == kIQUseDefaultKeyboardDistance) ? keyboardDistanceFromTextField : specialKeyboardDistanceFromTextField
|
||||
|
||||
var kbSize = keyboardFrame.size
|
||||
|
||||
do {
|
||||
var kbFrame = keyboardFrame
|
||||
|
||||
kbFrame.origin.y -= newKeyboardDistanceFromTextField
|
||||
kbFrame.size.height += newKeyboardDistanceFromTextField
|
||||
|
||||
//Calculating actual keyboard covered size respect to window, keyboard frame may be different when hardware keyboard is attached (Bug ID: #469) (Bug ID: #381) (Bug ID: #1506)
|
||||
let intersectRect = kbFrame.intersection(window.frame)
|
||||
|
||||
if intersectRect.isNull {
|
||||
kbSize = CGSize(width: kbFrame.size.width, height: 0)
|
||||
} else {
|
||||
kbSize = intersectRect.size
|
||||
}
|
||||
}
|
||||
|
||||
let statusBarHeight: CGFloat
|
||||
|
||||
let navigationBarAreaHeight: CGFloat
|
||||
if let navigationController = rootController.navigationController {
|
||||
navigationBarAreaHeight = navigationController.navigationBar.frame.maxY
|
||||
} else {
|
||||
#if swift(>=5.1)
|
||||
if #available(iOS 13, *) {
|
||||
statusBarHeight = window.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
|
||||
} else {
|
||||
statusBarHeight = UIApplication.shared.statusBarFrame.height
|
||||
}
|
||||
#else
|
||||
statusBarHeight = UIApplication.shared.statusBarFrame.height
|
||||
#endif
|
||||
navigationBarAreaHeight = statusBarHeight
|
||||
}
|
||||
|
||||
let layoutAreaHeight: CGFloat = rootController.view.layoutMargins.bottom
|
||||
|
||||
let isTextView: Bool
|
||||
let isNonScrollableTextView: Bool
|
||||
|
||||
if let textView = textFieldView as? UIScrollView, textFieldView.responds(to: #selector(getter: UITextView.isEditable)) {
|
||||
|
||||
isTextView = true
|
||||
isNonScrollableTextView = !textView.isScrollEnabled
|
||||
} else {
|
||||
isTextView = false
|
||||
isNonScrollableTextView = false
|
||||
}
|
||||
|
||||
let topLayoutGuide: CGFloat = max(navigationBarAreaHeight, layoutAreaHeight) + 5
|
||||
|
||||
let bottomLayoutGuide: CGFloat = (isTextView && !isNonScrollableTextView) ? 0 : rootController.view.layoutMargins.bottom //Validation of textView for case where there is a tab bar at the bottom or running on iPhone X and textView is at the bottom.
|
||||
let visibleHeight: CGFloat = window.frame.height-kbSize.height
|
||||
|
||||
// Move positive = textField is hidden.
|
||||
// Move negative = textField is showing.
|
||||
// Calculating move position.
|
||||
var move: CGFloat
|
||||
|
||||
//Special case: when the textView is not scrollable, then we'll be scrolling to the bottom part and let hide the top part above
|
||||
if isNonScrollableTextView {
|
||||
move = textFieldViewRectInWindow.maxY - visibleHeight + bottomLayoutGuide
|
||||
} else {
|
||||
move = min(textFieldViewRectInRootSuperview.minY-(topLayoutGuide), textFieldViewRectInWindow.maxY - visibleHeight + bottomLayoutGuide)
|
||||
}
|
||||
|
||||
showLog("Need to move: \(move)")
|
||||
|
||||
var superScrollView: UIScrollView?
|
||||
var superView = textFieldView.superviewOfClassType(UIScrollView.self) as? UIScrollView
|
||||
|
||||
//Getting UIScrollView whose scrolling is enabled. // (Bug ID: #285)
|
||||
while let view = superView {
|
||||
|
||||
if view.isScrollEnabled, !view.shouldIgnoreScrollingAdjustment {
|
||||
superScrollView = view
|
||||
break
|
||||
} else {
|
||||
// Getting it's superScrollView. // (Enhancement ID: #21, #24)
|
||||
superView = view.superviewOfClassType(UIScrollView.self) as? UIScrollView
|
||||
}
|
||||
}
|
||||
|
||||
//If there was a lastScrollView. // (Bug ID: #34)
|
||||
if let lastScrollView = lastScrollView {
|
||||
//If we can't find current superScrollView, then setting lastScrollView to it's original form.
|
||||
if superScrollView == nil {
|
||||
|
||||
if lastScrollView.contentInset != self.startingContentInsets {
|
||||
showLog("Restoring contentInset to: \(startingContentInsets)")
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
lastScrollView.contentInset = self.startingContentInsets
|
||||
lastScrollView.scrollIndicatorInsets = self.startingScrollIndicatorInsets
|
||||
})
|
||||
}
|
||||
|
||||
if lastScrollView.shouldRestoreScrollViewContentOffset, !lastScrollView.contentOffset.equalTo(startingContentOffset) {
|
||||
showLog("Restoring contentOffset to: \(startingContentOffset)")
|
||||
|
||||
let animatedContentOffset = textFieldView.superviewOfClassType(UIStackView.self, belowView: lastScrollView) != nil // (Bug ID: #1365, #1508, #1541)
|
||||
|
||||
if animatedContentOffset {
|
||||
lastScrollView.setContentOffset(startingContentOffset, animated: UIView.areAnimationsEnabled)
|
||||
} else {
|
||||
lastScrollView.contentOffset = startingContentOffset
|
||||
}
|
||||
}
|
||||
|
||||
startingContentInsets = UIEdgeInsets()
|
||||
startingScrollIndicatorInsets = UIEdgeInsets()
|
||||
startingContentOffset = CGPoint.zero
|
||||
self.lastScrollView = nil
|
||||
} else if superScrollView != lastScrollView { //If both scrollView's are different, then reset lastScrollView to it's original frame and setting current scrollView as last scrollView.
|
||||
|
||||
if lastScrollView.contentInset != self.startingContentInsets {
|
||||
showLog("Restoring contentInset to: \(startingContentInsets)")
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
lastScrollView.contentInset = self.startingContentInsets
|
||||
lastScrollView.scrollIndicatorInsets = self.startingScrollIndicatorInsets
|
||||
})
|
||||
}
|
||||
|
||||
if lastScrollView.shouldRestoreScrollViewContentOffset, !lastScrollView.contentOffset.equalTo(startingContentOffset) {
|
||||
showLog("Restoring contentOffset to: \(startingContentOffset)")
|
||||
|
||||
let animatedContentOffset = textFieldView.superviewOfClassType(UIStackView.self, belowView: lastScrollView) != nil // (Bug ID: #1365, #1508, #1541)
|
||||
|
||||
if animatedContentOffset {
|
||||
lastScrollView.setContentOffset(startingContentOffset, animated: UIView.areAnimationsEnabled)
|
||||
} else {
|
||||
lastScrollView.contentOffset = startingContentOffset
|
||||
}
|
||||
}
|
||||
|
||||
self.lastScrollView = superScrollView
|
||||
if let scrollView = superScrollView {
|
||||
startingContentInsets = scrollView.contentInset
|
||||
startingContentOffset = scrollView.contentOffset
|
||||
|
||||
#if swift(>=5.1)
|
||||
if #available(iOS 11.1, *) {
|
||||
startingScrollIndicatorInsets = scrollView.verticalScrollIndicatorInsets
|
||||
} else {
|
||||
startingScrollIndicatorInsets = scrollView.scrollIndicatorInsets
|
||||
}
|
||||
#else
|
||||
startingScrollIndicatorInsets = scrollView.scrollIndicatorInsets
|
||||
#endif
|
||||
}
|
||||
|
||||
showLog("Saving ScrollView New contentInset: \(startingContentInsets) and contentOffset: \(startingContentOffset)")
|
||||
}
|
||||
//Else the case where superScrollView == lastScrollView means we are on same scrollView after switching to different textField. So doing nothing, going ahead
|
||||
} else if let unwrappedSuperScrollView = superScrollView { //If there was no lastScrollView and we found a current scrollView. then setting it as lastScrollView.
|
||||
lastScrollView = unwrappedSuperScrollView
|
||||
startingContentInsets = unwrappedSuperScrollView.contentInset
|
||||
startingContentOffset = unwrappedSuperScrollView.contentOffset
|
||||
|
||||
#if swift(>=5.1)
|
||||
if #available(iOS 11.1, *) {
|
||||
startingScrollIndicatorInsets = unwrappedSuperScrollView.verticalScrollIndicatorInsets
|
||||
} else {
|
||||
startingScrollIndicatorInsets = unwrappedSuperScrollView.scrollIndicatorInsets
|
||||
}
|
||||
#else
|
||||
startingScrollIndicatorInsets = unwrappedSuperScrollView.scrollIndicatorInsets
|
||||
#endif
|
||||
|
||||
showLog("Saving ScrollView contentInset: \(startingContentInsets) and contentOffset: \(startingContentOffset)")
|
||||
}
|
||||
|
||||
// Special case for ScrollView.
|
||||
// If we found lastScrollView then setting it's contentOffset to show textField.
|
||||
if let lastScrollView = lastScrollView {
|
||||
//Saving
|
||||
var lastView = textFieldView
|
||||
var superScrollView = self.lastScrollView
|
||||
|
||||
while let scrollView = superScrollView {
|
||||
|
||||
var shouldContinue = false
|
||||
|
||||
if move > 0 {
|
||||
shouldContinue = move > (-scrollView.contentOffset.y - scrollView.contentInset.top)
|
||||
|
||||
} else if let tableView = scrollView.superviewOfClassType(UITableView.self) as? UITableView {
|
||||
|
||||
shouldContinue = scrollView.contentOffset.y > 0
|
||||
|
||||
if shouldContinue, let tableCell = textFieldView.superviewOfClassType(UITableViewCell.self) as? UITableViewCell, let indexPath = tableView.indexPath(for: tableCell), let previousIndexPath = tableView.previousIndexPath(of: indexPath) {
|
||||
|
||||
let previousCellRect = tableView.rectForRow(at: previousIndexPath)
|
||||
if !previousCellRect.isEmpty {
|
||||
let previousCellRectInRootSuperview = tableView.convert(previousCellRect, to: rootController.view.superview)
|
||||
|
||||
move = min(0, previousCellRectInRootSuperview.maxY - topLayoutGuide)
|
||||
}
|
||||
}
|
||||
} else if let collectionView = scrollView.superviewOfClassType(UICollectionView.self) as? UICollectionView {
|
||||
|
||||
shouldContinue = scrollView.contentOffset.y > 0
|
||||
|
||||
if shouldContinue, let collectionCell = textFieldView.superviewOfClassType(UICollectionViewCell.self) as? UICollectionViewCell, let indexPath = collectionView.indexPath(for: collectionCell), let previousIndexPath = collectionView.previousIndexPath(of: indexPath), let attributes = collectionView.layoutAttributesForItem(at: previousIndexPath) {
|
||||
|
||||
let previousCellRect = attributes.frame
|
||||
if !previousCellRect.isEmpty {
|
||||
let previousCellRectInRootSuperview = collectionView.convert(previousCellRect, to: rootController.view.superview)
|
||||
|
||||
move = min(0, previousCellRectInRootSuperview.maxY - topLayoutGuide)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if isNonScrollableTextView {
|
||||
shouldContinue = textFieldViewRectInWindow.maxY < visibleHeight + bottomLayoutGuide
|
||||
|
||||
if shouldContinue {
|
||||
move = min(0, textFieldViewRectInWindow.maxY - visibleHeight + bottomLayoutGuide)
|
||||
}
|
||||
} else {
|
||||
shouldContinue = textFieldViewRectInRootSuperview.minY < topLayoutGuide
|
||||
|
||||
if shouldContinue {
|
||||
move = min(0, textFieldViewRectInRootSuperview.minY - topLayoutGuide)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Looping in upper hierarchy until we don't found any scrollView in it's upper hirarchy till UIWindow object.
|
||||
if shouldContinue {
|
||||
|
||||
var tempScrollView = scrollView.superviewOfClassType(UIScrollView.self) as? UIScrollView
|
||||
var nextScrollView: UIScrollView?
|
||||
while let view = tempScrollView {
|
||||
|
||||
if view.isScrollEnabled, !view.shouldIgnoreScrollingAdjustment {
|
||||
nextScrollView = view
|
||||
break
|
||||
} else {
|
||||
tempScrollView = view.superviewOfClassType(UIScrollView.self) as? UIScrollView
|
||||
}
|
||||
}
|
||||
|
||||
//Getting lastViewRect.
|
||||
if let lastViewRect = lastView.superview?.convert(lastView.frame, to: scrollView) {
|
||||
|
||||
//Calculating the expected Y offset from move and scrollView's contentOffset.
|
||||
var shouldOffsetY = scrollView.contentOffset.y - min(scrollView.contentOffset.y, -move)
|
||||
|
||||
//Rearranging the expected Y offset according to the view.
|
||||
|
||||
if isNonScrollableTextView {
|
||||
shouldOffsetY = min(shouldOffsetY, lastViewRect.maxY - visibleHeight + bottomLayoutGuide)
|
||||
} else {
|
||||
shouldOffsetY = min(shouldOffsetY, lastViewRect.minY)
|
||||
}
|
||||
|
||||
//[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type
|
||||
//nextScrollView == nil If processing scrollView is last scrollView in upper hierarchy (there is no other scrollView upper hierrchy.)
|
||||
//[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type
|
||||
//shouldOffsetY >= 0 shouldOffsetY must be greater than in order to keep distance from navigationBar (Bug ID: #92)
|
||||
if isTextView, !isNonScrollableTextView,
|
||||
nextScrollView == nil,
|
||||
shouldOffsetY >= 0 {
|
||||
|
||||
// Converting Rectangle according to window bounds.
|
||||
if let currentTextFieldViewRect = textFieldView.superview?.convert(textFieldView.frame, to: window) {
|
||||
|
||||
//Calculating expected fix distance which needs to be managed from navigation bar
|
||||
let expectedFixDistance: CGFloat = currentTextFieldViewRect.minY - topLayoutGuide
|
||||
|
||||
//Now if expectedOffsetY (superScrollView.contentOffset.y + expectedFixDistance) is lower than current shouldOffsetY, which means we're in a position where navigationBar up and hide, then reducing shouldOffsetY with expectedOffsetY (superScrollView.contentOffset.y + expectedFixDistance)
|
||||
shouldOffsetY = min(shouldOffsetY, scrollView.contentOffset.y + expectedFixDistance)
|
||||
|
||||
//Setting move to 0 because now we don't want to move any view anymore (All will be managed by our contentInset logic.
|
||||
move = 0
|
||||
} else {
|
||||
//Subtracting the Y offset from the move variable, because we are going to change scrollView's contentOffset.y to shouldOffsetY.
|
||||
move -= (shouldOffsetY-scrollView.contentOffset.y)
|
||||
}
|
||||
} else {
|
||||
//Subtracting the Y offset from the move variable, because we are going to change scrollView's contentOffset.y to shouldOffsetY.
|
||||
move -= (shouldOffsetY-scrollView.contentOffset.y)
|
||||
}
|
||||
|
||||
let newContentOffset = CGPoint(x: scrollView.contentOffset.x, y: shouldOffsetY)
|
||||
|
||||
if scrollView.contentOffset.equalTo(newContentOffset) == false {
|
||||
|
||||
showLog("old contentOffset: \(scrollView.contentOffset) new contentOffset: \(newContentOffset)")
|
||||
self.showLog("Remaining Move: \(move)")
|
||||
|
||||
//Getting problem while using `setContentOffset:animated:`, So I used animation API.
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
let animatedContentOffset = textFieldView.superviewOfClassType(UIStackView.self, belowView: scrollView) != nil // (Bug ID: #1365, #1508, #1541)
|
||||
|
||||
if animatedContentOffset {
|
||||
scrollView.setContentOffset(newContentOffset, animated: UIView.areAnimationsEnabled)
|
||||
} else {
|
||||
scrollView.contentOffset = newContentOffset
|
||||
}
|
||||
}, completion: { _ in
|
||||
|
||||
if scrollView is UITableView || scrollView is UICollectionView {
|
||||
//This will update the next/previous states
|
||||
self.addToolbarIfRequired()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Getting next lastView & superScrollView.
|
||||
lastView = scrollView
|
||||
superScrollView = nextScrollView
|
||||
} else {
|
||||
move = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//Updating contentInset
|
||||
if let lastScrollViewRect = lastScrollView.superview?.convert(lastScrollView.frame, to: window),
|
||||
lastScrollView.shouldIgnoreContentInsetAdjustment == false {
|
||||
|
||||
var bottomInset: CGFloat = (kbSize.height)-(window.frame.height-lastScrollViewRect.maxY)
|
||||
var bottomScrollIndicatorInset = bottomInset - newKeyboardDistanceFromTextField
|
||||
|
||||
// Update the insets so that the scroll vew doesn't shift incorrectly when the offset is near the bottom of the scroll view.
|
||||
bottomInset = max(startingContentInsets.bottom, bottomInset)
|
||||
bottomScrollIndicatorInset = max(startingScrollIndicatorInsets.bottom, bottomScrollIndicatorInset)
|
||||
|
||||
if #available(iOS 11, *) {
|
||||
bottomInset -= lastScrollView.safeAreaInsets.bottom
|
||||
bottomScrollIndicatorInset -= lastScrollView.safeAreaInsets.bottom
|
||||
}
|
||||
|
||||
var movedInsets = lastScrollView.contentInset
|
||||
movedInsets.bottom = bottomInset
|
||||
|
||||
if lastScrollView.contentInset != movedInsets {
|
||||
showLog("old ContentInset: \(lastScrollView.contentInset) new ContentInset: \(movedInsets)")
|
||||
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
lastScrollView.contentInset = movedInsets
|
||||
|
||||
var newScrollIndicatorInset: UIEdgeInsets
|
||||
|
||||
#if swift(>=5.1)
|
||||
if #available(iOS 11.1, *) {
|
||||
newScrollIndicatorInset = lastScrollView.verticalScrollIndicatorInsets
|
||||
} else {
|
||||
newScrollIndicatorInset = lastScrollView.scrollIndicatorInsets
|
||||
}
|
||||
#else
|
||||
newScrollIndicatorInset = lastScrollView.scrollIndicatorInsets
|
||||
#endif
|
||||
|
||||
newScrollIndicatorInset.bottom = bottomScrollIndicatorInset
|
||||
lastScrollView.scrollIndicatorInsets = newScrollIndicatorInset
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
//Going ahead. No else if.
|
||||
|
||||
//Special case for UITextView(Readjusting textView.contentInset when textView hight is too big to fit on screen)
|
||||
//_lastScrollView If not having inside any scrollView, (now contentInset manages the full screen textView.
|
||||
//[_textFieldView isKindOfClass:[UITextView class]] If is a UITextView type
|
||||
if let textView = textFieldView as? UIScrollView, textView.isScrollEnabled, textFieldView.responds(to: #selector(getter: UITextView.isEditable)) {
|
||||
|
||||
// CGRect rootSuperViewFrameInWindow = [_rootViewController.view.superview convertRect:_rootViewController.view.superview.bounds toView:keyWindow];
|
||||
//
|
||||
// CGFloat keyboardOverlapping = CGRectGetMaxY(rootSuperViewFrameInWindow) - keyboardYPosition;
|
||||
//
|
||||
// CGFloat textViewHeight = MIN(CGRectGetHeight(_textFieldView.frame), (CGRectGetHeight(rootSuperViewFrameInWindow)-topLayoutGuide-keyboardOverlapping));
|
||||
|
||||
let keyboardYPosition = window.frame.height - (kbSize.height-newKeyboardDistanceFromTextField)
|
||||
var rootSuperViewFrameInWindow = window.frame
|
||||
if let rootSuperview = rootController.view.superview {
|
||||
rootSuperViewFrameInWindow = rootSuperview.convert(rootSuperview.bounds, to: window)
|
||||
}
|
||||
|
||||
let keyboardOverlapping = rootSuperViewFrameInWindow.maxY - keyboardYPosition
|
||||
|
||||
let textViewHeight = min(textView.frame.height, rootSuperViewFrameInWindow.height-topLayoutGuide-keyboardOverlapping)
|
||||
|
||||
if textView.frame.size.height-textView.contentInset.bottom>textViewHeight {
|
||||
//_isTextViewContentInsetChanged, If frame is not change by library in past, then saving user textView properties (Bug ID: #92)
|
||||
if !self.isTextViewContentInsetChanged {
|
||||
self.startingTextViewContentInsets = textView.contentInset
|
||||
|
||||
#if swift(>=5.1)
|
||||
if #available(iOS 11.1, *) {
|
||||
self.startingTextViewScrollIndicatorInsets = textView.verticalScrollIndicatorInsets
|
||||
} else {
|
||||
self.startingTextViewScrollIndicatorInsets = textView.scrollIndicatorInsets
|
||||
}
|
||||
#else
|
||||
self.startingTextViewScrollIndicatorInsets = textView.scrollIndicatorInsets
|
||||
#endif
|
||||
}
|
||||
|
||||
self.isTextViewContentInsetChanged = true
|
||||
|
||||
var newContentInset = textView.contentInset
|
||||
newContentInset.bottom = textView.frame.size.height-textViewHeight
|
||||
|
||||
if #available(iOS 11, *) {
|
||||
newContentInset.bottom -= textView.safeAreaInsets.bottom
|
||||
}
|
||||
|
||||
if textView.contentInset != newContentInset {
|
||||
self.showLog("\(textFieldView) Old UITextView.contentInset: \(textView.contentInset) New UITextView.contentInset: \(newContentInset)")
|
||||
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
textView.contentInset = newContentInset
|
||||
textView.scrollIndicatorInsets = newContentInset
|
||||
}, completion: { (_) -> Void in })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// +Positive or zero.
|
||||
if move >= 0 {
|
||||
|
||||
rootViewOrigin.y = max(rootViewOrigin.y - move, min(0, -(kbSize.height-newKeyboardDistanceFromTextField)))
|
||||
|
||||
if rootController.view.frame.origin.equalTo(rootViewOrigin) == false {
|
||||
showLog("Moving Upward")
|
||||
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
var rect = rootController.view.frame
|
||||
rect.origin = rootViewOrigin
|
||||
rootController.view.frame = rect
|
||||
|
||||
//Animating content if needed (Bug ID: #204)
|
||||
if self.layoutIfNeededOnUpdate {
|
||||
//Animating content (Bug ID: #160)
|
||||
rootController.view.setNeedsLayout()
|
||||
rootController.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
self.showLog("Set \(rootController) origin to: \(rootViewOrigin)")
|
||||
})
|
||||
}
|
||||
|
||||
movedDistance = (topViewBeginOrigin.y-rootViewOrigin.y)
|
||||
} else { // -Negative
|
||||
let disturbDistance: CGFloat = rootViewOrigin.y-topViewBeginOrigin.y
|
||||
|
||||
// disturbDistance Negative = frame disturbed.
|
||||
// disturbDistance positive = frame not disturbed.
|
||||
if disturbDistance <= 0 {
|
||||
|
||||
rootViewOrigin.y -= max(move, disturbDistance)
|
||||
|
||||
if rootController.view.frame.origin.equalTo(rootViewOrigin) == false {
|
||||
showLog("Moving Downward")
|
||||
// Setting adjusted rootViewRect
|
||||
// Setting adjusted rootViewRect
|
||||
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
var rect = rootController.view.frame
|
||||
rect.origin = rootViewOrigin
|
||||
rootController.view.frame = rect
|
||||
|
||||
//Animating content if needed (Bug ID: #204)
|
||||
if self.layoutIfNeededOnUpdate {
|
||||
//Animating content (Bug ID: #160)
|
||||
rootController.view.setNeedsLayout()
|
||||
rootController.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
self.showLog("Set \(rootController) origin to: \(rootViewOrigin)")
|
||||
})
|
||||
}
|
||||
|
||||
movedDistance = (topViewBeginOrigin.y-rootViewOrigin.y)
|
||||
}
|
||||
}
|
||||
|
||||
let elapsedTime = CACurrentMediaTime() - startTime
|
||||
showLog("<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
|
||||
}
|
||||
|
||||
internal func restorePosition() {
|
||||
|
||||
hasPendingAdjustRequest = false
|
||||
|
||||
// Setting rootViewController frame to it's original position. // (Bug ID: #18)
|
||||
guard topViewBeginOrigin.equalTo(IQKeyboardManager.kIQCGPointInvalid) == false, let rootViewController = rootViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
if rootViewController.view.frame.origin.equalTo(self.topViewBeginOrigin) == false {
|
||||
//Used UIViewAnimationOptionBeginFromCurrentState to minimize strange animations.
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
self.showLog("Restoring \(rootViewController) origin to: \(self.topViewBeginOrigin)")
|
||||
|
||||
// Setting it's new frame
|
||||
var rect = rootViewController.view.frame
|
||||
rect.origin = self.topViewBeginOrigin
|
||||
rootViewController.view.frame = rect
|
||||
|
||||
//Animating content if needed (Bug ID: #204)
|
||||
if self.layoutIfNeededOnUpdate {
|
||||
//Animating content (Bug ID: #160)
|
||||
rootViewController.view.setNeedsLayout()
|
||||
rootViewController.view.layoutIfNeeded()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
self.movedDistance = 0
|
||||
|
||||
if rootViewController.navigationController?.interactivePopGestureRecognizer?.state == .began {
|
||||
self.rootViewControllerWhilePopGestureRecognizerActive = rootViewController
|
||||
self.topViewBeginOriginWhilePopGestureRecognizerActive = self.topViewBeginOrigin
|
||||
}
|
||||
|
||||
self.rootViewController = nil
|
||||
}
|
||||
}
|
||||
368
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Toolbar.swift
generated
Normal file
368
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+Toolbar.swift
generated
Normal file
@@ -0,0 +1,368 @@
|
||||
//
|
||||
// IQKeyboardManager+Toolbar.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
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
public extension IQKeyboardManager {
|
||||
|
||||
/**
|
||||
Default tag for toolbar with Done button -1002.
|
||||
*/
|
||||
private static let kIQDoneButtonToolbarTag = -1002
|
||||
|
||||
/**
|
||||
Default tag for toolbar with Previous/Next buttons -1005.
|
||||
*/
|
||||
private static let kIQPreviousNextButtonToolbarTag = -1005
|
||||
|
||||
/** Add toolbar if it is required to add on textFields and it's siblings. */
|
||||
internal func addToolbarIfRequired() {
|
||||
|
||||
//Either there is no inputAccessoryView or if accessoryView is not appropriate for current situation(There is Previous/Next/Done toolbar).
|
||||
guard let siblings = responderViews(), !siblings.isEmpty,
|
||||
let textField = textFieldView, textField.responds(to: #selector(setter: UITextField.inputAccessoryView)),
|
||||
(textField.inputAccessoryView == nil ||
|
||||
textField.inputAccessoryView?.tag == IQKeyboardManager.kIQPreviousNextButtonToolbarTag ||
|
||||
textField.inputAccessoryView?.tag == IQKeyboardManager.kIQDoneButtonToolbarTag) else {
|
||||
return
|
||||
}
|
||||
|
||||
let startTime = CACurrentMediaTime()
|
||||
showLog(">>>>> \(#function) started >>>>>", indentation: 1)
|
||||
|
||||
showLog("Found \(siblings.count) responder sibling(s)")
|
||||
|
||||
let rightConfiguration: IQBarButtonItemConfiguration
|
||||
|
||||
if let doneBarButtonItemImage = toolbarDoneBarButtonItemImage {
|
||||
rightConfiguration = IQBarButtonItemConfiguration(image: doneBarButtonItemImage, action: #selector(self.doneAction(_:)))
|
||||
} else if let doneBarButtonItemText = toolbarDoneBarButtonItemText {
|
||||
rightConfiguration = IQBarButtonItemConfiguration(title: doneBarButtonItemText, action: #selector(self.doneAction(_:)))
|
||||
} else {
|
||||
rightConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .done, action: #selector(self.doneAction(_:)))
|
||||
}
|
||||
rightConfiguration.accessibilityLabel = toolbarDoneBarButtonItemAccessibilityLabel ?? "Done"
|
||||
|
||||
// If only one object is found, then adding only Done button.
|
||||
if (siblings.count <= 1 && previousNextDisplayMode == .default) || previousNextDisplayMode == .alwaysHide {
|
||||
|
||||
textField.addKeyboardToolbarWithTarget(target: self, titleText: (shouldShowToolbarPlaceholder ? textField.drawingToolbarPlaceholder: nil), rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: nil, nextBarButtonConfiguration: nil)
|
||||
|
||||
textField.inputAccessoryView?.tag = IQKeyboardManager.kIQDoneButtonToolbarTag // (Bug ID: #78)
|
||||
|
||||
} else if previousNextDisplayMode == .default || previousNextDisplayMode == .alwaysShow {
|
||||
|
||||
let prevConfiguration: IQBarButtonItemConfiguration
|
||||
|
||||
if let doneBarButtonItemImage = toolbarPreviousBarButtonItemImage {
|
||||
prevConfiguration = IQBarButtonItemConfiguration(image: doneBarButtonItemImage, action: #selector(self.previousAction(_:)))
|
||||
} else if let doneBarButtonItemText = toolbarPreviousBarButtonItemText {
|
||||
prevConfiguration = IQBarButtonItemConfiguration(title: doneBarButtonItemText, action: #selector(self.previousAction(_:)))
|
||||
} else {
|
||||
prevConfiguration = IQBarButtonItemConfiguration(image: (UIImage.keyboardPreviousImage() ?? UIImage()), action: #selector(self.previousAction(_:)))
|
||||
}
|
||||
prevConfiguration.accessibilityLabel = toolbarPreviousBarButtonItemAccessibilityLabel ?? "Previous"
|
||||
|
||||
let nextConfiguration: IQBarButtonItemConfiguration
|
||||
|
||||
if let doneBarButtonItemImage = toolbarNextBarButtonItemImage {
|
||||
nextConfiguration = IQBarButtonItemConfiguration(image: doneBarButtonItemImage, action: #selector(self.nextAction(_:)))
|
||||
} else if let doneBarButtonItemText = toolbarNextBarButtonItemText {
|
||||
nextConfiguration = IQBarButtonItemConfiguration(title: doneBarButtonItemText, action: #selector(self.nextAction(_:)))
|
||||
} else {
|
||||
nextConfiguration = IQBarButtonItemConfiguration(image: (UIImage.keyboardNextImage() ?? UIImage()), action: #selector(self.nextAction(_:)))
|
||||
}
|
||||
nextConfiguration.accessibilityLabel = toolbarNextBarButtonItemAccessibilityLabel ?? "Next"
|
||||
|
||||
textField.addKeyboardToolbarWithTarget(target: self, titleText: (shouldShowToolbarPlaceholder ? textField.drawingToolbarPlaceholder: nil), rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: prevConfiguration, nextBarButtonConfiguration: nextConfiguration)
|
||||
|
||||
textField.inputAccessoryView?.tag = IQKeyboardManager.kIQPreviousNextButtonToolbarTag // (Bug ID: #78)
|
||||
}
|
||||
|
||||
let toolbar = textField.keyboardToolbar
|
||||
|
||||
//Setting toolbar tintColor // (Enhancement ID: #30)
|
||||
toolbar.tintColor = shouldToolbarUsesTextFieldTintColor ? textField.tintColor : toolbarTintColor
|
||||
|
||||
// Setting toolbar to keyboard.
|
||||
if let textFieldView = textField as? UITextInput {
|
||||
|
||||
//Bar style according to keyboard appearance
|
||||
switch textFieldView.keyboardAppearance {
|
||||
|
||||
case .dark?:
|
||||
toolbar.barStyle = .black
|
||||
toolbar.barTintColor = nil
|
||||
default:
|
||||
toolbar.barStyle = .default
|
||||
toolbar.barTintColor = toolbarBarTintColor
|
||||
}
|
||||
}
|
||||
|
||||
//Setting toolbar title font. // (Enhancement ID: #30)
|
||||
if shouldShowToolbarPlaceholder, !textField.shouldHideToolbarPlaceholder {
|
||||
|
||||
//Updating placeholder font to toolbar. //(Bug ID: #148, #272)
|
||||
if toolbar.titleBarButton.title == nil ||
|
||||
toolbar.titleBarButton.title != textField.drawingToolbarPlaceholder {
|
||||
toolbar.titleBarButton.title = textField.drawingToolbarPlaceholder
|
||||
}
|
||||
|
||||
//Setting toolbar title font. // (Enhancement ID: #30)
|
||||
toolbar.titleBarButton.titleFont = placeholderFont
|
||||
|
||||
//Setting toolbar title color. // (Enhancement ID: #880)
|
||||
toolbar.titleBarButton.titleColor = placeholderColor
|
||||
|
||||
//Setting toolbar button title color. // (Enhancement ID: #880)
|
||||
toolbar.titleBarButton.selectableTitleColor = placeholderButtonColor
|
||||
|
||||
} else {
|
||||
toolbar.titleBarButton.title = nil
|
||||
}
|
||||
|
||||
//In case of UITableView (Special), the next/previous buttons has to be refreshed everytime. (Bug ID: #56)
|
||||
|
||||
textField.keyboardToolbar.previousBarButton.isEnabled = (siblings.first != textField) // If firstTextField, then previous should not be enabled.
|
||||
textField.keyboardToolbar.nextBarButton.isEnabled = (siblings.last != textField) // If lastTextField then next should not be enaled.
|
||||
|
||||
let elapsedTime = CACurrentMediaTime() - startTime
|
||||
showLog("<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
|
||||
}
|
||||
|
||||
/** Remove any toolbar if it is IQToolbar. */
|
||||
internal func removeToolbarIfRequired() { // (Bug ID: #18)
|
||||
|
||||
guard let siblings = responderViews(), !siblings.isEmpty,
|
||||
let textField = textFieldView, textField.responds(to: #selector(setter: UITextField.inputAccessoryView)),
|
||||
(textField.inputAccessoryView == nil ||
|
||||
textField.inputAccessoryView?.tag == IQKeyboardManager.kIQPreviousNextButtonToolbarTag ||
|
||||
textField.inputAccessoryView?.tag == IQKeyboardManager.kIQDoneButtonToolbarTag) else {
|
||||
return
|
||||
}
|
||||
|
||||
let startTime = CACurrentMediaTime()
|
||||
showLog(">>>>> \(#function) started >>>>>", indentation: 1)
|
||||
|
||||
showLog("Found \(siblings.count) responder sibling(s)")
|
||||
|
||||
for view in siblings {
|
||||
if let toolbar = view.inputAccessoryView as? IQToolbar {
|
||||
|
||||
//setInputAccessoryView: check (Bug ID: #307)
|
||||
if view.responds(to: #selector(setter: UITextField.inputAccessoryView)),
|
||||
(toolbar.tag == IQKeyboardManager.kIQDoneButtonToolbarTag || toolbar.tag == IQKeyboardManager.kIQPreviousNextButtonToolbarTag) {
|
||||
|
||||
if let textField = view as? UITextField {
|
||||
textField.inputAccessoryView = nil
|
||||
} else if let textView = view as? UITextView {
|
||||
textView.inputAccessoryView = nil
|
||||
}
|
||||
|
||||
view.reloadInputViews()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let elapsedTime = CACurrentMediaTime() - startTime
|
||||
showLog("<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
|
||||
}
|
||||
|
||||
/** reloadInputViews to reload toolbar buttons enable/disable state on the fly Enhancement ID #434. */
|
||||
@objc func reloadInputViews() {
|
||||
|
||||
//If enabled then adding toolbar.
|
||||
if privateIsEnableAutoToolbar() {
|
||||
self.addToolbarIfRequired()
|
||||
} else {
|
||||
self.removeToolbarIfRequired()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Previous next button actions
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
public extension IQKeyboardManager {
|
||||
|
||||
/**
|
||||
Returns YES if can navigate to previous responder textField/textView, otherwise NO.
|
||||
*/
|
||||
@objc var canGoPrevious: Bool {
|
||||
//If it is not first textField. then it's previous object canBecomeFirstResponder.
|
||||
guard let textFields = responderViews(), let textFieldRetain = textFieldView, let index = textFields.firstIndex(of: textFieldRetain), index > 0 else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
Returns YES if can navigate to next responder textField/textView, otherwise NO.
|
||||
*/
|
||||
@objc var canGoNext: Bool {
|
||||
//If it is not first textField. then it's previous object canBecomeFirstResponder.
|
||||
guard let textFields = responderViews(), let textFieldRetain = textFieldView, let index = textFields.firstIndex(of: textFieldRetain), index < textFields.count-1 else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
Navigate to previous responder textField/textView.
|
||||
*/
|
||||
@objc @discardableResult func goPrevious() -> Bool {
|
||||
|
||||
//If it is not first textField. then it's previous object becomeFirstResponder.
|
||||
guard let textFields = responderViews(), let textFieldRetain = textFieldView, let index = textFields.firstIndex(of: textFieldRetain), index > 0 else {
|
||||
return false
|
||||
}
|
||||
|
||||
let nextTextField = textFields[index-1]
|
||||
|
||||
let isAcceptAsFirstResponder = nextTextField.becomeFirstResponder()
|
||||
|
||||
// If it refuses then becoming previous textFieldView as first responder again. (Bug ID: #96)
|
||||
if isAcceptAsFirstResponder == false {
|
||||
showLog("Refuses to become first responder: \(nextTextField)")
|
||||
}
|
||||
|
||||
return isAcceptAsFirstResponder
|
||||
}
|
||||
|
||||
/**
|
||||
Navigate to next responder textField/textView.
|
||||
*/
|
||||
@objc @discardableResult func goNext() -> Bool {
|
||||
|
||||
//If it is not first textField. then it's previous object becomeFirstResponder.
|
||||
guard let textFields = responderViews(), let textFieldRetain = textFieldView, let index = textFields.firstIndex(of: textFieldRetain), index < textFields.count-1 else {
|
||||
return false
|
||||
}
|
||||
|
||||
let nextTextField = textFields[index+1]
|
||||
|
||||
let isAcceptAsFirstResponder = nextTextField.becomeFirstResponder()
|
||||
|
||||
// If it refuses then becoming previous textFieldView as first responder again. (Bug ID: #96)
|
||||
if isAcceptAsFirstResponder == false {
|
||||
showLog("Refuses to become first responder: \(nextTextField)")
|
||||
}
|
||||
|
||||
return isAcceptAsFirstResponder
|
||||
}
|
||||
|
||||
/** previousAction. */
|
||||
@objc internal func previousAction (_ barButton: IQBarButtonItem) {
|
||||
|
||||
//If user wants to play input Click sound.
|
||||
if shouldPlayInputClicks {
|
||||
//Play Input Click Sound.
|
||||
UIDevice.current.playInputClick()
|
||||
}
|
||||
|
||||
guard canGoPrevious, let textFieldRetain = textFieldView else {
|
||||
return
|
||||
}
|
||||
|
||||
let isAcceptAsFirstResponder = goPrevious()
|
||||
|
||||
var invocation = barButton.invocation
|
||||
var sender = textFieldRetain
|
||||
|
||||
//Handling search bar special case
|
||||
do {
|
||||
if let searchBar = textFieldRetain.textFieldSearchBar() {
|
||||
invocation = searchBar.keyboardToolbar.previousBarButton.invocation
|
||||
sender = searchBar
|
||||
}
|
||||
}
|
||||
|
||||
if isAcceptAsFirstResponder {
|
||||
invocation?.invoke(from: sender)
|
||||
}
|
||||
}
|
||||
|
||||
/** nextAction. */
|
||||
@objc internal func nextAction (_ barButton: IQBarButtonItem) {
|
||||
|
||||
//If user wants to play input Click sound.
|
||||
if shouldPlayInputClicks {
|
||||
//Play Input Click Sound.
|
||||
UIDevice.current.playInputClick()
|
||||
}
|
||||
|
||||
guard canGoNext, let textFieldRetain = textFieldView else {
|
||||
return
|
||||
}
|
||||
|
||||
let isAcceptAsFirstResponder = goNext()
|
||||
|
||||
var invocation = barButton.invocation
|
||||
var sender = textFieldRetain
|
||||
|
||||
//Handling search bar special case
|
||||
do {
|
||||
if let searchBar = textFieldRetain.textFieldSearchBar() {
|
||||
invocation = searchBar.keyboardToolbar.nextBarButton.invocation
|
||||
sender = searchBar
|
||||
}
|
||||
}
|
||||
|
||||
if isAcceptAsFirstResponder {
|
||||
invocation?.invoke(from: sender)
|
||||
}
|
||||
}
|
||||
|
||||
/** doneAction. Resigning current textField. */
|
||||
@objc internal func doneAction (_ barButton: IQBarButtonItem) {
|
||||
|
||||
//If user wants to play input Click sound.
|
||||
if shouldPlayInputClicks {
|
||||
//Play Input Click Sound.
|
||||
UIDevice.current.playInputClick()
|
||||
}
|
||||
|
||||
guard let textFieldRetain = textFieldView else {
|
||||
return
|
||||
}
|
||||
|
||||
//Resign textFieldView.
|
||||
let isResignedFirstResponder = resignFirstResponder()
|
||||
|
||||
var invocation = barButton.invocation
|
||||
var sender = textFieldRetain
|
||||
|
||||
//Handling search bar special case
|
||||
do {
|
||||
if let searchBar = textFieldRetain.textFieldSearchBar() {
|
||||
invocation = searchBar.keyboardToolbar.doneBarButton.invocation
|
||||
sender = searchBar
|
||||
}
|
||||
}
|
||||
|
||||
if isResignedFirstResponder {
|
||||
invocation?.invoke(from: sender)
|
||||
}
|
||||
}
|
||||
}
|
||||
365
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UIKeyboardNotification.swift
generated
Normal file
365
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UIKeyboardNotification.swift
generated
Normal file
@@ -0,0 +1,365 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
227
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UITextFieldViewNotification.swift
generated
Normal file
227
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager+UITextFieldViewNotification.swift
generated
Normal file
@@ -0,0 +1,227 @@
|
||||
//
|
||||
// IQKeyboardManager+UITextFieldViewNotification.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: UITextField/UITextView Notifications
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
internal extension IQKeyboardManager {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var textFieldView = "textFieldView"
|
||||
static var topViewBeginOrigin = "topViewBeginOrigin"
|
||||
static var rootViewController = "rootViewController"
|
||||
static var rootViewControllerWhilePopGestureRecognizerActive = "rootViewControllerWhilePopGestureRecognizerActive"
|
||||
static var topViewBeginOriginWhilePopGestureRecognizerActive = "topViewBeginOriginWhilePopGestureRecognizerActive"
|
||||
}
|
||||
|
||||
/** To save UITextField/UITextView object voa textField/textView notifications. */
|
||||
weak var textFieldView: UIView? {
|
||||
get {
|
||||
return (objc_getAssociatedObject(self, &AssociatedKeys.textFieldView) as? WeakObjectContainer)?.object as? UIView
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.textFieldView, WeakObjectContainer(object: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
var topViewBeginOrigin: CGPoint {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.topViewBeginOrigin) as? CGPoint ?? IQKeyboardManager.kIQCGPointInvalid
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.topViewBeginOrigin, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** To save rootViewController */
|
||||
weak var rootViewController: UIViewController? {
|
||||
get {
|
||||
return (objc_getAssociatedObject(self, &AssociatedKeys.rootViewController) as? WeakObjectContainer)?.object as? UIViewController
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.rootViewController, WeakObjectContainer(object: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** To overcome with popGestureRecognizer issue Bug ID: #1361 */
|
||||
weak var rootViewControllerWhilePopGestureRecognizerActive: UIViewController? {
|
||||
get {
|
||||
return (objc_getAssociatedObject(self, &AssociatedKeys.rootViewControllerWhilePopGestureRecognizerActive) as? WeakObjectContainer)?.object as? UIViewController
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.rootViewControllerWhilePopGestureRecognizerActive, WeakObjectContainer(object: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
var topViewBeginOriginWhilePopGestureRecognizerActive: CGPoint {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.topViewBeginOriginWhilePopGestureRecognizerActive) as? CGPoint ?? IQKeyboardManager.kIQCGPointInvalid
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.topViewBeginOriginWhilePopGestureRecognizerActive, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
/** UITextFieldTextDidBeginEditingNotification, UITextViewTextDidBeginEditingNotification. Fetching UITextFieldView object. */
|
||||
@objc func textFieldViewDidBeginEditing(_ notification: Notification) {
|
||||
|
||||
guard let object = notification.object as? UIView, let isKeyWindow = object.window?.isKeyWindow, isKeyWindow else {
|
||||
return
|
||||
}
|
||||
|
||||
let startTime = CACurrentMediaTime()
|
||||
showLog("📝>>>>> \(#function) started >>>>>", indentation: 1)
|
||||
showLog("Notification Object:\(notification.object ?? "NULL")")
|
||||
|
||||
// Getting object
|
||||
textFieldView = notification.object as? UIView
|
||||
|
||||
if overrideKeyboardAppearance, let textInput = textFieldView as? UITextInput, textInput.keyboardAppearance != keyboardAppearance {
|
||||
//Setting textField keyboard appearance and reloading inputViews.
|
||||
if let textFieldView = textFieldView as? UITextField {
|
||||
textFieldView.keyboardAppearance = keyboardAppearance
|
||||
} else if let textFieldView = textFieldView as? UITextView {
|
||||
textFieldView.keyboardAppearance = keyboardAppearance
|
||||
}
|
||||
textFieldView?.reloadInputViews()
|
||||
}
|
||||
|
||||
//If autoToolbar enable, then add toolbar on all the UITextField/UITextView's if required.
|
||||
if privateIsEnableAutoToolbar() {
|
||||
|
||||
//UITextView special case. Keyboard Notification is firing before textView notification so we need to resign it first and then again set it as first responder to add toolbar on it.
|
||||
if let textView = textFieldView as? UIScrollView, textView.responds(to: #selector(getter: UITextView.isEditable)),
|
||||
textView.inputAccessoryView == nil {
|
||||
|
||||
UIView.animate(withDuration: 0.00001, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
self.addToolbarIfRequired()
|
||||
|
||||
}, completion: { (_) -> Void in
|
||||
|
||||
//On textView toolbar didn't appear on first time, so forcing textView to reload it's inputViews.
|
||||
textView.reloadInputViews()
|
||||
})
|
||||
} else {
|
||||
//Adding toolbar
|
||||
addToolbarIfRequired()
|
||||
}
|
||||
} else {
|
||||
removeToolbarIfRequired()
|
||||
}
|
||||
|
||||
resignFirstResponderGesture.isEnabled = privateShouldResignOnTouchOutside()
|
||||
textFieldView?.window?.addGestureRecognizer(resignFirstResponderGesture) // (Enhancement ID: #14)
|
||||
|
||||
if privateIsEnabled() == false {
|
||||
restorePosition()
|
||||
topViewBeginOrigin = IQKeyboardManager.kIQCGPointInvalid
|
||||
} else {
|
||||
if topViewBeginOrigin.equalTo(IQKeyboardManager.kIQCGPointInvalid) { // (Bug ID: #5)
|
||||
|
||||
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 textFieldView is inside ignored responder then do nothing. (Bug ID: #37, #74, #76)
|
||||
//See notes:- 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)
|
||||
}
|
||||
|
||||
/** UITextFieldTextDidEndEditingNotification, UITextViewTextDidEndEditingNotification. Removing fetched object. */
|
||||
@objc func textFieldViewDidEndEditing(_ notification: Notification) {
|
||||
|
||||
guard let object = notification.object as? UIView, let isKeyWindow = object.window?.isKeyWindow, isKeyWindow else {
|
||||
return
|
||||
}
|
||||
|
||||
let startTime = CACurrentMediaTime()
|
||||
showLog("📝>>>>> \(#function) started >>>>>", indentation: 1)
|
||||
showLog("Notification Object:\(notification.object ?? "NULL")")
|
||||
|
||||
//Removing gesture recognizer (Enhancement ID: #14)
|
||||
textFieldView?.window?.removeGestureRecognizer(resignFirstResponderGesture)
|
||||
|
||||
// We check if there's a change in original frame or not.
|
||||
|
||||
if let textView = textFieldView as? UIScrollView, textView.responds(to: #selector(getter: UITextView.isEditable)) {
|
||||
|
||||
if isTextViewContentInsetChanged {
|
||||
self.isTextViewContentInsetChanged = false
|
||||
|
||||
if textView.contentInset != self.startingTextViewContentInsets {
|
||||
self.showLog("Restoring textView.contentInset to: \(self.startingTextViewContentInsets)")
|
||||
|
||||
UIView.animate(withDuration: animationDuration, delay: 0, options: animationCurve, animations: { () -> Void in
|
||||
|
||||
//Setting textField to it's initial contentInset
|
||||
textView.contentInset = self.startingTextViewContentInsets
|
||||
textView.scrollIndicatorInsets = self.startingTextViewScrollIndicatorInsets
|
||||
|
||||
}, completion: { (_) -> Void in })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Setting object to nil
|
||||
#if swift(>=5.7)
|
||||
if #available(iOS 16.0, *), let textView = object as? UITextView, textView.isFindInteractionEnabled {
|
||||
//Not setting it nil, because it may be doing find interaction.
|
||||
//As of now, here textView.findInteraction?.isFindNavigatorVisible returns false
|
||||
//So there is no way to detect if this is dismissed due to findInteraction
|
||||
} else {
|
||||
textFieldView = nil
|
||||
}
|
||||
#else
|
||||
textFieldView = nil
|
||||
#endif
|
||||
|
||||
let elapsedTime = CACurrentMediaTime() - startTime
|
||||
showLog("📝<<<<< \(#function) ended: \(elapsedTime) seconds <<<<<", indentation: -1)
|
||||
}
|
||||
}
|
||||
424
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager.swift
generated
Normal file
424
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardManager.swift
generated
Normal file
@@ -0,0 +1,424 @@
|
||||
//
|
||||
// IQKeyboardManager.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
|
||||
import CoreGraphics
|
||||
import QuartzCore
|
||||
|
||||
// MARK: IQToolbar tags
|
||||
|
||||
/**
|
||||
Codeless drop-in universal library allows to prevent issues of keyboard sliding up and cover UITextField/UITextView. Neither need to write any code nor any setup required and much more. A generic version of KeyboardManagement. https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
|
||||
*/
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public final class IQKeyboardManager: NSObject {
|
||||
|
||||
/**
|
||||
Returns the default singleton instance.
|
||||
*/
|
||||
@objc public static let shared = IQKeyboardManager()
|
||||
|
||||
/**
|
||||
Invalid point value.
|
||||
*/
|
||||
internal static let kIQCGPointInvalid = CGPoint.init(x: CGFloat.greatestFiniteMagnitude, y: CGFloat.greatestFiniteMagnitude)
|
||||
|
||||
// MARK: UIKeyboard handling
|
||||
|
||||
/**
|
||||
Enable/disable managing distance between keyboard and textField. Default is YES(Enabled when class loads in `+(void)load` method).
|
||||
*/
|
||||
@objc public var enable = false {
|
||||
|
||||
didSet {
|
||||
//If not enable, enable it.
|
||||
if enable, !oldValue {
|
||||
//If keyboard is currently showing. Sending a fake notification for keyboardWillHide to retain view's original position.
|
||||
if let notification = keyboardShowNotification {
|
||||
keyboardWillShow(notification)
|
||||
}
|
||||
showLog("Enabled")
|
||||
} else if !enable, oldValue { //If not disable, desable it.
|
||||
keyboardWillHide(nil)
|
||||
showLog("Disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
To set keyboard distance from textField. can't be less than zero. Default is 10.0.
|
||||
*/
|
||||
@objc public var keyboardDistanceFromTextField: CGFloat = 10.0
|
||||
|
||||
// MARK: IQToolbar handling
|
||||
|
||||
/**
|
||||
Automatic add the IQToolbar functionality. Default is YES.
|
||||
*/
|
||||
@objc public var enableAutoToolbar = true {
|
||||
didSet {
|
||||
privateIsEnableAutoToolbar() ? addToolbarIfRequired() : removeToolbarIfRequired()
|
||||
|
||||
let enableToolbar = enableAutoToolbar ? "Yes" : "NO"
|
||||
|
||||
showLog("enableAutoToolbar: \(enableToolbar)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
IQAutoToolbarBySubviews: Creates Toolbar according to subview's hirarchy of Textfield's in view.
|
||||
IQAutoToolbarByTag: Creates Toolbar according to tag property of TextField's.
|
||||
IQAutoToolbarByPosition: Creates Toolbar according to the y,x position of textField in it's superview coordinate.
|
||||
|
||||
Default is IQAutoToolbarBySubviews.
|
||||
*/
|
||||
AutoToolbar managing behaviour. Default is IQAutoToolbarBySubviews.
|
||||
*/
|
||||
@objc public var toolbarManageBehaviour = IQAutoToolbarManageBehaviour.bySubviews
|
||||
|
||||
/**
|
||||
If YES, then uses textField's tintColor property for IQToolbar, otherwise tint color is default. Default is NO.
|
||||
*/
|
||||
@objc public var shouldToolbarUsesTextFieldTintColor = false
|
||||
|
||||
/**
|
||||
This is used for toolbar.tintColor when textfield.keyboardAppearance is UIKeyboardAppearanceDefault. If shouldToolbarUsesTextFieldTintColor is YES then this property is ignored. Default is nil and uses black color.
|
||||
*/
|
||||
@objc public var toolbarTintColor: UIColor?
|
||||
|
||||
/**
|
||||
This is used for toolbar.barTintColor. Default is nil.
|
||||
*/
|
||||
@objc public var toolbarBarTintColor: UIColor?
|
||||
|
||||
/**
|
||||
IQPreviousNextDisplayModeDefault: Show NextPrevious when there are more than 1 textField otherwise hide.
|
||||
IQPreviousNextDisplayModeAlwaysHide: Do not show NextPrevious buttons in any case.
|
||||
IQPreviousNextDisplayModeAlwaysShow: Always show nextPrevious buttons, if there are more than 1 textField then both buttons will be visible but will be shown as disabled.
|
||||
*/
|
||||
@objc public var previousNextDisplayMode = IQPreviousNextDisplayMode.default
|
||||
|
||||
/**
|
||||
Toolbar previous/next/done button icon, If nothing is provided then check toolbarDoneBarButtonItemText to draw done button.
|
||||
*/
|
||||
@objc public var toolbarPreviousBarButtonItemImage: UIImage?
|
||||
@objc public var toolbarNextBarButtonItemImage: UIImage?
|
||||
@objc public var toolbarDoneBarButtonItemImage: UIImage?
|
||||
|
||||
/**
|
||||
Toolbar previous/next/done button text, If nothing is provided then system default 'UIBarButtonSystemItemDone' will be used.
|
||||
*/
|
||||
@objc public var toolbarPreviousBarButtonItemText: String?
|
||||
@objc public var toolbarPreviousBarButtonItemAccessibilityLabel: String?
|
||||
@objc public var toolbarNextBarButtonItemText: String?
|
||||
@objc public var toolbarNextBarButtonItemAccessibilityLabel: String?
|
||||
@objc public var toolbarDoneBarButtonItemText: String?
|
||||
@objc public var toolbarDoneBarButtonItemAccessibilityLabel: String?
|
||||
|
||||
/**
|
||||
If YES, then it add the textField's placeholder text on IQToolbar. Default is YES.
|
||||
*/
|
||||
@objc public var shouldShowToolbarPlaceholder = true
|
||||
|
||||
/**
|
||||
Placeholder Font. Default is nil.
|
||||
*/
|
||||
@objc public var placeholderFont: UIFont?
|
||||
|
||||
/**
|
||||
Placeholder Color. Default is nil. Which means lightGray
|
||||
*/
|
||||
@objc public var placeholderColor: UIColor?
|
||||
|
||||
/**
|
||||
Placeholder Button Color when it's treated as button. Default is nil.
|
||||
*/
|
||||
@objc public var placeholderButtonColor: UIColor?
|
||||
|
||||
// MARK: UIKeyboard appearance overriding
|
||||
|
||||
/**
|
||||
Override the keyboardAppearance for all textField/textView. Default is NO.
|
||||
*/
|
||||
@objc public var overrideKeyboardAppearance = false
|
||||
|
||||
/**
|
||||
If overrideKeyboardAppearance is YES, then all the textField keyboardAppearance is set using this property.
|
||||
*/
|
||||
@objc public var keyboardAppearance = UIKeyboardAppearance.default
|
||||
|
||||
// MARK: UITextField/UITextView Next/Previous/Resign handling
|
||||
|
||||
/**
|
||||
Resigns Keyboard on touching outside of UITextField/View. Default is NO.
|
||||
*/
|
||||
@objc public var shouldResignOnTouchOutside = false {
|
||||
|
||||
didSet {
|
||||
resignFirstResponderGesture.isEnabled = privateShouldResignOnTouchOutside()
|
||||
|
||||
let shouldResign = shouldResignOnTouchOutside ? "Yes" : "NO"
|
||||
|
||||
showLog("shouldResignOnTouchOutside: \(shouldResign)")
|
||||
}
|
||||
}
|
||||
|
||||
/** TapGesture to resign keyboard on view's touch. It's a readonly property and exposed only for adding/removing dependencies if your added gesture does have collision with this one */
|
||||
@objc lazy public var resignFirstResponderGesture: UITapGestureRecognizer = {
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapRecognized(_:)))
|
||||
tapGesture.cancelsTouchesInView = false
|
||||
tapGesture.delegate = self
|
||||
|
||||
return tapGesture
|
||||
}()
|
||||
|
||||
/*******************************************/
|
||||
|
||||
/**
|
||||
Resigns currently first responder field.
|
||||
*/
|
||||
@objc @discardableResult public func resignFirstResponder() -> Bool {
|
||||
|
||||
guard let textFieldRetain = textFieldView else {
|
||||
return false
|
||||
}
|
||||
|
||||
//Resigning first responder
|
||||
guard textFieldRetain.resignFirstResponder() else {
|
||||
showLog("Refuses to resign first responder: \(textFieldRetain)")
|
||||
// If it refuses then becoming it as first responder again. (Bug ID: #96)
|
||||
//If it refuses to resign then becoming it first responder again for getting notifications callback.
|
||||
textFieldRetain.becomeFirstResponder()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: UISound handling
|
||||
|
||||
/**
|
||||
If YES, then it plays inputClick sound on next/previous/done click.
|
||||
*/
|
||||
@objc public var shouldPlayInputClicks = true
|
||||
|
||||
// MARK: UIAnimation handling
|
||||
|
||||
/**
|
||||
If YES, then calls 'setNeedsLayout' and 'layoutIfNeeded' on any frame update of to viewController's view.
|
||||
*/
|
||||
@objc public var layoutIfNeededOnUpdate = false
|
||||
|
||||
// MARK: Class Level disabling methods
|
||||
|
||||
/**
|
||||
Disable distance handling within the scope of disabled distance handling viewControllers classes. Within this scope, 'enabled' property is ignored. Class should be kind of UIViewController.
|
||||
*/
|
||||
@objc public var disabledDistanceHandlingClasses = [UIViewController.Type]()
|
||||
|
||||
/**
|
||||
Enable distance handling within the scope of enabled distance handling viewControllers classes. Within this scope, 'enabled' property is ignored. Class should be kind of UIViewController. If same Class is added in disabledDistanceHandlingClasses list, then enabledDistanceHandlingClasses will be ignored.
|
||||
*/
|
||||
@objc public var enabledDistanceHandlingClasses = [UIViewController.Type]()
|
||||
|
||||
/**
|
||||
Disable automatic toolbar creation within the scope of disabled toolbar viewControllers classes. Within this scope, 'enableAutoToolbar' property is ignored. Class should be kind of UIViewController.
|
||||
*/
|
||||
@objc public var disabledToolbarClasses = [UIViewController.Type]()
|
||||
|
||||
/**
|
||||
Enable automatic toolbar creation within the scope of enabled toolbar viewControllers classes. Within this scope, 'enableAutoToolbar' property is ignored. Class should be kind of UIViewController. If same Class is added in disabledToolbarClasses list, then enabledToolbarClasses will be ignore.
|
||||
*/
|
||||
@objc public var enabledToolbarClasses = [UIViewController.Type]()
|
||||
|
||||
/**
|
||||
Allowed subclasses of UIView to add all inner textField, this will allow to navigate between textField contains in different superview. Class should be kind of UIView.
|
||||
*/
|
||||
@objc public var toolbarPreviousNextAllowedClasses = [UIView.Type]()
|
||||
|
||||
/**
|
||||
Disabled classes to ignore 'shouldResignOnTouchOutside' property, Class should be kind of UIViewController.
|
||||
*/
|
||||
@objc public var disabledTouchResignedClasses = [UIViewController.Type]()
|
||||
|
||||
/**
|
||||
Enabled classes to forcefully enable 'shouldResignOnTouchOutsite' property. Class should be kind of UIViewController. If same Class is added in disabledTouchResignedClasses list, then enabledTouchResignedClasses will be ignored.
|
||||
*/
|
||||
@objc public var enabledTouchResignedClasses = [UIViewController.Type]()
|
||||
|
||||
/**
|
||||
if shouldResignOnTouchOutside is enabled then you can customise the behaviour to not recognise gesture touches on some specific view subclasses. Class should be kind of UIView. Default is [UIControl, UINavigationBar]
|
||||
*/
|
||||
@objc public var touchResignedGestureIgnoreClasses = [UIView.Type]()
|
||||
|
||||
// MARK: Third Party Library support
|
||||
/// Add TextField/TextView Notifications customised Notifications. For example while using YYTextView https://github.com/ibireme/YYText
|
||||
|
||||
/**
|
||||
Add/Remove customised Notification for third party customised TextField/TextView. Please be aware that the Notification object must be idential to UITextField/UITextView Notification objects and customised TextField/TextView support must be idential to UITextField/UITextView.
|
||||
@param didBeginEditingNotificationName This should be identical to UITextViewTextDidBeginEditingNotification
|
||||
@param didEndEditingNotificationName This should be identical to UITextViewTextDidEndEditingNotification
|
||||
*/
|
||||
|
||||
@objc public func registerTextFieldViewClass(_ aClass: UIView.Type, didBeginEditingNotificationName: String, didEndEditingNotificationName: String) {
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.textFieldViewDidBeginEditing(_:)), name: Notification.Name(rawValue: didBeginEditingNotificationName), object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.textFieldViewDidEndEditing(_:)), name: Notification.Name(rawValue: didEndEditingNotificationName), object: nil)
|
||||
}
|
||||
|
||||
@objc public func unregisterTextFieldViewClass(_ aClass: UIView.Type, didBeginEditingNotificationName: String, didEndEditingNotificationName: String) {
|
||||
|
||||
NotificationCenter.default.removeObserver(self, name: Notification.Name(rawValue: didBeginEditingNotificationName), object: nil)
|
||||
NotificationCenter.default.removeObserver(self, name: Notification.Name(rawValue: didEndEditingNotificationName), object: nil)
|
||||
}
|
||||
|
||||
/**************************************************************************************/
|
||||
internal struct WeakObjectContainer {
|
||||
weak var object: AnyObject?
|
||||
}
|
||||
|
||||
/**************************************************************************************/
|
||||
|
||||
// MARK: Initialization/Deinitialization
|
||||
|
||||
/* Singleton Object Initialization. */
|
||||
override init() {
|
||||
|
||||
super.init()
|
||||
|
||||
self.registerAllNotifications()
|
||||
|
||||
//Creating gesture for @shouldResignOnTouchOutside. (Enhancement ID: #14)
|
||||
resignFirstResponderGesture.isEnabled = shouldResignOnTouchOutside
|
||||
|
||||
//Loading IQToolbar, IQTitleBarButtonItem, IQBarButtonItem to fix first time keyboard appearance delay (Bug ID: #550)
|
||||
//If you experience exception breakpoint issue at below line then try these solutions https://stackoverflow.com/questions/27375640/all-exception-break-point-is-stopping-for-no-reason-on-simulator
|
||||
let textField = UITextField()
|
||||
textField.addDoneOnKeyboardWithTarget(nil, action: #selector(self.doneAction(_:)))
|
||||
textField.addPreviousNextDoneOnKeyboardWithTarget(nil, previousAction: #selector(self.previousAction(_:)), nextAction: #selector(self.nextAction(_:)), doneAction: #selector(self.doneAction(_:)))
|
||||
|
||||
disabledDistanceHandlingClasses.append(UITableViewController.self)
|
||||
disabledDistanceHandlingClasses.append(UIAlertController.self)
|
||||
disabledToolbarClasses.append(UIAlertController.self)
|
||||
disabledTouchResignedClasses.append(UIAlertController.self)
|
||||
toolbarPreviousNextAllowedClasses.append(UITableView.self)
|
||||
toolbarPreviousNextAllowedClasses.append(UICollectionView.self)
|
||||
toolbarPreviousNextAllowedClasses.append(IQPreviousNextView.self)
|
||||
touchResignedGestureIgnoreClasses.append(UIControl.self)
|
||||
touchResignedGestureIgnoreClasses.append(UINavigationBar.self)
|
||||
}
|
||||
|
||||
deinit {
|
||||
// Disable the keyboard manager.
|
||||
enable = false
|
||||
}
|
||||
|
||||
/** Getting keyWindow. */
|
||||
internal func keyWindow() -> UIWindow? {
|
||||
|
||||
if let keyWindow = textFieldView?.window {
|
||||
return keyWindow
|
||||
} else {
|
||||
|
||||
struct Static {
|
||||
/** @abstract Save keyWindow object for reuse.
|
||||
@discussion Sometimes [[UIApplication sharedApplication] keyWindow] is returning nil between the app. */
|
||||
static weak var keyWindow: UIWindow?
|
||||
}
|
||||
|
||||
var originalKeyWindow: UIWindow?
|
||||
|
||||
#if swift(>=5.1)
|
||||
if #available(iOS 13, *) {
|
||||
originalKeyWindow = UIApplication.shared.connectedScenes
|
||||
.compactMap { $0 as? UIWindowScene }
|
||||
.flatMap { $0.windows }
|
||||
.first(where: { $0.isKeyWindow })
|
||||
} else {
|
||||
originalKeyWindow = UIApplication.shared.keyWindow
|
||||
}
|
||||
#else
|
||||
originalKeyWindow = UIApplication.shared.keyWindow
|
||||
#endif
|
||||
|
||||
//If original key window is not nil and the cached keywindow is also not original keywindow then changing keywindow.
|
||||
if let originalKeyWindow = originalKeyWindow {
|
||||
Static.keyWindow = originalKeyWindow
|
||||
}
|
||||
|
||||
//Return KeyWindow
|
||||
return Static.keyWindow
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public Methods
|
||||
|
||||
/* Refreshes textField/textView position if any external changes is explicitly made by user. */
|
||||
@objc public func reloadLayoutIfNeeded() {
|
||||
|
||||
guard privateIsEnabled(),
|
||||
keyboardShowing,
|
||||
topViewBeginOrigin.equalTo(IQKeyboardManager.kIQCGPointInvalid) == false, let textFieldView = textFieldView,
|
||||
textFieldView.isAlertViewTextField() == false else {
|
||||
return
|
||||
}
|
||||
optimizedAdjustPosition()
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
extension IQKeyboardManager: UIGestureRecognizerDelegate {
|
||||
|
||||
/** Resigning on tap gesture. (Enhancement ID: #14)*/
|
||||
@objc internal func tapRecognized(_ gesture: UITapGestureRecognizer) {
|
||||
|
||||
if gesture.state == .ended {
|
||||
|
||||
//Resigning currently responder textField.
|
||||
resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
/** Note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES. */
|
||||
@objc public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/** To not detect touch events in a subclass of UIControl, these may have added their own selector for specific work */
|
||||
@objc public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
||||
// Should not recognize gesture if the clicked view is either UIControl or UINavigationBar(<Back button etc...) (Bug ID: #145)
|
||||
|
||||
for ignoreClass in touchResignedGestureIgnoreClasses {
|
||||
|
||||
if touch.view?.isKind(of: ignoreClass) ?? false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
645
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift
generated
Normal file
645
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQKeyboardReturnKeyHandler.swift
generated
Normal file
@@ -0,0 +1,645 @@
|
||||
//
|
||||
// IQKeyboardReturnKeyHandler.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
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
private final class IQTextFieldViewInfoModal: NSObject {
|
||||
|
||||
fileprivate weak var textFieldDelegate: UITextFieldDelegate?
|
||||
fileprivate weak var textViewDelegate: UITextViewDelegate?
|
||||
fileprivate weak var textFieldView: UIView?
|
||||
fileprivate var originalReturnKeyType = UIReturnKeyType.default
|
||||
|
||||
init(textFieldView: UIView?, textFieldDelegate: UITextFieldDelegate?, textViewDelegate: UITextViewDelegate?, originalReturnKeyType: UIReturnKeyType = .default) {
|
||||
self.textFieldView = textFieldView
|
||||
self.textFieldDelegate = textFieldDelegate
|
||||
self.textViewDelegate = textViewDelegate
|
||||
self.originalReturnKeyType = originalReturnKeyType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Manages the return key to work like next/done in a view hierarchy.
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public final class IQKeyboardReturnKeyHandler: NSObject {
|
||||
|
||||
// MARK: Settings
|
||||
|
||||
/**
|
||||
Delegate of textField/textView.
|
||||
*/
|
||||
@objc public weak var delegate: (UITextFieldDelegate & UITextViewDelegate)?
|
||||
|
||||
/**
|
||||
Set the last textfield return key type. Default is UIReturnKeyDefault.
|
||||
*/
|
||||
@objc public var lastTextFieldReturnKeyType: UIReturnKeyType = UIReturnKeyType.default {
|
||||
|
||||
didSet {
|
||||
|
||||
for modal in textFieldInfoCache {
|
||||
|
||||
if let view = modal.textFieldView {
|
||||
updateReturnKeyTypeOnTextField(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Initialization/Deinitialization
|
||||
|
||||
@objc public override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
/**
|
||||
Add all the textFields available in UIViewController's view.
|
||||
*/
|
||||
@objc public init(controller: UIViewController) {
|
||||
super.init()
|
||||
|
||||
addResponderFromView(controller.view)
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
for modal in textFieldInfoCache {
|
||||
|
||||
if let textField = modal.textFieldView as? UITextField {
|
||||
textField.returnKeyType = modal.originalReturnKeyType
|
||||
|
||||
textField.delegate = modal.textFieldDelegate
|
||||
|
||||
} else if let textView = modal.textFieldView as? UITextView {
|
||||
|
||||
textView.returnKeyType = modal.originalReturnKeyType
|
||||
|
||||
textView.delegate = modal.textViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
textFieldInfoCache.removeAll()
|
||||
}
|
||||
|
||||
// MARK: Private variables
|
||||
private var textFieldInfoCache = [IQTextFieldViewInfoModal]()
|
||||
|
||||
// MARK: Private Functions
|
||||
private func textFieldViewCachedInfo(_ textField: UIView) -> IQTextFieldViewInfoModal? {
|
||||
|
||||
for modal in textFieldInfoCache {
|
||||
|
||||
if let view = modal.textFieldView {
|
||||
|
||||
if view == textField {
|
||||
return modal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private func updateReturnKeyTypeOnTextField(_ view: UIView) {
|
||||
var superConsideredView: UIView?
|
||||
|
||||
//If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347)
|
||||
for disabledClass in IQKeyboardManager.shared.toolbarPreviousNextAllowedClasses {
|
||||
|
||||
superConsideredView = view.superviewOfClassType(disabledClass)
|
||||
|
||||
if superConsideredView != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var textFields = [UIView]()
|
||||
|
||||
//If there is a tableView in view's hierarchy, then fetching all it's subview that responds.
|
||||
if let unwrappedTableView = superConsideredView { // (Enhancement ID: #22)
|
||||
textFields = unwrappedTableView.deepResponderViews()
|
||||
} else { //Otherwise fetching all the siblings
|
||||
|
||||
textFields = view.responderSiblings()
|
||||
|
||||
//Sorting textFields according to behaviour
|
||||
switch IQKeyboardManager.shared.toolbarManageBehaviour {
|
||||
//If needs to sort it by tag
|
||||
case .byTag: textFields = textFields.sortedArrayByTag()
|
||||
//If needs to sort it by Position
|
||||
case .byPosition: textFields = textFields.sortedArrayByPosition()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
if let lastView = textFields.last {
|
||||
|
||||
if let textField = view as? UITextField {
|
||||
|
||||
//If it's the last textField in responder view, else next
|
||||
textField.returnKeyType = (view == lastView) ? lastTextFieldReturnKeyType: UIReturnKeyType.next
|
||||
} else if let textView = view as? UITextView {
|
||||
|
||||
//If it's the last textField in responder view, else next
|
||||
textView.returnKeyType = (view == lastView) ? lastTextFieldReturnKeyType: UIReturnKeyType.next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Registering/Unregistering textFieldView
|
||||
|
||||
/**
|
||||
Should pass UITextField/UITextView intance. Assign textFieldView delegate to self, change it's returnKeyType.
|
||||
|
||||
@param view UITextField/UITextView object to register.
|
||||
*/
|
||||
@objc public func addTextFieldView(_ view: UIView) {
|
||||
|
||||
let modal = IQTextFieldViewInfoModal(textFieldView: view, textFieldDelegate: nil, textViewDelegate: nil)
|
||||
|
||||
if let textField = view as? UITextField {
|
||||
|
||||
modal.originalReturnKeyType = textField.returnKeyType
|
||||
modal.textFieldDelegate = textField.delegate
|
||||
textField.delegate = self
|
||||
|
||||
} else if let textView = view as? UITextView {
|
||||
|
||||
modal.originalReturnKeyType = textView.returnKeyType
|
||||
modal.textViewDelegate = textView.delegate
|
||||
textView.delegate = self
|
||||
}
|
||||
|
||||
textFieldInfoCache.append(modal)
|
||||
}
|
||||
|
||||
/**
|
||||
Should pass UITextField/UITextView intance. Restore it's textFieldView delegate and it's returnKeyType.
|
||||
|
||||
@param view UITextField/UITextView object to unregister.
|
||||
*/
|
||||
@objc public func removeTextFieldView(_ view: UIView) {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(view) {
|
||||
|
||||
if let textField = view as? UITextField {
|
||||
|
||||
textField.returnKeyType = modal.originalReturnKeyType
|
||||
textField.delegate = modal.textFieldDelegate
|
||||
} else if let textView = view as? UITextView {
|
||||
|
||||
textView.returnKeyType = modal.originalReturnKeyType
|
||||
textView.delegate = modal.textViewDelegate
|
||||
}
|
||||
|
||||
if let index = textFieldInfoCache.firstIndex(where: { $0.textFieldView == view}) {
|
||||
|
||||
textFieldInfoCache.remove(at: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Add all the UITextField/UITextView responderView's.
|
||||
|
||||
@param view UIView object to register all it's responder subviews.
|
||||
*/
|
||||
@objc public func addResponderFromView(_ view: UIView) {
|
||||
|
||||
let textFields = view.deepResponderViews()
|
||||
|
||||
for textField in textFields {
|
||||
|
||||
addTextFieldView(textField)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Remove all the UITextField/UITextView responderView's.
|
||||
|
||||
@param view UIView object to unregister all it's responder subviews.
|
||||
*/
|
||||
@objc public func removeResponderFromView(_ view: UIView) {
|
||||
|
||||
let textFields = view.deepResponderViews()
|
||||
|
||||
for textField in textFields {
|
||||
|
||||
removeTextFieldView(textField)
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult private func goToNextResponderOrResign(_ view: UIView) -> Bool {
|
||||
|
||||
var superConsideredView: UIView?
|
||||
|
||||
//If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347)
|
||||
for disabledClass in IQKeyboardManager.shared.toolbarPreviousNextAllowedClasses {
|
||||
|
||||
superConsideredView = view.superviewOfClassType(disabledClass)
|
||||
|
||||
if superConsideredView != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var textFields = [UIView]()
|
||||
|
||||
//If there is a tableView in view's hierarchy, then fetching all it's subview that responds.
|
||||
if let unwrappedTableView = superConsideredView { // (Enhancement ID: #22)
|
||||
textFields = unwrappedTableView.deepResponderViews()
|
||||
} else { //Otherwise fetching all the siblings
|
||||
|
||||
textFields = view.responderSiblings()
|
||||
|
||||
//Sorting textFields according to behaviour
|
||||
switch IQKeyboardManager.shared.toolbarManageBehaviour {
|
||||
//If needs to sort it by tag
|
||||
case .byTag: textFields = textFields.sortedArrayByTag()
|
||||
//If needs to sort it by Position
|
||||
case .byPosition: textFields = textFields.sortedArrayByPosition()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//Getting index of current textField.
|
||||
if let index = textFields.firstIndex(of: view) {
|
||||
//If it is not last textField. then it's next object becomeFirstResponder.
|
||||
if index < (textFields.count - 1) {
|
||||
|
||||
let nextTextField = textFields[index+1]
|
||||
nextTextField.becomeFirstResponder()
|
||||
return false
|
||||
} else {
|
||||
|
||||
view.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UITextFieldDelegate
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
extension IQKeyboardReturnKeyHandler: UITextFieldDelegate {
|
||||
|
||||
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldBeginEditing(_:))) {
|
||||
return unwrapDelegate.textFieldShouldBeginEditing?(textField) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:))) {
|
||||
return unwrapDelegate.textFieldShouldEndEditing?(textField) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
updateReturnKeyTypeOnTextField(textField)
|
||||
|
||||
var aDelegate: UITextFieldDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(textField) {
|
||||
aDelegate = modal.textFieldDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textFieldDidBeginEditing?(textField)
|
||||
}
|
||||
|
||||
@objc public func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
|
||||
var aDelegate: UITextFieldDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(textField) {
|
||||
aDelegate = modal.textFieldDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textFieldDidEndEditing?(textField)
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
@objc public func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) {
|
||||
|
||||
var aDelegate: UITextFieldDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(textField) {
|
||||
aDelegate = modal.textFieldDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textFieldDidEndEditing?(textField, reason: reason)
|
||||
}
|
||||
|
||||
@objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textField(_:shouldChangeCharactersIn:replacementString:))) {
|
||||
return unwrapDelegate.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldClear(_ textField: UITextField) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldClear(_:))) {
|
||||
return unwrapDelegate.textFieldShouldClear?(textField) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
|
||||
var shouldReturn = true
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(textField)?.textFieldDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(UITextFieldDelegate.textFieldShouldReturn(_:))) {
|
||||
shouldReturn = unwrapDelegate.textFieldShouldReturn?(textField) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shouldReturn {
|
||||
goToNextResponderOrResign(textField)
|
||||
return true
|
||||
} else {
|
||||
return goToNextResponderOrResign(textField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UITextViewDelegate
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
extension IQKeyboardReturnKeyHandler: UITextViewDelegate {
|
||||
|
||||
@objc public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(textView)?.textViewDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textViewShouldBeginEditing(_:))) {
|
||||
return unwrapDelegate.textViewShouldBeginEditing?(textView) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(textView)?.textViewDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textViewShouldEndEditing(_:))) {
|
||||
return unwrapDelegate.textViewShouldEndEditing?(textView) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func textViewDidBeginEditing(_ textView: UITextView) {
|
||||
updateReturnKeyTypeOnTextField(textView)
|
||||
|
||||
var aDelegate: UITextViewDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(textView) {
|
||||
aDelegate = modal.textViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textViewDidBeginEditing?(textView)
|
||||
}
|
||||
|
||||
@objc public func textViewDidEndEditing(_ textView: UITextView) {
|
||||
|
||||
var aDelegate: UITextViewDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(textView) {
|
||||
aDelegate = modal.textViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textViewDidEndEditing?(textView)
|
||||
}
|
||||
|
||||
@objc public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
|
||||
var shouldReturn = true
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(textView)?.textViewDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(UITextViewDelegate.textView(_:shouldChangeTextIn:replacementText:))) {
|
||||
shouldReturn = (unwrapDelegate.textView?(textView, shouldChangeTextIn: range, replacementText: text)) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shouldReturn, text == "\n" {
|
||||
shouldReturn = goToNextResponderOrResign(textView)
|
||||
}
|
||||
|
||||
return shouldReturn
|
||||
}
|
||||
|
||||
@objc public func textViewDidChange(_ textView: UITextView) {
|
||||
|
||||
var aDelegate: UITextViewDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(textView) {
|
||||
aDelegate = modal.textViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textViewDidChange?(textView)
|
||||
}
|
||||
|
||||
@objc public func textViewDidChangeSelection(_ textView: UITextView) {
|
||||
|
||||
var aDelegate: UITextViewDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(textView) {
|
||||
aDelegate = modal.textViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textViewDidChangeSelection?(textView)
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
@objc public func textView(_ aTextView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(textView as (UITextView, URL, NSRange, UITextItemInteraction) -> Bool)) {
|
||||
return unwrapDelegate.textView?(aTextView, shouldInteractWith: URL, in: characterRange, interaction: interaction) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
@objc public func textView(_ aTextView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(textView as (UITextView, NSTextAttachment, NSRange, UITextItemInteraction) -> Bool)) {
|
||||
return unwrapDelegate.textView?(aTextView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@available(iOS, deprecated: 10.0)
|
||||
@objc public func textView(_ aTextView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(textView as (UITextView, URL, NSRange) -> Bool)) {
|
||||
return unwrapDelegate.textView?(aTextView, shouldInteractWith: URL, in: characterRange) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@available(iOS, deprecated: 10.0)
|
||||
@objc public func textView(_ aTextView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange) -> Bool {
|
||||
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(textView as (UITextView, NSTextAttachment, NSRange) -> Bool)) {
|
||||
return unwrapDelegate.textView?(aTextView, shouldInteractWith: textAttachment, in: characterRange) ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
#if swift(>=5.7)
|
||||
@available(iOS 16.0, *)
|
||||
public func textView(_ aTextView: UITextView, editMenuForTextIn range: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? {
|
||||
if delegate == nil {
|
||||
|
||||
if let unwrapDelegate = textFieldViewCachedInfo(aTextView)?.textViewDelegate {
|
||||
if unwrapDelegate.responds(to: #selector(textView as (UITextView, NSRange, [UIMenuElement]) -> UIMenu?)) {
|
||||
return unwrapDelegate.textView?(aTextView, editMenuForTextIn: range, suggestedActions: suggestedActions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
public func textView(_ aTextView: UITextView, willPresentEditMenuWith animator: UIEditMenuInteractionAnimating) {
|
||||
var aDelegate: UITextViewDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(aTextView) {
|
||||
aDelegate = modal.textViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textView?(aTextView, willPresentEditMenuWith: animator)
|
||||
}
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
public func textView(_ aTextView: UITextView, willDismissEditMenuWith animator: UIEditMenuInteractionAnimating) {
|
||||
var aDelegate: UITextViewDelegate? = delegate
|
||||
|
||||
if aDelegate == nil {
|
||||
|
||||
if let modal = textFieldViewCachedInfo(aTextView) {
|
||||
aDelegate = modal.textViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
aDelegate?.textView?(aTextView, willDismissEditMenuWith: animator)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
189
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQTextView.swift
generated
Normal file
189
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQTextView/IQTextView.swift
generated
Normal file
@@ -0,0 +1,189 @@
|
||||
//
|
||||
// IQTextView.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 UIKit
|
||||
|
||||
/** @abstract UITextView with placeholder support */
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc open class IQTextView: UITextView {
|
||||
|
||||
@objc required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextView.textDidChangeNotification, object: self)
|
||||
}
|
||||
|
||||
@objc override public init(frame: CGRect, textContainer: NSTextContainer?) {
|
||||
super.init(frame: frame, textContainer: textContainer)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextView.textDidChangeNotification, object: self)
|
||||
}
|
||||
|
||||
@objc override open func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextView.textDidChangeNotification, object: self)
|
||||
}
|
||||
|
||||
deinit {
|
||||
IQ_PlaceholderLabel.removeFromSuperview()
|
||||
}
|
||||
|
||||
private var placeholderInsets: UIEdgeInsets {
|
||||
return UIEdgeInsets(top: self.textContainerInset.top, left: self.textContainerInset.left + self.textContainer.lineFragmentPadding, bottom: self.textContainerInset.bottom, right: self.textContainerInset.right + self.textContainer.lineFragmentPadding)
|
||||
}
|
||||
|
||||
private var placeholderExpectedFrame: CGRect {
|
||||
let placeholderInsets = self.placeholderInsets
|
||||
let maxWidth = self.frame.width-placeholderInsets.left-placeholderInsets.right
|
||||
let expectedSize = IQ_PlaceholderLabel.sizeThatFits(CGSize(width: maxWidth, height: self.frame.height-placeholderInsets.top-placeholderInsets.bottom))
|
||||
|
||||
return CGRect(x: placeholderInsets.left, y: placeholderInsets.top, width: maxWidth, height: expectedSize.height)
|
||||
}
|
||||
|
||||
lazy var IQ_PlaceholderLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
|
||||
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
label.numberOfLines = 0
|
||||
label.font = self.font
|
||||
label.textAlignment = self.textAlignment
|
||||
label.backgroundColor = UIColor.clear
|
||||
label.isAccessibilityElement = false
|
||||
#if swift(>=5.1)
|
||||
label.textColor = UIColor.systemGray
|
||||
#else
|
||||
label.textColor = UIColor.lightText
|
||||
#endif
|
||||
label.alpha = 0
|
||||
self.addSubview(label)
|
||||
|
||||
return label
|
||||
}()
|
||||
|
||||
/** @abstract To set textView's placeholder text color. */
|
||||
@IBInspectable open var placeholderTextColor: UIColor? {
|
||||
|
||||
get {
|
||||
return IQ_PlaceholderLabel.textColor
|
||||
}
|
||||
|
||||
set {
|
||||
IQ_PlaceholderLabel.textColor = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/** @abstract To set textView's placeholder text. Default is nil. */
|
||||
@IBInspectable open var placeholder: String? {
|
||||
|
||||
get {
|
||||
return IQ_PlaceholderLabel.text
|
||||
}
|
||||
|
||||
set {
|
||||
IQ_PlaceholderLabel.text = newValue
|
||||
refreshPlaceholder()
|
||||
}
|
||||
}
|
||||
|
||||
/** @abstract To set textView's placeholder attributed text. Default is nil. */
|
||||
open var attributedPlaceholder: NSAttributedString? {
|
||||
get {
|
||||
return IQ_PlaceholderLabel.attributedText
|
||||
}
|
||||
|
||||
set {
|
||||
IQ_PlaceholderLabel.attributedText = newValue
|
||||
refreshPlaceholder()
|
||||
}
|
||||
}
|
||||
|
||||
@objc override open func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
IQ_PlaceholderLabel.frame = placeholderExpectedFrame
|
||||
}
|
||||
|
||||
@objc internal func refreshPlaceholder() {
|
||||
|
||||
if !text.isEmpty || !attributedText.string.isEmpty {
|
||||
IQ_PlaceholderLabel.alpha = 0
|
||||
} else {
|
||||
IQ_PlaceholderLabel.alpha = 1
|
||||
}
|
||||
}
|
||||
|
||||
@objc override open var text: String! {
|
||||
|
||||
didSet {
|
||||
refreshPlaceholder()
|
||||
}
|
||||
}
|
||||
|
||||
open override var attributedText: NSAttributedString! {
|
||||
|
||||
didSet {
|
||||
refreshPlaceholder()
|
||||
}
|
||||
}
|
||||
|
||||
@objc override open var font: UIFont? {
|
||||
|
||||
didSet {
|
||||
|
||||
if let unwrappedFont = font {
|
||||
IQ_PlaceholderLabel.font = unwrappedFont
|
||||
} else {
|
||||
IQ_PlaceholderLabel.font = UIFont.systemFont(ofSize: 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc override open var textAlignment: NSTextAlignment {
|
||||
didSet {
|
||||
IQ_PlaceholderLabel.textAlignment = textAlignment
|
||||
}
|
||||
}
|
||||
|
||||
@objc override weak open var delegate: UITextViewDelegate? {
|
||||
|
||||
get {
|
||||
refreshPlaceholder()
|
||||
return super.delegate
|
||||
}
|
||||
|
||||
set {
|
||||
super.delegate = newValue
|
||||
}
|
||||
}
|
||||
|
||||
@objc override open var intrinsicContentSize: CGSize {
|
||||
guard !hasText else {
|
||||
return super.intrinsicContentSize
|
||||
}
|
||||
|
||||
var newSize = super.intrinsicContentSize
|
||||
let placeholderInsets = self.placeholderInsets
|
||||
newSize.height = placeholderExpectedFrame.height + placeholderInsets.top + placeholderInsets.bottom
|
||||
|
||||
return newSize
|
||||
}
|
||||
}
|
||||
99
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift
generated
Normal file
99
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQBarButtonItem.swift
generated
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// IQBarButtonItem.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
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc open class IQBarButtonItem: UIBarButtonItem {
|
||||
|
||||
@objc public override init() {
|
||||
super.init()
|
||||
initialize()
|
||||
}
|
||||
|
||||
@objc public required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
initialize()
|
||||
}
|
||||
|
||||
private func initialize() {
|
||||
|
||||
let states: [UIControl.State] = [.normal, .highlighted, .disabled, .focused]
|
||||
|
||||
for state in states {
|
||||
|
||||
setBackgroundImage(nil, for: state, barMetrics: .default)
|
||||
setBackgroundImage(nil, for: state, style: .plain, barMetrics: .default)
|
||||
setBackButtonBackgroundImage(nil, for: state, barMetrics: .default)
|
||||
}
|
||||
|
||||
setTitlePositionAdjustment(UIOffset(), for: .default)
|
||||
setBackgroundVerticalPositionAdjustment(0, for: .default)
|
||||
setBackButtonBackgroundVerticalPositionAdjustment(0, for: .default)
|
||||
}
|
||||
|
||||
@objc override open var tintColor: UIColor? {
|
||||
didSet {
|
||||
|
||||
var textAttributes = [NSAttributedString.Key: Any]()
|
||||
textAttributes[.foregroundColor] = tintColor
|
||||
|
||||
if let attributes = titleTextAttributes(for: .normal) {
|
||||
for (key, value) in attributes {
|
||||
textAttributes[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
setTitleTextAttributes(textAttributes, for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Boolean to know if it's a system item or custom item, we are having a limitation that we cannot override a designated initializer, so we are manually setting this property once in initialization
|
||||
*/
|
||||
@objc internal var isSystemItem = false
|
||||
|
||||
/**
|
||||
Additional target & action to do get callback action. Note that setting custom target & selector doesn't affect native functionality, this is just an additional target to get a callback.
|
||||
|
||||
@param target Target object.
|
||||
@param action Target Selector.
|
||||
*/
|
||||
@objc open func setTarget(_ target: AnyObject?, action: Selector?) {
|
||||
if let target = target, let action = action {
|
||||
invocation = IQInvocation(target, action)
|
||||
} else {
|
||||
invocation = nil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Customized Invocation to be called when button is pressed. invocation is internally created using setTarget:action: method.
|
||||
*/
|
||||
@objc open var invocation: IQInvocation?
|
||||
|
||||
deinit {
|
||||
target = nil
|
||||
}
|
||||
}
|
||||
45
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQInvocation.swift
generated
Normal file
45
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQInvocation.swift
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// IQInvocation.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 UIKit
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public final class IQInvocation: NSObject {
|
||||
@objc public weak var target: AnyObject?
|
||||
@objc public var action: Selector
|
||||
|
||||
@objc public init(_ target: AnyObject, _ action: Selector) {
|
||||
self.target = target
|
||||
self.action = action
|
||||
}
|
||||
|
||||
@objc public func invoke(from: Any) {
|
||||
if let target = target {
|
||||
UIApplication.shared.sendAction(action, to: target, from: from, for: UIEvent())
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
target = nil
|
||||
}
|
||||
}
|
||||
29
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift
generated
Normal file
29
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQPreviousNextView.swift
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// IQPreviousNextView.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 UIKit
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc open class IQPreviousNextView: UIView {
|
||||
|
||||
}
|
||||
167
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift
generated
Normal file
167
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQTitleBarButtonItem.swift
generated
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// IQTitleBarButtonItem.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 UIKit
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc open class IQTitleBarButtonItem: IQBarButtonItem {
|
||||
|
||||
@objc open var titleFont: UIFont? {
|
||||
|
||||
didSet {
|
||||
if let unwrappedFont = titleFont {
|
||||
titleButton?.titleLabel?.font = unwrappedFont
|
||||
} else {
|
||||
titleButton?.titleLabel?.font = UIFont.systemFont(ofSize: 13)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc override open var title: String? {
|
||||
didSet {
|
||||
titleButton?.setTitle(title, for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
titleColor to be used for displaying button text when displaying title (disabled state).
|
||||
*/
|
||||
@objc open var titleColor: UIColor? {
|
||||
|
||||
didSet {
|
||||
|
||||
if let color = titleColor {
|
||||
titleButton?.setTitleColor(color, for: .disabled)
|
||||
} else {
|
||||
titleButton?.setTitleColor(UIColor.lightGray, for: .disabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
selectableTitleColor to be used for displaying button text when button is enabled.
|
||||
*/
|
||||
@objc open var selectableTitleColor: UIColor? {
|
||||
|
||||
didSet {
|
||||
|
||||
if let color = selectableTitleColor {
|
||||
titleButton?.setTitleColor(color, for: .normal)
|
||||
} else {
|
||||
#if swift(>=5.1)
|
||||
titleButton?.setTitleColor(UIColor.systemBlue, for: .normal)
|
||||
#else
|
||||
titleButton?.setTitleColor(UIColor(red: 0.0, green: 0.5, blue: 1.0, alpha: 1), for: .normal)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Customized Invocation to be called on title button action. titleInvocation is internally created using setTitleTarget:action: method.
|
||||
*/
|
||||
@objc override open var invocation: IQInvocation? {
|
||||
|
||||
didSet {
|
||||
|
||||
if let target = invocation?.target, let action = invocation?.action {
|
||||
self.isEnabled = true
|
||||
titleButton?.isEnabled = true
|
||||
titleButton?.addTarget(target, action: action, for: .touchUpInside)
|
||||
} else {
|
||||
self.isEnabled = false
|
||||
titleButton?.isEnabled = false
|
||||
titleButton?.removeTarget(nil, action: nil, for: .touchUpInside)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal var titleButton: UIButton?
|
||||
private var _titleView: UIView?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc public convenience init(title: String?) {
|
||||
|
||||
self.init(title: nil, style: .plain, target: nil, action: nil)
|
||||
|
||||
_titleView = UIView()
|
||||
_titleView?.backgroundColor = UIColor.clear
|
||||
|
||||
titleButton = UIButton(type: .system)
|
||||
titleButton?.isEnabled = false
|
||||
titleButton?.titleLabel?.numberOfLines = 3
|
||||
titleButton?.setTitleColor(UIColor.lightGray, for: .disabled)
|
||||
#if swift(>=5.1)
|
||||
titleButton?.setTitleColor(UIColor.systemBlue, for: .normal)
|
||||
#else
|
||||
titleButton?.setTitleColor(UIColor(red: 0.0, green: 0.5, blue: 1.0, alpha: 1), for: .normal)
|
||||
#endif
|
||||
titleButton?.backgroundColor = UIColor.clear
|
||||
titleButton?.titleLabel?.textAlignment = .center
|
||||
titleButton?.setTitle(title, for: .normal)
|
||||
titleFont = UIFont.systemFont(ofSize: 13.0)
|
||||
titleButton?.titleLabel?.font = self.titleFont
|
||||
_titleView?.addSubview(titleButton!)
|
||||
|
||||
if #available(iOS 11, *) {
|
||||
|
||||
let layoutDefaultLowPriority = UILayoutPriority(rawValue: UILayoutPriority.defaultLow.rawValue-1)
|
||||
let layoutDefaultHighPriority = UILayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue-1)
|
||||
|
||||
_titleView?.translatesAutoresizingMaskIntoConstraints = false
|
||||
_titleView?.setContentHuggingPriority(layoutDefaultLowPriority, for: .vertical)
|
||||
_titleView?.setContentHuggingPriority(layoutDefaultLowPriority, for: .horizontal)
|
||||
_titleView?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .vertical)
|
||||
_titleView?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .horizontal)
|
||||
|
||||
titleButton?.translatesAutoresizingMaskIntoConstraints = false
|
||||
titleButton?.setContentHuggingPriority(layoutDefaultLowPriority, for: .vertical)
|
||||
titleButton?.setContentHuggingPriority(layoutDefaultLowPriority, for: .horizontal)
|
||||
titleButton?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .vertical)
|
||||
titleButton?.setContentCompressionResistancePriority(layoutDefaultHighPriority, for: .horizontal)
|
||||
|
||||
let top = NSLayoutConstraint.init(item: titleButton!, attribute: .top, relatedBy: .equal, toItem: _titleView, attribute: .top, multiplier: 1, constant: 0)
|
||||
let bottom = NSLayoutConstraint.init(item: titleButton!, attribute: .bottom, relatedBy: .equal, toItem: _titleView, attribute: .bottom, multiplier: 1, constant: 0)
|
||||
let leading = NSLayoutConstraint.init(item: titleButton!, attribute: .leading, relatedBy: .equal, toItem: _titleView, attribute: .leading, multiplier: 1, constant: 0)
|
||||
let trailing = NSLayoutConstraint.init(item: titleButton!, attribute: .trailing, relatedBy: .equal, toItem: _titleView, attribute: .trailing, multiplier: 1, constant: 0)
|
||||
|
||||
_titleView?.addConstraints([top, bottom, leading, trailing])
|
||||
} else {
|
||||
_titleView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
titleButton?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
}
|
||||
|
||||
customView = _titleView
|
||||
}
|
||||
|
||||
@objc required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
deinit {
|
||||
customView = nil
|
||||
}
|
||||
}
|
||||
252
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift
generated
Normal file
252
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQToolbar.swift
generated
Normal file
@@ -0,0 +1,252 @@
|
||||
//
|
||||
// IQToolbar.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 UIKit
|
||||
|
||||
/** @abstract IQToolbar for IQKeyboardManager. */
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc open class IQToolbar: UIToolbar, UIInputViewAudioFeedback {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
@objc required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
private func initialize() {
|
||||
|
||||
sizeToFit()
|
||||
|
||||
autoresizingMask = .flexibleWidth
|
||||
self.isTranslucent = true
|
||||
self.barTintColor = nil
|
||||
|
||||
let positions: [UIBarPosition] = [.any, .bottom, .top, .topAttached]
|
||||
|
||||
for position in positions {
|
||||
|
||||
self.setBackgroundImage(nil, forToolbarPosition: position, barMetrics: .default)
|
||||
self.setShadowImage(nil, forToolbarPosition: .any)
|
||||
}
|
||||
|
||||
//Background color
|
||||
self.backgroundColor = nil
|
||||
}
|
||||
|
||||
/**
|
||||
Previous bar button of toolbar.
|
||||
*/
|
||||
private var privatePreviousBarButton: IQBarButtonItem?
|
||||
@objc open var previousBarButton: IQBarButtonItem {
|
||||
get {
|
||||
if privatePreviousBarButton == nil {
|
||||
privatePreviousBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil)
|
||||
}
|
||||
return privatePreviousBarButton!
|
||||
}
|
||||
|
||||
set (newValue) {
|
||||
privatePreviousBarButton = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Next bar button of toolbar.
|
||||
*/
|
||||
private var privateNextBarButton: IQBarButtonItem?
|
||||
@objc open var nextBarButton: IQBarButtonItem {
|
||||
get {
|
||||
if privateNextBarButton == nil {
|
||||
privateNextBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil)
|
||||
}
|
||||
return privateNextBarButton!
|
||||
}
|
||||
|
||||
set (newValue) {
|
||||
privateNextBarButton = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Title bar button of toolbar.
|
||||
*/
|
||||
private var privateTitleBarButton: IQTitleBarButtonItem?
|
||||
@objc open var titleBarButton: IQTitleBarButtonItem {
|
||||
get {
|
||||
if privateTitleBarButton == nil {
|
||||
privateTitleBarButton = IQTitleBarButtonItem(title: nil)
|
||||
privateTitleBarButton?.accessibilityLabel = "Title"
|
||||
privateTitleBarButton?.accessibilityIdentifier = privateTitleBarButton?.accessibilityLabel
|
||||
}
|
||||
return privateTitleBarButton!
|
||||
}
|
||||
|
||||
set (newValue) {
|
||||
privateTitleBarButton = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Done bar button of toolbar.
|
||||
*/
|
||||
private var privateDoneBarButton: IQBarButtonItem?
|
||||
@objc open var doneBarButton: IQBarButtonItem {
|
||||
get {
|
||||
if privateDoneBarButton == nil {
|
||||
privateDoneBarButton = IQBarButtonItem(title: nil, style: .done, target: nil, action: nil)
|
||||
}
|
||||
return privateDoneBarButton!
|
||||
}
|
||||
|
||||
set (newValue) {
|
||||
privateDoneBarButton = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Fixed space bar button of toolbar.
|
||||
*/
|
||||
private var privateFixedSpaceBarButton: IQBarButtonItem?
|
||||
@objc open var fixedSpaceBarButton: IQBarButtonItem {
|
||||
get {
|
||||
if privateFixedSpaceBarButton == nil {
|
||||
privateFixedSpaceBarButton = IQBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
|
||||
}
|
||||
privateFixedSpaceBarButton!.isSystemItem = true
|
||||
|
||||
if #available(iOS 10, *) {
|
||||
privateFixedSpaceBarButton!.width = 6
|
||||
} else {
|
||||
privateFixedSpaceBarButton!.width = 20
|
||||
}
|
||||
|
||||
return privateFixedSpaceBarButton!
|
||||
}
|
||||
|
||||
set (newValue) {
|
||||
privateFixedSpaceBarButton = newValue
|
||||
}
|
||||
}
|
||||
|
||||
@objc override open func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
var sizeThatFit = super.sizeThatFits(size)
|
||||
sizeThatFit.height = 44
|
||||
return sizeThatFit
|
||||
}
|
||||
|
||||
@objc override open var tintColor: UIColor! {
|
||||
|
||||
didSet {
|
||||
if let unwrappedItems = items {
|
||||
for item in unwrappedItems {
|
||||
item.tintColor = tintColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc override open func layoutSubviews() {
|
||||
|
||||
super.layoutSubviews()
|
||||
|
||||
if #available(iOS 11, *) {
|
||||
return
|
||||
} else if let customTitleView = titleBarButton.customView {
|
||||
var leftRect = CGRect.null
|
||||
var rightRect = CGRect.null
|
||||
var isTitleBarButtonFound = false
|
||||
|
||||
let sortedSubviews = self.subviews.sorted(by: { (view1: UIView, view2: UIView) -> Bool in
|
||||
if view1.frame.minX != view2.frame.minX {
|
||||
return view1.frame.minX < view2.frame.minX
|
||||
} else {
|
||||
return view1.frame.minY < view2.frame.minY
|
||||
}
|
||||
})
|
||||
|
||||
for barButtonItemView in sortedSubviews {
|
||||
|
||||
if isTitleBarButtonFound {
|
||||
rightRect = barButtonItemView.frame
|
||||
break
|
||||
} else if barButtonItemView === customTitleView {
|
||||
isTitleBarButtonFound = true
|
||||
//If it's UIToolbarButton or UIToolbarTextButton (which actually UIBarButtonItem)
|
||||
} else if barButtonItemView.isKind(of: UIControl.self) {
|
||||
leftRect = barButtonItemView.frame
|
||||
}
|
||||
}
|
||||
|
||||
let titleMargin: CGFloat = 16
|
||||
|
||||
let maxWidth: CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX)
|
||||
let maxHeight = self.frame.height
|
||||
|
||||
let sizeThatFits = customTitleView.sizeThatFits(CGSize(width: maxWidth, height: maxHeight))
|
||||
|
||||
var titleRect: CGRect
|
||||
|
||||
if sizeThatFits.width > 0, sizeThatFits.height > 0 {
|
||||
let width = min(sizeThatFits.width, maxWidth)
|
||||
let height = min(sizeThatFits.height, maxHeight)
|
||||
|
||||
var xPosition: CGFloat
|
||||
|
||||
if !leftRect.isNull {
|
||||
xPosition = titleMargin + leftRect.maxX + ((maxWidth - width)/2)
|
||||
} else {
|
||||
xPosition = titleMargin
|
||||
}
|
||||
|
||||
let yPosition = (maxHeight - height)/2
|
||||
|
||||
titleRect = CGRect(x: xPosition, y: yPosition, width: width, height: height)
|
||||
} else {
|
||||
|
||||
var xPosition: CGFloat
|
||||
|
||||
if !leftRect.isNull {
|
||||
xPosition = titleMargin + leftRect.maxX
|
||||
} else {
|
||||
xPosition = titleMargin
|
||||
}
|
||||
|
||||
let width: CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX)
|
||||
|
||||
titleRect = CGRect(x: xPosition, y: 0, width: width, height: maxHeight)
|
||||
}
|
||||
|
||||
customTitleView.frame = titleRect
|
||||
}
|
||||
}
|
||||
|
||||
@objc open var enableInputClicksWhenVisible: Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
552
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift
generated
Normal file
552
Pods/IQKeyboardManagerSwift/IQKeyboardManagerSwift/IQToolbar/IQUIView+IQKeyboardToolbar.swift
generated
Normal file
@@ -0,0 +1,552 @@
|
||||
//
|
||||
// IQUIView+IQKeyboardToolbar.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 UIKit
|
||||
|
||||
/**
|
||||
IQBarButtonItemConfiguration for creating toolbar with bar button items
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public final class IQBarButtonItemConfiguration: NSObject {
|
||||
|
||||
@objc public init(barButtonSystemItem: UIBarButtonItem.SystemItem, action: Selector) {
|
||||
self.barButtonSystemItem = barButtonSystemItem
|
||||
self.image = nil
|
||||
self.title = nil
|
||||
self.action = action
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc public init(image: UIImage, action: Selector) {
|
||||
self.barButtonSystemItem = nil
|
||||
self.image = image
|
||||
self.title = nil
|
||||
self.action = action
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc public init(title: String, action: Selector) {
|
||||
self.barButtonSystemItem = nil
|
||||
self.image = nil
|
||||
self.title = title
|
||||
self.action = action
|
||||
super.init()
|
||||
}
|
||||
|
||||
public let barButtonSystemItem: UIBarButtonItem.SystemItem? //System Item to be used to instantiate bar button.
|
||||
|
||||
@objc public let image: UIImage? //Image to show on bar button item if it's not a system item.
|
||||
|
||||
@objc public let title: String? //Title to show on bar button item if it's not a system item.
|
||||
|
||||
@objc public let action: Selector? //action for bar button item. Usually 'doneAction:(IQBarButtonItem*)item'.
|
||||
}
|
||||
|
||||
/**
|
||||
UIImage category methods to get next/prev images
|
||||
*/
|
||||
// swiftlint:disable nesting
|
||||
// swiftlint:disable line_length
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public extension UIImage {
|
||||
|
||||
static func keyboardLeftImage() -> UIImage? {
|
||||
|
||||
struct Static {
|
||||
static var keyboardLeftImage: UIImage?
|
||||
}
|
||||
|
||||
if Static.keyboardLeftImage == nil {
|
||||
|
||||
let base64Data = "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"
|
||||
|
||||
if let data = Data(base64Encoded: base64Data, options: .ignoreUnknownCharacters) {
|
||||
Static.keyboardLeftImage = UIImage(data: data, scale: 3)
|
||||
}
|
||||
|
||||
//Support for RTL languages like Arabic, Persia etc... (Bug ID: #448)
|
||||
Static.keyboardLeftImage = Static.keyboardLeftImage?.imageFlippedForRightToLeftLayoutDirection()
|
||||
}
|
||||
|
||||
return Static.keyboardLeftImage
|
||||
}
|
||||
|
||||
static func keyboardRightImage() -> UIImage? {
|
||||
|
||||
struct Static {
|
||||
static var keyboardRightImage: UIImage?
|
||||
}
|
||||
|
||||
if Static.keyboardRightImage == nil {
|
||||
|
||||
let base64Data = "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"
|
||||
|
||||
if let data = Data(base64Encoded: base64Data, options: .ignoreUnknownCharacters) {
|
||||
Static.keyboardRightImage = UIImage(data: data, scale: 3)
|
||||
}
|
||||
|
||||
//Support for RTL languages like Arabic, Persia etc... (Bug ID: #448)
|
||||
Static.keyboardRightImage = Static.keyboardRightImage?.imageFlippedForRightToLeftLayoutDirection()
|
||||
}
|
||||
|
||||
return Static.keyboardRightImage
|
||||
}
|
||||
|
||||
static func keyboardUpImage() -> UIImage? {
|
||||
|
||||
struct Static {
|
||||
static var keyboardUpImage: UIImage?
|
||||
}
|
||||
|
||||
if Static.keyboardUpImage == nil {
|
||||
|
||||
let base64Data = "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"
|
||||
|
||||
if let data = Data(base64Encoded: base64Data, options: .ignoreUnknownCharacters) {
|
||||
Static.keyboardUpImage = UIImage(data: data, scale: 3)
|
||||
}
|
||||
|
||||
//Support for RTL languages like Arabic, Persia etc... (Bug ID: #448)
|
||||
Static.keyboardUpImage = Static.keyboardUpImage?.imageFlippedForRightToLeftLayoutDirection()
|
||||
}
|
||||
|
||||
return Static.keyboardUpImage
|
||||
}
|
||||
|
||||
static func keyboardDownImage() -> UIImage? {
|
||||
|
||||
struct Static {
|
||||
static var keyboardDownImage: UIImage?
|
||||
}
|
||||
|
||||
if Static.keyboardDownImage == nil {
|
||||
|
||||
let base64Data = "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"
|
||||
|
||||
if let data = Data(base64Encoded: base64Data, options: .ignoreUnknownCharacters) {
|
||||
Static.keyboardDownImage = UIImage(data: data, scale: 3)
|
||||
}
|
||||
|
||||
//Support for RTL languages like Arabic, Persia etc... (Bug ID: #448)
|
||||
Static.keyboardDownImage = Static.keyboardDownImage?.imageFlippedForRightToLeftLayoutDirection()
|
||||
}
|
||||
|
||||
return Static.keyboardDownImage
|
||||
}
|
||||
|
||||
static func keyboardPreviousImage() -> UIImage? {
|
||||
|
||||
if #available(iOS 10, *) {
|
||||
return keyboardUpImage()
|
||||
} else {
|
||||
return keyboardLeftImage()
|
||||
}
|
||||
}
|
||||
|
||||
static func keyboardNextImage() -> UIImage? {
|
||||
|
||||
if #available(iOS 10, *) {
|
||||
return keyboardDownImage()
|
||||
} else {
|
||||
return keyboardRightImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
UIView category methods to add IQToolbar on UIKeyboard.
|
||||
*/
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
@objc public extension UIView {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var keyboardToolbar = "keyboardToolbar"
|
||||
static var shouldHideToolbarPlaceholder = "shouldHideToolbarPlaceholder"
|
||||
static var toolbarPlaceholder = "toolbarPlaceholder"
|
||||
}
|
||||
|
||||
// MARK: Toolbar
|
||||
|
||||
/**
|
||||
IQToolbar references for better customization control.
|
||||
*/
|
||||
var keyboardToolbar: IQToolbar {
|
||||
var toolbar = inputAccessoryView as? IQToolbar
|
||||
|
||||
if toolbar == nil {
|
||||
toolbar = objc_getAssociatedObject(self, &AssociatedKeys.keyboardToolbar) as? IQToolbar
|
||||
}
|
||||
|
||||
if let unwrappedToolbar = toolbar {
|
||||
return unwrappedToolbar
|
||||
} else {
|
||||
|
||||
let frame = CGRect(origin: .zero, size: .init(width: UIScreen.main.bounds.width, height: 44))
|
||||
let newToolbar = IQToolbar(frame: frame)
|
||||
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.keyboardToolbar, newToolbar, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
|
||||
return newToolbar
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Toolbar title
|
||||
|
||||
/**
|
||||
If `shouldHideToolbarPlaceholder` is YES, then title will not be added to the toolbar. Default to NO.
|
||||
*/
|
||||
var shouldHideToolbarPlaceholder: Bool {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.shouldHideToolbarPlaceholder) as? Bool ?? false
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.shouldHideToolbarPlaceholder, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
self.keyboardToolbar.titleBarButton.title = self.drawingToolbarPlaceholder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`toolbarPlaceholder` to override default `placeholder` text when drawing text on toolbar.
|
||||
*/
|
||||
var toolbarPlaceholder: String? {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.toolbarPlaceholder) as? String
|
||||
}
|
||||
set(newValue) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.toolbarPlaceholder, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
self.keyboardToolbar.titleBarButton.title = self.drawingToolbarPlaceholder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`drawingToolbarPlaceholder` will be actual text used to draw on toolbar. This would either `placeholder` or `toolbarPlaceholder`.
|
||||
*/
|
||||
var drawingToolbarPlaceholder: String? {
|
||||
|
||||
if self.shouldHideToolbarPlaceholder {
|
||||
return nil
|
||||
} else if self.toolbarPlaceholder?.isEmpty == false {
|
||||
return self.toolbarPlaceholder
|
||||
} else if self.responds(to: #selector(getter: UITextField.placeholder)) {
|
||||
|
||||
if let textField = self as? UITextField {
|
||||
return textField.placeholder
|
||||
} else if let textView = self as? IQTextView {
|
||||
return textView.placeholder
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private helper
|
||||
|
||||
// swiftlint:disable nesting
|
||||
private static func flexibleBarButtonItem () -> IQBarButtonItem {
|
||||
|
||||
struct Static {
|
||||
|
||||
static let nilButton = IQBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||
}
|
||||
|
||||
Static.nilButton.isSystemItem = true
|
||||
return Static.nilButton
|
||||
}
|
||||
|
||||
// MARK: Common
|
||||
|
||||
func addKeyboardToolbarWithTarget(target: AnyObject?, titleText: String?, rightBarButtonConfiguration: IQBarButtonItemConfiguration?, previousBarButtonConfiguration: IQBarButtonItemConfiguration? = nil, nextBarButtonConfiguration: IQBarButtonItemConfiguration? = nil) {
|
||||
|
||||
//If can't set InputAccessoryView. Then return
|
||||
if self.responds(to: #selector(setter: UITextField.inputAccessoryView)) {
|
||||
|
||||
// Creating a toolBar for phoneNumber keyboard
|
||||
let toolbar = self.keyboardToolbar
|
||||
|
||||
var items: [IQBarButtonItem] = []
|
||||
|
||||
if let prevConfig = previousBarButtonConfiguration {
|
||||
|
||||
var prev = toolbar.previousBarButton
|
||||
|
||||
if prevConfig.barButtonSystemItem == nil, !prev.isSystemItem {
|
||||
prev.title = prevConfig.title
|
||||
prev.accessibilityLabel = prevConfig.accessibilityLabel
|
||||
prev.accessibilityIdentifier = prev.accessibilityLabel
|
||||
prev.image = prevConfig.image
|
||||
prev.target = target
|
||||
prev.action = prevConfig.action
|
||||
} else {
|
||||
if let systemItem = prevConfig.barButtonSystemItem {
|
||||
prev = IQBarButtonItem(barButtonSystemItem: systemItem, target: target, action: prevConfig.action)
|
||||
prev.isSystemItem = true
|
||||
} else if let image = prevConfig.image {
|
||||
prev = IQBarButtonItem(image: image, style: .plain, target: target, action: prevConfig.action)
|
||||
} else {
|
||||
prev = IQBarButtonItem(title: prevConfig.title, style: .plain, target: target, action: prevConfig.action)
|
||||
}
|
||||
|
||||
prev.invocation = toolbar.previousBarButton.invocation
|
||||
prev.accessibilityLabel = prevConfig.accessibilityLabel
|
||||
prev.accessibilityIdentifier = prev.accessibilityLabel
|
||||
prev.isEnabled = toolbar.previousBarButton.isEnabled
|
||||
prev.tag = toolbar.previousBarButton.tag
|
||||
toolbar.previousBarButton = prev
|
||||
}
|
||||
|
||||
items.append(prev)
|
||||
}
|
||||
|
||||
if previousBarButtonConfiguration != nil, nextBarButtonConfiguration != nil {
|
||||
|
||||
items.append(toolbar.fixedSpaceBarButton)
|
||||
}
|
||||
|
||||
if let nextConfig = nextBarButtonConfiguration {
|
||||
|
||||
var next = toolbar.nextBarButton
|
||||
|
||||
if nextConfig.barButtonSystemItem == nil, !next.isSystemItem {
|
||||
next.title = nextConfig.title
|
||||
next.accessibilityLabel = nextConfig.accessibilityLabel
|
||||
next.accessibilityIdentifier = next.accessibilityLabel
|
||||
next.image = nextConfig.image
|
||||
next.target = target
|
||||
next.action = nextConfig.action
|
||||
} else {
|
||||
if let systemItem = nextConfig.barButtonSystemItem {
|
||||
next = IQBarButtonItem(barButtonSystemItem: systemItem, target: target, action: nextConfig.action)
|
||||
next.isSystemItem = true
|
||||
} else if let image = nextConfig.image {
|
||||
next = IQBarButtonItem(image: image, style: .plain, target: target, action: nextConfig.action)
|
||||
} else {
|
||||
next = IQBarButtonItem(title: nextConfig.title, style: .plain, target: target, action: nextConfig.action)
|
||||
}
|
||||
|
||||
next.invocation = toolbar.nextBarButton.invocation
|
||||
next.accessibilityLabel = nextConfig.accessibilityLabel
|
||||
next.accessibilityIdentifier = next.accessibilityLabel
|
||||
next.isEnabled = toolbar.nextBarButton.isEnabled
|
||||
next.tag = toolbar.nextBarButton.tag
|
||||
toolbar.nextBarButton = next
|
||||
}
|
||||
|
||||
items.append(next)
|
||||
}
|
||||
|
||||
//Title bar button item
|
||||
do {
|
||||
//Flexible space
|
||||
items.append(UIView.flexibleBarButtonItem())
|
||||
|
||||
//Title button
|
||||
toolbar.titleBarButton.title = titleText
|
||||
|
||||
if #available(iOS 11, *) {} else {
|
||||
toolbar.titleBarButton.customView?.frame = CGRect.zero
|
||||
}
|
||||
|
||||
items.append(toolbar.titleBarButton)
|
||||
|
||||
//Flexible space
|
||||
items.append(UIView.flexibleBarButtonItem())
|
||||
}
|
||||
|
||||
if let rightConfig = rightBarButtonConfiguration {
|
||||
|
||||
var done = toolbar.doneBarButton
|
||||
|
||||
if rightConfig.barButtonSystemItem == nil, !done.isSystemItem {
|
||||
done.title = rightConfig.title
|
||||
done.accessibilityLabel = rightConfig.accessibilityLabel
|
||||
done.accessibilityIdentifier = done.accessibilityLabel
|
||||
done.image = rightConfig.image
|
||||
done.target = target
|
||||
done.action = rightConfig.action
|
||||
} else {
|
||||
if let systemItem = rightConfig.barButtonSystemItem {
|
||||
done = IQBarButtonItem(barButtonSystemItem: systemItem, target: target, action: rightConfig.action)
|
||||
done.isSystemItem = true
|
||||
} else if let image = rightConfig.image {
|
||||
done = IQBarButtonItem(image: image, style: .plain, target: target, action: rightConfig.action)
|
||||
} else {
|
||||
done = IQBarButtonItem(title: rightConfig.title, style: .plain, target: target, action: rightConfig.action)
|
||||
}
|
||||
|
||||
done.invocation = toolbar.doneBarButton.invocation
|
||||
done.accessibilityLabel = rightConfig.accessibilityLabel
|
||||
done.accessibilityIdentifier = done.accessibilityLabel
|
||||
done.isEnabled = toolbar.doneBarButton.isEnabled
|
||||
done.tag = toolbar.doneBarButton.tag
|
||||
toolbar.doneBarButton = done
|
||||
}
|
||||
|
||||
items.append(done)
|
||||
}
|
||||
|
||||
// Adding button to toolBar.
|
||||
toolbar.items = items
|
||||
|
||||
if let textInput = self as? UITextInput {
|
||||
switch textInput.keyboardAppearance {
|
||||
case .dark?:
|
||||
toolbar.barStyle = .black
|
||||
default:
|
||||
toolbar.barStyle = .default
|
||||
}
|
||||
}
|
||||
|
||||
// Setting toolbar to keyboard.
|
||||
if let textField = self as? UITextField {
|
||||
textField.inputAccessoryView = toolbar
|
||||
} else if let textView = self as? UITextView {
|
||||
textView.inputAccessoryView = toolbar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Right
|
||||
|
||||
func addDoneOnKeyboardWithTarget(_ target: AnyObject?, action: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addDoneOnKeyboardWithTarget(target, action: action, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addDoneOnKeyboardWithTarget(_ target: AnyObject?, action: Selector, titleText: String?) {
|
||||
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .done, action: action)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration)
|
||||
}
|
||||
|
||||
func addRightButtonOnKeyboardWithImage(_ image: UIImage, target: AnyObject?, action: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addRightButtonOnKeyboardWithImage(image, target: target, action: action, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addRightButtonOnKeyboardWithImage(_ image: UIImage, target: AnyObject?, action: Selector, titleText: String?) {
|
||||
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(image: image, action: action)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration)
|
||||
}
|
||||
|
||||
func addRightButtonOnKeyboardWithText(_ text: String, target: AnyObject?, action: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addRightButtonOnKeyboardWithText(text, target: target, action: action, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addRightButtonOnKeyboardWithText(_ text: String, target: AnyObject?, action: Selector, titleText: String?) {
|
||||
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(title: text, action: action)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration)
|
||||
}
|
||||
|
||||
// MARK: Right/Left
|
||||
|
||||
func addCancelDoneOnKeyboardWithTarget(_ target: AnyObject?, cancelAction: Selector, doneAction: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addCancelDoneOnKeyboardWithTarget(target, cancelAction: cancelAction, doneAction: doneAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addRightLeftOnKeyboardWithTarget(_ target: AnyObject?, leftButtonTitle: String, rightButtonTitle: String, leftButtonAction: Selector, rightButtonAction: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addRightLeftOnKeyboardWithTarget(target, leftButtonTitle: leftButtonTitle, rightButtonTitle: rightButtonTitle, leftButtonAction: leftButtonAction, rightButtonAction: rightButtonAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addRightLeftOnKeyboardWithTarget(_ target: AnyObject?, leftButtonImage: UIImage, rightButtonImage: UIImage, leftButtonAction: Selector, rightButtonAction: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addRightLeftOnKeyboardWithTarget(target, leftButtonImage: leftButtonImage, rightButtonImage: rightButtonImage, leftButtonAction: leftButtonAction, rightButtonAction: rightButtonAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addCancelDoneOnKeyboardWithTarget(_ target: AnyObject?, cancelAction: Selector, doneAction: Selector, titleText: String?) {
|
||||
|
||||
let leftConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .cancel, action: cancelAction)
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .done, action: doneAction)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: leftConfiguration)
|
||||
}
|
||||
|
||||
func addRightLeftOnKeyboardWithTarget(_ target: AnyObject?, leftButtonTitle: String, rightButtonTitle: String, leftButtonAction: Selector, rightButtonAction: Selector, titleText: String?) {
|
||||
|
||||
let leftConfiguration = IQBarButtonItemConfiguration(title: leftButtonTitle, action: leftButtonAction)
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(title: rightButtonTitle, action: rightButtonAction)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: leftConfiguration)
|
||||
}
|
||||
|
||||
func addRightLeftOnKeyboardWithTarget(_ target: AnyObject?, leftButtonImage: UIImage, rightButtonImage: UIImage, leftButtonAction: Selector, rightButtonAction: Selector, titleText: String?) {
|
||||
|
||||
let leftConfiguration = IQBarButtonItemConfiguration(image: leftButtonImage, action: leftButtonAction)
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(image: rightButtonImage, action: rightButtonAction)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: leftConfiguration)
|
||||
}
|
||||
|
||||
// MARK: Previous/Next/Right
|
||||
|
||||
func addPreviousNextDoneOnKeyboardWithTarget (_ target: AnyObject?, previousAction: Selector, nextAction: Selector, doneAction: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addPreviousNextDoneOnKeyboardWithTarget(target, previousAction: previousAction, nextAction: nextAction, doneAction: doneAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addPreviousNextRightOnKeyboardWithTarget(_ target: AnyObject?, rightButtonImage: UIImage, previousAction: Selector, nextAction: Selector, rightButtonAction: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addPreviousNextRightOnKeyboardWithTarget(target, rightButtonImage: rightButtonImage, previousAction: previousAction, nextAction: nextAction, rightButtonAction: rightButtonAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addPreviousNextRightOnKeyboardWithTarget(_ target: AnyObject?, rightButtonTitle: String, previousAction: Selector, nextAction: Selector, rightButtonAction: Selector, shouldShowPlaceholder: Bool = false) {
|
||||
|
||||
addPreviousNextRightOnKeyboardWithTarget(target, rightButtonTitle: rightButtonTitle, previousAction: previousAction, nextAction: nextAction, rightButtonAction: rightButtonAction, titleText: (shouldShowPlaceholder ? self.drawingToolbarPlaceholder: nil))
|
||||
}
|
||||
|
||||
func addPreviousNextDoneOnKeyboardWithTarget (_ target: AnyObject?, previousAction: Selector, nextAction: Selector, doneAction: Selector, titleText: String?) {
|
||||
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(barButtonSystemItem: .done, action: doneAction)
|
||||
let nextConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardNextImage() ?? UIImage(), action: nextAction)
|
||||
let prevConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardPreviousImage() ?? UIImage(), action: previousAction)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: prevConfiguration, nextBarButtonConfiguration: nextConfiguration)
|
||||
}
|
||||
|
||||
func addPreviousNextRightOnKeyboardWithTarget(_ target: AnyObject?, rightButtonImage: UIImage, previousAction: Selector, nextAction: Selector, rightButtonAction: Selector, titleText: String?) {
|
||||
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(image: rightButtonImage, action: rightButtonAction)
|
||||
let nextConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardNextImage() ?? UIImage(), action: nextAction)
|
||||
let prevConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardPreviousImage() ?? UIImage(), action: previousAction)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: prevConfiguration, nextBarButtonConfiguration: nextConfiguration)
|
||||
}
|
||||
|
||||
func addPreviousNextRightOnKeyboardWithTarget(_ target: AnyObject?, rightButtonTitle: String, previousAction: Selector, nextAction: Selector, rightButtonAction: Selector, titleText: String?) {
|
||||
|
||||
let rightConfiguration = IQBarButtonItemConfiguration(title: rightButtonTitle, action: rightButtonAction)
|
||||
let nextConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardNextImage() ?? UIImage(), action: nextAction)
|
||||
let prevConfiguration = IQBarButtonItemConfiguration(image: UIImage.keyboardPreviousImage() ?? UIImage(), action: previousAction)
|
||||
|
||||
addKeyboardToolbarWithTarget(target: target, titleText: titleText, rightBarButtonConfiguration: rightConfiguration, previousBarButtonConfiguration: prevConfiguration, nextBarButtonConfiguration: nextConfiguration)
|
||||
}
|
||||
}
|
||||
21
Pods/IQKeyboardManagerSwift/LICENSE.md
generated
Normal file
21
Pods/IQKeyboardManagerSwift/LICENSE.md
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-2017 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.
|
||||
223
Pods/IQKeyboardManagerSwift/README.md
generated
Normal file
223
Pods/IQKeyboardManagerSwift/README.md
generated
Normal file
@@ -0,0 +1,223 @@
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Demo/Resources/icon.png" alt="Icon"/>
|
||||
</p>
|
||||
<H1 align="center">IQKeyboardManager</H1>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/github/license/hackiftekhar/IQKeyboardManager.svg"
|
||||
alt="GitHub license"/>
|
||||
|
||||
|
||||
[](https://travis-ci.org/hackiftekhar/IQKeyboardManager)
|
||||
|
||||
|
||||
While developing iOS apps, we often run into issues where the iPhone keyboard slides up and covers the `UITextField/UITextView`. `IQKeyboardManager` allows you to prevent this issue of keyboard sliding up and covering `UITextField/UITextView` without needing you to write any code or make any additional setup. To use `IQKeyboardManager` you simply need to add source files to your project.
|
||||
|
||||
|
||||
#### Key Features
|
||||
|
||||
1) `**CODELESS**, Zero Lines of Code`
|
||||
|
||||
2) `Works Automatically`
|
||||
|
||||
3) `No More UIScrollView`
|
||||
|
||||
4) `No More Subclasses`
|
||||
|
||||
5) `No More Manual Work`
|
||||
|
||||
6) `No More #imports`
|
||||
|
||||
`IQKeyboardManager` works on all orientations, and with the toolbar. It also has nice optional features allowing you to customize the distance from the text field, behaviour of previous, next and done buttons in the keyboard toolbar, play sound when the user navigates through the form and more.
|
||||
|
||||
|
||||
## Screenshot
|
||||
[](http://youtu.be/6nhLw6hju2A)
|
||||
[](http://youtu.be/6nhLw6hju2A)
|
||||
|
||||
## GIF animation
|
||||
[](http://youtu.be/6nhLw6hju2A)
|
||||
|
||||
## Video
|
||||
|
||||
<a href="http://youtu.be/WAYc2Qj-OQg" target="_blank"><img src="http://img.youtube.com/vi/WAYc2Qj-OQg/0.jpg"
|
||||
alt="IQKeyboardManager Demo Video" width="480" height="360" border="10" /></a>
|
||||
|
||||
## Tutorial video by @rebeloper ([#1135](https://github.com/hackiftekhar/IQKeyboardManager/issues/1135))
|
||||
|
||||
@rebeloper demonstrated two videos on how to implement **IQKeyboardManager** at it's core:
|
||||
|
||||
<a href="https://www.youtube.com/playlist?list=PL_csAAO9PQ8aTL87XnueOXi3RpWE2m_8v" target="_blank"><img src="https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/ThirdPartyYoutubeTutorial.jpg"
|
||||
alt="Youtube Tutorial Playlist"/></a>
|
||||
|
||||
https://www.youtube.com/playlist?list=PL_csAAO9PQ8aTL87XnueOXi3RpWE2m_8v
|
||||
|
||||
## Warning
|
||||
|
||||
- **If you're planning to build SDK/library/framework and want to handle UITextField/UITextView with IQKeyboardManager then you're totally going the wrong way.** I would never suggest to add **IQKeyboardManager** as **dependency/adding/shipping** with any third-party library. Instead of adding **IQKeyboardManager** you should implement your own solution to achieve same kind of results. **IQKeyboardManager** is totally designed for projects to help developers for their convenience, it's not designed for **adding/dependency/shipping** with any **third-party library**, because **doing this could block adoption by other developers for their projects as well (who are not using IQKeyboardManager and have implemented their custom solution to handle UITextField/UITextView in the project).**
|
||||
- If **IQKeyboardManager** conflicts with other **third-party library**, then it's **developer responsibility** to **enable/disable IQKeyboardManager** when **presenting/dismissing** third-party library UI. Third-party libraries are not responsible to handle IQKeyboardManager.
|
||||
|
||||
## Requirements
|
||||
[]()
|
||||
|
||||
| | Language | Minimum iOS Target | Minimum Xcode Version |
|
||||
|------------------------|----------|--------------------|-----------------------|
|
||||
| IQKeyboardManager | Obj-C | iOS 8.0 | Xcode 9 |
|
||||
| IQKeyboardManagerSwift | Swift | iOS 8.0 | Xcode 9 |
|
||||
| Demo Project | | | Xcode 11 |
|
||||
|
||||
#### Swift versions support
|
||||
|
||||
| Swift | Xcode | IQKeyboardManagerSwift |
|
||||
|-------------------|-------|------------------------|
|
||||
| 5.1, 5.0, 4.2, 4.0, 3.2, 3.0| 11 | >= 6.5.0 |
|
||||
| 5.0,4.2, 4.0, 3.2, 3.0| 10.2 | >= 6.2.1 |
|
||||
| 4.2, 4.0, 3.2, 3.0| 10.0 | >= 6.0.4 |
|
||||
| 4.0, 3.2, 3.0 | 9.0 | 5.0.0 |
|
||||
|
||||
Installation
|
||||
==========================
|
||||
|
||||
#### Installation with CocoaPods
|
||||
|
||||
[](http://cocoadocs.org/docsets/IQKeyboardManager)
|
||||
|
||||
***IQKeyboardManager (Objective-C):*** IQKeyboardManager is available through [CocoaPods](http://cocoapods.org). To install
|
||||
it, simply add the following line to your Podfile: ([#9](https://github.com/hackiftekhar/IQKeyboardManager/issues/9))
|
||||
|
||||
```ruby
|
||||
pod 'IQKeyboardManager' #iOS8 and later
|
||||
```
|
||||
|
||||
***IQKeyboardManager (Swift):*** IQKeyboardManagerSwift is available through [CocoaPods](http://cocoapods.org). To install
|
||||
it, simply add the following line to your Podfile: ([#236](https://github.com/hackiftekhar/IQKeyboardManager/issues/236))
|
||||
|
||||
*Swift 5.1, 5.0, 4.2, 4.0, 3.2, 3.0 (Xcode 11)*
|
||||
|
||||
```ruby
|
||||
pod 'IQKeyboardManagerSwift'
|
||||
```
|
||||
|
||||
*Or you can choose the version you need based on Swift support table from [Requirements](README.md#requirements)*
|
||||
|
||||
```ruby
|
||||
pod 'IQKeyboardManagerSwift', '6.3.0'
|
||||
```
|
||||
|
||||
In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager.
|
||||
|
||||
```swift
|
||||
import IQKeyboardManagerSwift
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
|
||||
IQKeyboardManager.shared.enable = true
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Installation with Carthage
|
||||
|
||||
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
|
||||
|
||||
You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
|
||||
|
||||
```bash
|
||||
$ brew update
|
||||
$ brew install carthage
|
||||
```
|
||||
|
||||
To integrate `IQKeyboardManger` or `IQKeyboardManagerSwift` into your Xcode project using Carthage, add the following line to your `Cartfile`:
|
||||
|
||||
```ogdl
|
||||
github "hackiftekhar/IQKeyboardManager"
|
||||
```
|
||||
|
||||
Run `carthage` to build the frameworks and drag the appropriate framework (`IQKeyboardManager.framework` or `IQKeyboardManagerSwift.framework`) into your Xcode project based on your need. Make sure to add only one framework and not both.
|
||||
|
||||
|
||||
#### Installation with Source Code
|
||||
|
||||
[]()
|
||||
|
||||
|
||||
|
||||
***IQKeyboardManager (Objective-C):*** Just ***drag and drop*** `IQKeyboardManager` directory from demo project to your project. That's it.
|
||||
|
||||
***IQKeyboardManager (Swift):*** ***Drag and drop*** `IQKeyboardManagerSwift` directory from demo project to your project
|
||||
|
||||
In AppDelegate.swift, just enable IQKeyboardManager.
|
||||
|
||||
```swift
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
|
||||
IQKeyboardManager.shared.enable = true
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Installation with Swift Package Manager
|
||||
|
||||
[Swift Package Manager(SPM)](https://swift.org/package-manager/) is Apple's dependency manager tool. It is now supported in Xcode 11. So it can be used in all appleOS types of projects. It can be used alongside other tools like CocoaPods and Carthage as well.
|
||||
|
||||
To install IQKeyboardManager package into your packages, add a reference to IQKeyboardManager and a targeting release version in the dependencies section in `Package.swift` file:
|
||||
|
||||
```swift
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "YOUR_PROJECT_NAME",
|
||||
products: [],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/hackiftekhar/IQKeyboardManager.git", from: "6.5.0")
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
To install IQKeyboardManager package via Xcode
|
||||
|
||||
* Go to File -> Swift Packages -> Add Package Dependency...
|
||||
* Then search for https://github.com/hackiftekhar/IQKeyboardManager.git
|
||||
* And choose the version you want
|
||||
|
||||
Migration Guide
|
||||
==========================
|
||||
- [IQKeyboardManager 6.0.0 Migration Guide](https://github.com/hackiftekhar/IQKeyboardManager/wiki/IQKeyboardManager-6.0.0-Migration-Guide)
|
||||
|
||||
Other Links
|
||||
==========================
|
||||
|
||||
- [Known Issues](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Known-Issues)
|
||||
- [Manual Management Tweaks](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Manual-Management)
|
||||
- [Properties and functions usage](https://github.com/hackiftekhar/IQKeyboardManager/wiki/Properties-&-Functions)
|
||||
|
||||
## Flow Diagram
|
||||
[](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/master/Screenshot/IQKeyboardManagerFlowDiagram.jpg)
|
||||
|
||||
If you would like to see detailed Flow diagram then check [Detailed Flow Diagram](https://raw.githubusercontent.com/hackiftekhar/IQKeyboardManager/v3.3.0/Screenshot/IQKeyboardManagerCFD.jpg).
|
||||
|
||||
|
||||
LICENSE
|
||||
---
|
||||
Distributed under the MIT License.
|
||||
|
||||
Contributions
|
||||
---
|
||||
Any contribution is more than welcome! You can contribute through pull requests and issues on GitHub.
|
||||
|
||||
Author
|
||||
---
|
||||
If you wish to contact me, email at: hack.iftekhar@gmail.com
|
||||
Reference in New Issue
Block a user