├── .gitignore ├── Keyboard ├── Keyboard.swift ├── UIPresentationController+Keyboard.swift └── UIViewController+Keyboard.swift ├── KeyboardManager Demo ├── KeyboardManager Demo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── KeyboardManager Demo │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift └── KeyboardManager DemoTests │ ├── Info.plist │ └── KeyboardManager_DemoTests.swift ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /Keyboard/Keyboard.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public typealias KeyboardAnimation = (Keyboard) -> Void 4 | public typealias KeyboardAnimationCompletion = (Keyboard, _ finished: Bool) -> Void 5 | 6 | public extension UIApplication { 7 | public var keyboard: Keyboard { return Keyboard.sharedKeyboard } 8 | public struct KeyboardManager { 9 | static public var enabled: Bool { 10 | get { return Keyboard.sharedKeyboard.enabled } 11 | set { Keyboard.sharedKeyboard.enabled = newValue } 12 | } 13 | } 14 | } 15 | 16 | private let _KeyboardSharedInstance = Keyboard() 17 | /** 18 | A manager built to be used as a shared instance in order to get as much info as possible about the keyboard (if displayed). 19 | */ 20 | open class Keyboard { 21 | fileprivate class var sharedKeyboard: Keyboard { 22 | return _KeyboardSharedInstance 23 | } 24 | 25 | fileprivate init() {} 26 | 27 | open var enabled: Bool = false { 28 | didSet { 29 | if enabled { 30 | NotificationCenter.default.addObserver(self, selector: #selector(Keyboard.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) 31 | NotificationCenter.default.addObserver(self, selector: #selector(Keyboard.keyboardDidShow(_:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil) 32 | NotificationCenter.default.addObserver(self, selector: #selector(Keyboard.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) 33 | NotificationCenter.default.addObserver(self, selector: #selector(Keyboard.keyboardDidHide(_:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil) 34 | } else { 35 | NotificationCenter.default.removeObserver(self) 36 | } 37 | } 38 | } 39 | // MARK: - Basic infos 40 | 41 | /// Indicates if the keyboard is visible on screen or not. This value is set to true upon UIKeyboardWillShowNotification call, and false upon UIKeyboardDidHideNotification call. 42 | open fileprivate(set) var visible: Bool = false 43 | 44 | open fileprivate(set) var isBeingPresented: Bool = false 45 | open fileprivate(set) var isBeingDismissed: Bool = false 46 | 47 | /// Returns the window for the keyboard. Or nil if keyboard is not visible. 48 | open var window: UIWindow? { return visible == true ? UIApplication.shared.keyWindow : nil } 49 | 50 | // /// Returns the firstResponder. Or nil if keyboard is not visible. 51 | // public var firstResponder: UIResponder? { return self.window?.findFirstResponder() } 52 | 53 | /// The start frame of the keyboard inside its window 54 | open var startFrame: CGRect? { return self.visible ? (self.keyboardInfos?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue : nil } 55 | 56 | /// The start frame of the keyboard inside the given viewController's view 57 | open func startFrame(inViewController vc: UIViewController) -> CGRect? { return self.startFrame(inView: vc.isViewLoaded ? vc.view : nil) } 58 | open func startFrame(inView view: UIView?) -> CGRect? { 59 | if let startFrame = self.endFrame { 60 | return self.window?.convert(startFrame, to: view) 61 | } 62 | return nil 63 | } 64 | 65 | /// The end frame of the keyboard inside its window 66 | open var endFrame: CGRect? { return self.visible ? (self.keyboardInfos?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue : nil } 67 | 68 | /// The end frame of the keyboard inside the given viewController's view 69 | open func endFrame(inViewController vc: UIViewController) -> CGRect? { return self.endFrame(inView: vc.isViewLoaded ? vc.view : nil) } 70 | open func endFrame(inView view: UIView?) -> CGRect? { 71 | if let endFrame = self.endFrame { 72 | return self.window?.convert(endFrame, to: view) 73 | } 74 | return nil 75 | } 76 | 77 | // MARK: - Animation 78 | open var animationDuration: Double? { 79 | if let duration = (self.keyboardInfos?[UIKeyboardAnimationDurationUserInfoKey] as? Double), duration > 0.0 { 80 | return duration 81 | } 82 | return nil 83 | } 84 | open var animationCurve: UIViewAnimationCurve? { 85 | if let animationCurveRawValue = self.keyboardInfos?[UIKeyboardAnimationCurveUserInfoKey] as? Int { 86 | return UIViewAnimationCurve(rawValue: animationCurveRawValue) 87 | } 88 | return nil 89 | } 90 | open var animationOptions: UIViewAnimationOptions? { 91 | if let animationCurve = self.animationCurve { 92 | return UIViewAnimationOptions(rawValue: UInt(animationCurve.rawValue << 16)) 93 | } 94 | return nil 95 | } 96 | 97 | /** 98 | Gives you the opportunity to execute animations alongside with the keyboard. 99 | Calling this function outside a keyboardWillShow / keyboardWillHide notification has no effect. 100 | 101 | - returns: True if the animation & completion closures are accepted 102 | */ 103 | open func animateAlongsideWithKeyboard(_ animations: @escaping KeyboardAnimation, completion: KeyboardAnimationCompletion? = nil) { 104 | if self.canAnimationAlongside == true { 105 | self.alongsideAnimations.append(animations) 106 | if completion != nil { self.alonsideAnimationCompletions.append(completion!) } 107 | } 108 | } 109 | fileprivate var alongsideAnimations: [KeyboardAnimation] = [] 110 | fileprivate var alonsideAnimationCompletions: [KeyboardAnimationCompletion] = [] 111 | fileprivate var canAnimationAlongside: Bool = false { 112 | willSet { 113 | if newValue == false { 114 | self.alongsideAnimations.removeAll(keepingCapacity: false) 115 | self.alonsideAnimationCompletions.removeAll(keepingCapacity: false) 116 | } 117 | } 118 | } 119 | 120 | fileprivate func animateAlongsideWithKeyboardIfNeeded() { 121 | if self.alongsideAnimations.count > 0 { 122 | UIView.animate(withDuration: self.animationDuration ?? 0.0, 123 | delay: 0.0, 124 | options: self.animationOptions ?? .curveLinear, 125 | animations: { 126 | for animation in self.alongsideAnimations { 127 | animation(self) 128 | } 129 | }, 130 | completion: nil) 131 | } 132 | } 133 | 134 | // MARK: - Private 135 | 136 | fileprivate var keyboardInfos: [AnyHashable: Any]? 137 | @objc fileprivate func keyboardWillShow(_ n: Notification) { 138 | guard self.enabled == true else { return } 139 | self.keyboardInfos = n.userInfo 140 | self.visible = true 141 | self.isBeingPresented = true 142 | self.canAnimationAlongside = true 143 | 144 | // Call keyboardWillAppear(animated:) on rootViewController 145 | // dispatch_async() fixes https://github.com/FinalCAD/iOS-KeyboardManager/issues/2 146 | DispatchQueue.main.async { [unowned self] in 147 | self.window?.rootViewController?.keyboardWillAppear(self.animationDuration != nil) 148 | self.animateAlongsideWithKeyboardIfNeeded() 149 | } 150 | } 151 | 152 | @objc fileprivate func keyboardDidShow(_ n: Notification) { 153 | guard self.enabled == true else { return } 154 | let didShowKeyboardAnimated = self.animationDuration != nil 155 | self.keyboardInfos = n.userInfo 156 | self.isBeingPresented = false 157 | 158 | for completion in self.alonsideAnimationCompletions { completion(self, true) } 159 | self.canAnimationAlongside = false 160 | 161 | // Call keyboardDidAppear(animated:) on rootViewController 162 | self.window?.rootViewController?.keyboardDidAppear(didShowKeyboardAnimated) 163 | } 164 | 165 | @objc fileprivate func keyboardWillHide(_ n: Notification) { 166 | guard self.enabled == true else { return } 167 | self.keyboardInfos = n.userInfo 168 | self.isBeingDismissed = true 169 | self.canAnimationAlongside = true 170 | 171 | // Call keyboardWillDisappear(animated:) on rootViewController 172 | self.window?.rootViewController?.keyboardWillDisappear(self.animationDuration != nil) 173 | self.animateAlongsideWithKeyboardIfNeeded() 174 | } 175 | 176 | @objc fileprivate func keyboardDidHide(_ n: Notification) { 177 | guard self.enabled == true else { return } 178 | let didHideKeyboardAnimated = self.animationDuration != nil 179 | self.keyboardInfos = n.userInfo 180 | self.visible = false 181 | self.isBeingDismissed = false 182 | 183 | for completion in self.alonsideAnimationCompletions { completion(self, true) } 184 | self.canAnimationAlongside = false 185 | 186 | // Call keyboardDidDisappear(animated:) on rootViewController 187 | self.window?.rootViewController?.keyboardDidDisappear(didHideKeyboardAnimated) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Keyboard/UIPresentationController+Keyboard.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** Keyboards notifications for UIPresentationController are useful if your custom UIPresentationController presents its presentedView using only a portion of the screen. 4 | 5 | Default non-fullscreen modals frame automatically change once the keyboard appear/disppear on a non-compact environment (iPad, for exemple) 6 | */ 7 | public extension UIPresentationController { 8 | /** Called by the Keyboard when UIKeyboardWillShowNotification has been called. The 'animated' value depends if the keyboard did receive a duration in the userInfo. 9 | 10 | Default implementation does nothing. 11 | You can use this function to create animation alongside with the keyboard apparition. 12 | */ 13 | public func keyboardWillAppear(animated: Bool) { 14 | } 15 | 16 | /** Called by the Keyboard when UIKeyboardDidShowNotification has been called. The 'animated' value depends if the keyboard did receive a duration when UIKeyboardWillShowNotification has been called earlier. 17 | 18 | Default implementation does nothing. 19 | */ 20 | public func keyboardDidAppear(animated: Bool) { 21 | } 22 | 23 | /** Called by the Keyboard when UIKeyboardWillHideNotification has been called. The 'animated' value depends if the keyboard did receive a duration in the userInfo. 24 | 25 | Default implementation does nothing. 26 | You can use this function to create animation alongside with the keyboard apparition. 27 | */ 28 | public func keyboardWillDisappear(animated: Bool) { 29 | } 30 | 31 | /** Called by the Keyboard when UIKeyboardDidHideNotification has been called. The 'animated' value depends if the keyboard did receive a duration when UIKeyboardWillHideNotification has been called earlier. 32 | 33 | Default implementation does nothing. 34 | */ 35 | public func keyboardDidDisappear(animated: Bool) { 36 | } 37 | 38 | /** 39 | Gives you the opportunity to execute animations alongside with the keyboard. 40 | Calling this function outside a keyboardWillAppear / keyboardWillDisappear function has no effect. 41 | */ 42 | public func animateAlongsideWithKeyboard(animations: @escaping KeyboardAnimation, completion: KeyboardAnimationCompletion? = nil) { 43 | UIApplication.shared.keyboard.animateAlongsideWithKeyboard(animations, completion: completion) 44 | } 45 | 46 | } 47 | 48 | private extension UIView { 49 | private func findFirstResponder() -> UIResponder? { 50 | if self.isFirstResponder == true { 51 | return self 52 | } else { 53 | for subview in self.subviews { 54 | return subview.findFirstResponder() 55 | } 56 | } 57 | return nil 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Keyboard/UIViewController+Keyboard.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public extension UIViewController { 4 | /** Called by the Keyboard when UIKeyboardWillShowNotification has been called. The 'animated' value depends if the keyboard did receive a duration in the userInfo. 5 | 6 | When overriding this function, you must call super so it can follows the call to its children & presentedViewController 7 | You can use this function to create animation alongside with the keyboard apparition. 8 | */ 9 | @objc public func keyboardWillAppear(_ animated: Bool) { 10 | for childVC in self.childViewControllers { childVC.keyboardWillAppear(animated) } 11 | if self.childViewControllers.count == 0 { 12 | self.presentedViewController?.keyboardWillAppear(animated) 13 | } 14 | } 15 | 16 | /** Called by the Keyboard when UIKeyboardDidShowNotification has been called. The 'animated' value depends if the keyboard did receive a duration when UIKeyboardWillShowNotification has been called earlier. 17 | 18 | When overriding this function, you must call super so it can follows the call to its children & presentedViewController. 19 | */ 20 | @objc public func keyboardDidAppear(_ animated: Bool) { 21 | for childVC in self.childViewControllers { childVC.keyboardDidAppear(animated) } 22 | if self.childViewControllers.count == 0 { 23 | self.presentedViewController?.keyboardDidAppear(animated) 24 | } 25 | } 26 | 27 | /** Called by the Keyboard when UIKeyboardWillHideNotification has been called. The 'animated' value depends if the keyboard did receive a duration in the userInfo. 28 | 29 | When overriding this function, you must call super so it can follows the call to its children & presentedViewController. 30 | You can use this function to create animation alongside with the keyboard apparition. 31 | */ 32 | @objc public func keyboardWillDisappear(_ animated: Bool) { 33 | for childVC in self.childViewControllers { childVC.keyboardWillDisappear(animated) } 34 | if self.childViewControllers.count == 0 { 35 | self.presentedViewController?.keyboardWillDisappear(animated) 36 | } 37 | } 38 | 39 | /** Called by the Keyboard when UIKeyboardDidHideNotification has been called. The 'animated' value depends if the keyboard did receive a duration when UIKeyboardWillHideNotification has been called earlier. 40 | 41 | When overriding this function, you must call super so it can follows the call to its children & presentedViewController. 42 | */ 43 | public func keyboardDidDisappear(_ animated: Bool) { 44 | for childVC in self.childViewControllers { childVC.keyboardDidDisappear(animated) } 45 | if self.childViewControllers.count == 0 { 46 | self.presentedViewController?.keyboardDidDisappear(animated) 47 | } 48 | } 49 | 50 | /** 51 | Gives you the opportunity to execute animations alongside with the keyboard. 52 | Calling this function outside a keyboardWillAppear / keyboardWillDisappear function has no effect. 53 | */ 54 | public func animateAlongsideWithKeyboard(_ animations: @escaping KeyboardAnimation, completion: KeyboardAnimationCompletion? = nil) { 55 | UIApplication.shared.keyboard.animateAlongsideWithKeyboard(animations, completion: completion) 56 | } 57 | 58 | /// Returns the height of the keyboard inside the receiver's view. 59 | public var keyboardBottomLayoutGuideLength: CGFloat { 60 | if let endFrame = self.keyboardEndFrameInView(), endFrame.minY < self.view.frame.height { 61 | return self.view.frame.height - endFrame.minY 62 | } 63 | return 0 64 | } 65 | 66 | /// Returns the startFrame of the keyboard inside the view controller's view. This function simply converts the keyboardStartFrame to the view. 67 | public func keyboardStartFrameInView() -> CGRect? { 68 | if let keyboardStartFrame = UIApplication.shared.keyboard.startFrame { 69 | return self.view.convert(keyboardStartFrame, from: nil) 70 | } 71 | return nil 72 | } 73 | /// Returns the endFrame of the keyboard inside the view controller's view. This function simply converts the keyboardEndFrame to the view. 74 | public func keyboardEndFrameInView() -> CGRect? { 75 | if let keyboardEndFrame = UIApplication.shared.keyboard.endFrame { 76 | return self.view.convert(keyboardEndFrame, from: nil) 77 | } 78 | return nil 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 174992EF1A97B86900C85D44 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174992EE1A97B86900C85D44 /* AppDelegate.swift */; }; 11 | 174992F11A97B86900C85D44 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174992F01A97B86900C85D44 /* ViewController.swift */; }; 12 | 174992F41A97B86900C85D44 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 174992F21A97B86900C85D44 /* Main.storyboard */; }; 13 | 174992F61A97B86900C85D44 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 174992F51A97B86900C85D44 /* Images.xcassets */; }; 14 | 174992F91A97B86900C85D44 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 174992F71A97B86900C85D44 /* LaunchScreen.xib */; }; 15 | 174993051A97B86900C85D44 /* KeyboardManager_DemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174993041A97B86900C85D44 /* KeyboardManager_DemoTests.swift */; }; 16 | 174993121A97B88E00C85D44 /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1749930F1A97B88E00C85D44 /* Keyboard.swift */; }; 17 | 174993131A97B88E00C85D44 /* UIPresentationController+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174993101A97B88E00C85D44 /* UIPresentationController+Keyboard.swift */; }; 18 | 174993141A97B88E00C85D44 /* UIViewController+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 174993111A97B88E00C85D44 /* UIViewController+Keyboard.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 174992FF1A97B86900C85D44 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 174992E11A97B86900C85D44 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 174992E81A97B86900C85D44; 27 | remoteInfo = "KeyboardManager Demo"; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 174992E91A97B86900C85D44 /* KeyboardManager Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "KeyboardManager Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 174992ED1A97B86900C85D44 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 174992EE1A97B86900C85D44 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | 174992F01A97B86900C85D44 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 36 | 174992F31A97B86900C85D44 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 37 | 174992F51A97B86900C85D44 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 38 | 174992F81A97B86900C85D44 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 39 | 174992FE1A97B86900C85D44 /* KeyboardManager DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "KeyboardManager DemoTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 174993031A97B86900C85D44 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 174993041A97B86900C85D44 /* KeyboardManager_DemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardManager_DemoTests.swift; sourceTree = ""; }; 42 | 1749930F1A97B88E00C85D44 /* Keyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keyboard.swift; sourceTree = ""; }; 43 | 174993101A97B88E00C85D44 /* UIPresentationController+Keyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIPresentationController+Keyboard.swift"; sourceTree = ""; }; 44 | 174993111A97B88E00C85D44 /* UIViewController+Keyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Keyboard.swift"; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 174992E61A97B86900C85D44 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | 174992FB1A97B86900C85D44 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | 174992E01A97B86900C85D44 = { 66 | isa = PBXGroup; 67 | children = ( 68 | 1749930E1A97B88E00C85D44 /* Keyboard */, 69 | 174992EB1A97B86900C85D44 /* KeyboardManager Demo */, 70 | 174993011A97B86900C85D44 /* KeyboardManager DemoTests */, 71 | 174992EA1A97B86900C85D44 /* Products */, 72 | ); 73 | sourceTree = ""; 74 | }; 75 | 174992EA1A97B86900C85D44 /* Products */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 174992E91A97B86900C85D44 /* KeyboardManager Demo.app */, 79 | 174992FE1A97B86900C85D44 /* KeyboardManager DemoTests.xctest */, 80 | ); 81 | name = Products; 82 | sourceTree = ""; 83 | }; 84 | 174992EB1A97B86900C85D44 /* KeyboardManager Demo */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 174992EE1A97B86900C85D44 /* AppDelegate.swift */, 88 | 174992F01A97B86900C85D44 /* ViewController.swift */, 89 | 174992F21A97B86900C85D44 /* Main.storyboard */, 90 | 174992F51A97B86900C85D44 /* Images.xcassets */, 91 | 174992F71A97B86900C85D44 /* LaunchScreen.xib */, 92 | 174992EC1A97B86900C85D44 /* Supporting Files */, 93 | ); 94 | path = "KeyboardManager Demo"; 95 | sourceTree = ""; 96 | }; 97 | 174992EC1A97B86900C85D44 /* Supporting Files */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 174992ED1A97B86900C85D44 /* Info.plist */, 101 | ); 102 | name = "Supporting Files"; 103 | sourceTree = ""; 104 | }; 105 | 174993011A97B86900C85D44 /* KeyboardManager DemoTests */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 174993041A97B86900C85D44 /* KeyboardManager_DemoTests.swift */, 109 | 174993021A97B86900C85D44 /* Supporting Files */, 110 | ); 111 | path = "KeyboardManager DemoTests"; 112 | sourceTree = ""; 113 | }; 114 | 174993021A97B86900C85D44 /* Supporting Files */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 174993031A97B86900C85D44 /* Info.plist */, 118 | ); 119 | name = "Supporting Files"; 120 | sourceTree = ""; 121 | }; 122 | 1749930E1A97B88E00C85D44 /* Keyboard */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 1749930F1A97B88E00C85D44 /* Keyboard.swift */, 126 | 174993101A97B88E00C85D44 /* UIPresentationController+Keyboard.swift */, 127 | 174993111A97B88E00C85D44 /* UIViewController+Keyboard.swift */, 128 | ); 129 | name = Keyboard; 130 | path = ../Keyboard; 131 | sourceTree = ""; 132 | }; 133 | /* End PBXGroup section */ 134 | 135 | /* Begin PBXNativeTarget section */ 136 | 174992E81A97B86900C85D44 /* KeyboardManager Demo */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = 174993081A97B86900C85D44 /* Build configuration list for PBXNativeTarget "KeyboardManager Demo" */; 139 | buildPhases = ( 140 | 174992E51A97B86900C85D44 /* Sources */, 141 | 174992E61A97B86900C85D44 /* Frameworks */, 142 | 174992E71A97B86900C85D44 /* Resources */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = "KeyboardManager Demo"; 149 | productName = "KeyboardManager Demo"; 150 | productReference = 174992E91A97B86900C85D44 /* KeyboardManager Demo.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | 174992FD1A97B86900C85D44 /* KeyboardManager DemoTests */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 1749930B1A97B86900C85D44 /* Build configuration list for PBXNativeTarget "KeyboardManager DemoTests" */; 156 | buildPhases = ( 157 | 174992FA1A97B86900C85D44 /* Sources */, 158 | 174992FB1A97B86900C85D44 /* Frameworks */, 159 | 174992FC1A97B86900C85D44 /* Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | 174993001A97B86900C85D44 /* PBXTargetDependency */, 165 | ); 166 | name = "KeyboardManager DemoTests"; 167 | productName = "KeyboardManager DemoTests"; 168 | productReference = 174992FE1A97B86900C85D44 /* KeyboardManager DemoTests.xctest */; 169 | productType = "com.apple.product-type.bundle.unit-test"; 170 | }; 171 | /* End PBXNativeTarget section */ 172 | 173 | /* Begin PBXProject section */ 174 | 174992E11A97B86900C85D44 /* Project object */ = { 175 | isa = PBXProject; 176 | attributes = { 177 | LastSwiftMigration = 0700; 178 | LastSwiftUpdateCheck = 0700; 179 | LastUpgradeCheck = 0920; 180 | ORGANIZATIONNAME = KnowledgeCorp; 181 | TargetAttributes = { 182 | 174992E81A97B86900C85D44 = { 183 | CreatedOnToolsVersion = 6.3; 184 | LastSwiftMigration = 0920; 185 | }; 186 | 174992FD1A97B86900C85D44 = { 187 | CreatedOnToolsVersion = 6.3; 188 | TestTargetID = 174992E81A97B86900C85D44; 189 | }; 190 | }; 191 | }; 192 | buildConfigurationList = 174992E41A97B86900C85D44 /* Build configuration list for PBXProject "KeyboardManager Demo" */; 193 | compatibilityVersion = "Xcode 3.2"; 194 | developmentRegion = English; 195 | hasScannedForEncodings = 0; 196 | knownRegions = ( 197 | en, 198 | Base, 199 | ); 200 | mainGroup = 174992E01A97B86900C85D44; 201 | productRefGroup = 174992EA1A97B86900C85D44 /* Products */; 202 | projectDirPath = ""; 203 | projectRoot = ""; 204 | targets = ( 205 | 174992E81A97B86900C85D44 /* KeyboardManager Demo */, 206 | 174992FD1A97B86900C85D44 /* KeyboardManager DemoTests */, 207 | ); 208 | }; 209 | /* End PBXProject section */ 210 | 211 | /* Begin PBXResourcesBuildPhase section */ 212 | 174992E71A97B86900C85D44 /* Resources */ = { 213 | isa = PBXResourcesBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | 174992F41A97B86900C85D44 /* Main.storyboard in Resources */, 217 | 174992F91A97B86900C85D44 /* LaunchScreen.xib in Resources */, 218 | 174992F61A97B86900C85D44 /* Images.xcassets in Resources */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | 174992FC1A97B86900C85D44 /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | /* End PBXResourcesBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | 174992E51A97B86900C85D44 /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 174993141A97B88E00C85D44 /* UIViewController+Keyboard.swift in Sources */, 237 | 174993131A97B88E00C85D44 /* UIPresentationController+Keyboard.swift in Sources */, 238 | 174992F11A97B86900C85D44 /* ViewController.swift in Sources */, 239 | 174993121A97B88E00C85D44 /* Keyboard.swift in Sources */, 240 | 174992EF1A97B86900C85D44 /* AppDelegate.swift in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | 174992FA1A97B86900C85D44 /* Sources */ = { 245 | isa = PBXSourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 174993051A97B86900C85D44 /* KeyboardManager_DemoTests.swift in Sources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | /* End PBXSourcesBuildPhase section */ 253 | 254 | /* Begin PBXTargetDependency section */ 255 | 174993001A97B86900C85D44 /* PBXTargetDependency */ = { 256 | isa = PBXTargetDependency; 257 | target = 174992E81A97B86900C85D44 /* KeyboardManager Demo */; 258 | targetProxy = 174992FF1A97B86900C85D44 /* PBXContainerItemProxy */; 259 | }; 260 | /* End PBXTargetDependency section */ 261 | 262 | /* Begin PBXVariantGroup section */ 263 | 174992F21A97B86900C85D44 /* Main.storyboard */ = { 264 | isa = PBXVariantGroup; 265 | children = ( 266 | 174992F31A97B86900C85D44 /* Base */, 267 | ); 268 | name = Main.storyboard; 269 | sourceTree = ""; 270 | }; 271 | 174992F71A97B86900C85D44 /* LaunchScreen.xib */ = { 272 | isa = PBXVariantGroup; 273 | children = ( 274 | 174992F81A97B86900C85D44 /* Base */, 275 | ); 276 | name = LaunchScreen.xib; 277 | sourceTree = ""; 278 | }; 279 | /* End PBXVariantGroup section */ 280 | 281 | /* Begin XCBuildConfiguration section */ 282 | 174993061A97B86900C85D44 /* Debug */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | ALWAYS_SEARCH_USER_PATHS = NO; 286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 287 | CLANG_CXX_LIBRARY = "libc++"; 288 | CLANG_ENABLE_MODULES = YES; 289 | CLANG_ENABLE_OBJC_ARC = YES; 290 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 291 | CLANG_WARN_BOOL_CONVERSION = YES; 292 | CLANG_WARN_COMMA = YES; 293 | CLANG_WARN_CONSTANT_CONVERSION = YES; 294 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 295 | CLANG_WARN_EMPTY_BODY = YES; 296 | CLANG_WARN_ENUM_CONVERSION = YES; 297 | CLANG_WARN_INFINITE_RECURSION = YES; 298 | CLANG_WARN_INT_CONVERSION = YES; 299 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 300 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 301 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 302 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 303 | CLANG_WARN_STRICT_PROTOTYPES = YES; 304 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 305 | CLANG_WARN_UNREACHABLE_CODE = YES; 306 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 307 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 308 | COPY_PHASE_STRIP = NO; 309 | DEBUG_INFORMATION_FORMAT = dwarf; 310 | ENABLE_STRICT_OBJC_MSGSEND = YES; 311 | ENABLE_TESTABILITY = YES; 312 | GCC_C_LANGUAGE_STANDARD = gnu99; 313 | GCC_DYNAMIC_NO_PIC = NO; 314 | GCC_NO_COMMON_BLOCKS = YES; 315 | GCC_OPTIMIZATION_LEVEL = 0; 316 | GCC_PREPROCESSOR_DEFINITIONS = ( 317 | "DEBUG=1", 318 | "$(inherited)", 319 | ); 320 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 321 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 322 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 323 | GCC_WARN_UNDECLARED_SELECTOR = YES; 324 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 325 | GCC_WARN_UNUSED_FUNCTION = YES; 326 | GCC_WARN_UNUSED_VARIABLE = YES; 327 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 328 | MTL_ENABLE_DEBUG_INFO = YES; 329 | ONLY_ACTIVE_ARCH = YES; 330 | SDKROOT = iphoneos; 331 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 332 | TARGETED_DEVICE_FAMILY = "1,2"; 333 | }; 334 | name = Debug; 335 | }; 336 | 174993071A97B86900C85D44 /* Release */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | ALWAYS_SEARCH_USER_PATHS = NO; 340 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 341 | CLANG_CXX_LIBRARY = "libc++"; 342 | CLANG_ENABLE_MODULES = YES; 343 | CLANG_ENABLE_OBJC_ARC = YES; 344 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 345 | CLANG_WARN_BOOL_CONVERSION = YES; 346 | CLANG_WARN_COMMA = YES; 347 | CLANG_WARN_CONSTANT_CONVERSION = YES; 348 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 349 | CLANG_WARN_EMPTY_BODY = YES; 350 | CLANG_WARN_ENUM_CONVERSION = YES; 351 | CLANG_WARN_INFINITE_RECURSION = YES; 352 | CLANG_WARN_INT_CONVERSION = YES; 353 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 354 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 355 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 356 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 357 | CLANG_WARN_STRICT_PROTOTYPES = YES; 358 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 359 | CLANG_WARN_UNREACHABLE_CODE = YES; 360 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 361 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 362 | COPY_PHASE_STRIP = NO; 363 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 364 | ENABLE_NS_ASSERTIONS = NO; 365 | ENABLE_STRICT_OBJC_MSGSEND = YES; 366 | GCC_C_LANGUAGE_STANDARD = gnu99; 367 | GCC_NO_COMMON_BLOCKS = YES; 368 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 369 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 370 | GCC_WARN_UNDECLARED_SELECTOR = YES; 371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 372 | GCC_WARN_UNUSED_FUNCTION = YES; 373 | GCC_WARN_UNUSED_VARIABLE = YES; 374 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 375 | MTL_ENABLE_DEBUG_INFO = NO; 376 | SDKROOT = iphoneos; 377 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 378 | TARGETED_DEVICE_FAMILY = "1,2"; 379 | VALIDATE_PRODUCT = YES; 380 | }; 381 | name = Release; 382 | }; 383 | 174993091A97B86900C85D44 /* Debug */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 387 | INFOPLIST_FILE = "KeyboardManager Demo/Info.plist"; 388 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 389 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 390 | PRODUCT_BUNDLE_IDENTIFIER = "com.knowledgecorp.$(PRODUCT_NAME:rfc1034identifier)"; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 393 | SWIFT_VERSION = 4.0; 394 | }; 395 | name = Debug; 396 | }; 397 | 1749930A1A97B86900C85D44 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 401 | INFOPLIST_FILE = "KeyboardManager Demo/Info.plist"; 402 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 404 | PRODUCT_BUNDLE_IDENTIFIER = "com.knowledgecorp.$(PRODUCT_NAME:rfc1034identifier)"; 405 | PRODUCT_NAME = "$(TARGET_NAME)"; 406 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 407 | SWIFT_VERSION = 4.0; 408 | }; 409 | name = Release; 410 | }; 411 | 1749930C1A97B86900C85D44 /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | BUNDLE_LOADER = "$(TEST_HOST)"; 415 | FRAMEWORK_SEARCH_PATHS = ( 416 | "$(SDKROOT)/Developer/Library/Frameworks", 417 | "$(inherited)", 418 | ); 419 | GCC_PREPROCESSOR_DEFINITIONS = ( 420 | "DEBUG=1", 421 | "$(inherited)", 422 | ); 423 | INFOPLIST_FILE = "KeyboardManager DemoTests/Info.plist"; 424 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 425 | PRODUCT_BUNDLE_IDENTIFIER = "com.knowledgecorp.$(PRODUCT_NAME:rfc1034identifier)"; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KeyboardManager Demo.app/KeyboardManager Demo"; 428 | }; 429 | name = Debug; 430 | }; 431 | 1749930D1A97B86900C85D44 /* Release */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | BUNDLE_LOADER = "$(TEST_HOST)"; 435 | FRAMEWORK_SEARCH_PATHS = ( 436 | "$(SDKROOT)/Developer/Library/Frameworks", 437 | "$(inherited)", 438 | ); 439 | INFOPLIST_FILE = "KeyboardManager DemoTests/Info.plist"; 440 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 441 | PRODUCT_BUNDLE_IDENTIFIER = "com.knowledgecorp.$(PRODUCT_NAME:rfc1034identifier)"; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KeyboardManager Demo.app/KeyboardManager Demo"; 444 | }; 445 | name = Release; 446 | }; 447 | /* End XCBuildConfiguration section */ 448 | 449 | /* Begin XCConfigurationList section */ 450 | 174992E41A97B86900C85D44 /* Build configuration list for PBXProject "KeyboardManager Demo" */ = { 451 | isa = XCConfigurationList; 452 | buildConfigurations = ( 453 | 174993061A97B86900C85D44 /* Debug */, 454 | 174993071A97B86900C85D44 /* Release */, 455 | ); 456 | defaultConfigurationIsVisible = 0; 457 | defaultConfigurationName = Release; 458 | }; 459 | 174993081A97B86900C85D44 /* Build configuration list for PBXNativeTarget "KeyboardManager Demo" */ = { 460 | isa = XCConfigurationList; 461 | buildConfigurations = ( 462 | 174993091A97B86900C85D44 /* Debug */, 463 | 1749930A1A97B86900C85D44 /* Release */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | 1749930B1A97B86900C85D44 /* Build configuration list for PBXNativeTarget "KeyboardManager DemoTests" */ = { 469 | isa = XCConfigurationList; 470 | buildConfigurations = ( 471 | 1749930C1A97B86900C85D44 /* Debug */, 472 | 1749930D1A97B86900C85D44 /* Release */, 473 | ); 474 | defaultConfigurationIsVisible = 0; 475 | defaultConfigurationName = Release; 476 | }; 477 | /* End XCConfigurationList section */ 478 | }; 479 | rootObject = 174992E11A97B86900C85D44 /* Project object */; 480 | } 481 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // KeyboardManager Demo 4 | // 5 | // Created by Louka Desroziers on 20/02/15. 6 | // Copyright (c) 2015 KnowledgeCorp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { 17 | UIApplication.KeyboardManager.enabled = true 18 | return true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager Demo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 70 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager Demo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // KeyboardManager Demo 4 | // 5 | // Created by Louka Desroziers on 20/02/15. 6 | // Copyright (c) 2015 KnowledgeCorp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | //MARK : - Edition 24 | @IBOutlet var titleField: UITextField! 25 | @IBOutlet var descriptionField: UITextField! 26 | @IBOutlet private var textFieldsViewBottomConstraint: NSLayoutConstraint! 27 | 28 | // MARK: - Keyboard Management 29 | 30 | override func keyboardWillAppear(_ animated: Bool) { 31 | super.keyboardWillAppear(animated) 32 | 33 | self.animateAlongsideWithKeyboard({ _ in 34 | self.textFieldsViewBottomConstraint.constant = self.keyboardBottomLayoutGuideLength 35 | self.view.layoutIfNeeded() 36 | }) 37 | } 38 | 39 | override func keyboardWillDisappear(_ animated: Bool) { 40 | super.keyboardWillDisappear(animated) 41 | 42 | self.animateAlongsideWithKeyboard({ _ in 43 | self.textFieldsViewBottomConstraint.constant = self.keyboardBottomLayoutGuideLength 44 | self.view.layoutIfNeeded() 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager DemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /KeyboardManager Demo/KeyboardManager DemoTests/KeyboardManager_DemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardManager_DemoTests.swift 3 | // KeyboardManager DemoTests 4 | // 5 | // Created by Louka Desroziers on 20/02/15. 6 | // Copyright (c) 2015 KnowledgeCorp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class KeyboardManager_DemoTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 FinalCAD 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keyboard "Manager" for iOS 2 | Have you ever been frustated when it comes to manage the keyboard in your iOS app? 3 | 4 | Well, the "Keyboard" class is made for you. 5 | 6 | --- 7 | ######Keyboard provides some useful infos : 8 | 9 | ```swift 10 | var visible: Bool { get } 11 | var window: UIWindow? { get } 12 | 13 | var startFrame: CGRect? { get } 14 | func startFrame(inViewController vc: UIViewController) -> CGRect?{} 15 | func startFrame(inView view: UIView?) -> CGRect?{} 16 | 17 | var endFrame: CGRect? { get } 18 | func endFrame(inViewController vc: UIViewController) -> CGRect?{} 19 | func endFrame(inView view: UIView?) -> CGRect?{} 20 | 21 | var animationDuration: Double? { get } 22 | var animationCurve: UIViewAnimationCurve? { get } 23 | var animationOptions: UIViewAnimationOptions? { get } 24 | ``` 25 | 26 | ######`UIViewController` & `UIPresentationController` are extended to implement these methods : 27 | 28 | ```Swift 29 | func keyboardWillAppear(animated: Bool){} 30 | func keyboardDidAppear(animated: Bool){} 31 | func keyboardWillDisappear(animated: Bool){} 32 | func keyboardDidDisappear(animated: Bool){} 33 | 34 | // (UIViewController Only): 35 | var keyboardBottomLayoutGuideLength: CGFloat { get } 36 | func keyboardStartFrameInView() -> CGRect?{} 37 | func keyboardEndFrameInView() -> CGRect?{} 38 | ``` 39 | 40 | ######And forget about initiating animations, just use `animateAlongsideWithKeyboard()` like this : 41 | ```swift 42 | override func keyboardWillAppear(animated: Bool) { 43 | super.keyboardWillAppear(animated) 44 | self.animateAlongsideWithKeyboard({ keyboard in 45 | // execute your animations here and use the keyboard to get the info you need to update your layout 46 | }) 47 | } 48 | ``` 49 | 50 | ##SDK & Language 51 | 52 | iOS 8.0 / Swift 4.0 with Xcode 9.2 53 | 54 | 55 | ##Usage 56 | First of all, you must enable the module. We extended `UIApplication` so you'll just have to do this in your `AppDelegate` : 57 | ```swift 58 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 59 | UIApplication.KeyboardManager.enabled = true 60 | return true 61 | } 62 | ``` 63 | 64 | Now, let's imagine a common usage of the keyboard : UIViewController with UITextField(s). 65 | 66 | ```swift 67 | class MyViewController: UIViewController { 68 | 69 | //MARK: Keyboard Management 70 | override func keyboardWillAppear(animated: Bool) { 71 | super.keyboardWillAppear(animated) 72 | 73 | self.animateAlongsideWithKeyboard({ _ in 74 | self.textFieldsViewBottomConstraint.constant = self.keyboardBottomLengthInView() 75 | self.view.layoutIfNeeded() 76 | }) 77 | } 78 | 79 | override func keyboardWillDisappear(animated: Bool) { 80 | super.keyboardWillDisappear(animated) 81 | 82 | self.animateAlongsideWithKeyboard({ _ in 83 | self.textFieldsViewBottomConstraint.constant = 0 84 | self.view.layoutIfNeeded() 85 | }) 86 | } 87 | 88 | } 89 | ``` 90 | 91 | As you can see, the simplicity comes by the `keyboardWill(Dis)Appear` & `keyboardDid(Dis)Appear` functions, plus the ability to unify your animations using the `animateAlongsideWithKeyboard()` function. 92 | 93 | 94 | #License (MIT) 95 | 96 | Copyright (c) 2015 Knowledge Corp 97 | 98 | 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: 99 | 100 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 101 | 102 | 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. 103 | 104 | --------------------------------------------------------------------------------