├── .gitignore ├── .travis.yml ├── Example ├── Keyboard Example.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Keyboard Example.xcscheme └── Keyboard Example │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── Resources │ └── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon@2x.png │ │ └── Icon@3x.png │ │ ├── Contents.json │ │ └── background.imageset │ │ ├── Contents.json │ │ └── background.jpg │ ├── Source │ ├── AppDelegate.h │ ├── AppDelegate.m │ └── UI │ │ └── Screens │ │ ├── ViewController.h │ │ └── ViewController.m │ └── main.m ├── LICENSE ├── MSSKeyboardManager.podspec ├── README.md ├── Resource └── MSSKeyboardManager.png └── Source ├── MSSKeyboardManager.h ├── MSSKeyboardManager.m ├── UIView+MSSKeyboardDismiss.h └── UIView+MSSKeyboardDismiss.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | script: xcodebuild -project "Example/Keyboard Example.xcodeproj" -scheme "Keyboard Example" -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO 4 | -------------------------------------------------------------------------------- /Example/Keyboard Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 46B7EEBC1D00CCB0007368E8 /* MSSKeyboardManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 46B7EEB91D00CCB0007368E8 /* MSSKeyboardManager.m */; }; 11 | 46B7EEBD1D00CCB0007368E8 /* UIView+MSSKeyboardDismiss.m in Sources */ = {isa = PBXBuildFile; fileRef = 46B7EEBB1D00CCB0007368E8 /* UIView+MSSKeyboardDismiss.m */; }; 12 | 46C64DFE1CC6219400FB223B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C64DFD1CC6219400FB223B /* main.m */; }; 13 | 46C64E071CC6219400FB223B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46C64E051CC6219400FB223B /* Main.storyboard */; }; 14 | 46C64E0C1CC6219400FB223B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46C64E0A1CC6219400FB223B /* LaunchScreen.storyboard */; }; 15 | 46C64E241CC6230100FB223B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46C64E1C1CC6230100FB223B /* Assets.xcassets */; }; 16 | 46C64E251CC6230100FB223B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C64E1F1CC6230100FB223B /* AppDelegate.m */; }; 17 | 46C64E261CC6230100FB223B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 46C64E231CC6230100FB223B /* ViewController.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 46B7EEB81D00CCB0007368E8 /* MSSKeyboardManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSSKeyboardManager.h; sourceTree = ""; }; 22 | 46B7EEB91D00CCB0007368E8 /* MSSKeyboardManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSSKeyboardManager.m; sourceTree = ""; }; 23 | 46B7EEBA1D00CCB0007368E8 /* UIView+MSSKeyboardDismiss.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+MSSKeyboardDismiss.h"; sourceTree = ""; }; 24 | 46B7EEBB1D00CCB0007368E8 /* UIView+MSSKeyboardDismiss.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+MSSKeyboardDismiss.m"; sourceTree = ""; }; 25 | 46C64DF91CC6219400FB223B /* Keyboard Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Keyboard Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 46C64DFD1CC6219400FB223B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 27 | 46C64E061CC6219400FB223B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | 46C64E0B1CC6219400FB223B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | 46C64E0D1CC6219400FB223B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Info.plist; sourceTree = ""; }; 30 | 46C64E1C1CC6230100FB223B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 46C64E1E1CC6230100FB223B /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 32 | 46C64E1F1CC6230100FB223B /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 33 | 46C64E221CC6230100FB223B /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 34 | 46C64E231CC6230100FB223B /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 46C64DF61CC6219400FB223B /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 46B7EEB71D00CCB0007368E8 /* MSSKeyboardManager */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 46B7EEB81D00CCB0007368E8 /* MSSKeyboardManager.h */, 52 | 46B7EEB91D00CCB0007368E8 /* MSSKeyboardManager.m */, 53 | 46B7EEBA1D00CCB0007368E8 /* UIView+MSSKeyboardDismiss.h */, 54 | 46B7EEBB1D00CCB0007368E8 /* UIView+MSSKeyboardDismiss.m */, 55 | ); 56 | name = MSSKeyboardManager; 57 | path = ../Source; 58 | sourceTree = ""; 59 | }; 60 | 46C64DF01CC6219400FB223B = { 61 | isa = PBXGroup; 62 | children = ( 63 | 46C64DFB1CC6219400FB223B /* Keyboard Example */, 64 | 46B7EEB71D00CCB0007368E8 /* MSSKeyboardManager */, 65 | 46C64DFA1CC6219400FB223B /* Products */, 66 | ); 67 | sourceTree = ""; 68 | }; 69 | 46C64DFA1CC6219400FB223B /* Products */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 46C64DF91CC6219400FB223B /* Keyboard Example.app */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | 46C64DFB1CC6219400FB223B /* Keyboard Example */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 46C64E1D1CC6230100FB223B /* Source */, 81 | 46C64E1B1CC6230100FB223B /* Resources */, 82 | 46C64DFC1CC6219400FB223B /* Supporting Files */, 83 | ); 84 | path = "Keyboard Example"; 85 | sourceTree = ""; 86 | }; 87 | 46C64DFC1CC6219400FB223B /* Supporting Files */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 46C64DFD1CC6219400FB223B /* main.m */, 91 | ); 92 | name = "Supporting Files"; 93 | sourceTree = ""; 94 | }; 95 | 46C64E1B1CC6230100FB223B /* Resources */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 46C64E0A1CC6219400FB223B /* LaunchScreen.storyboard */, 99 | 46C64E1C1CC6230100FB223B /* Assets.xcassets */, 100 | 46C64E0D1CC6219400FB223B /* Info.plist */, 101 | ); 102 | path = Resources; 103 | sourceTree = ""; 104 | }; 105 | 46C64E1D1CC6230100FB223B /* Source */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 46C64E1E1CC6230100FB223B /* AppDelegate.h */, 109 | 46C64E1F1CC6230100FB223B /* AppDelegate.m */, 110 | 46C64E201CC6230100FB223B /* UI */, 111 | ); 112 | path = Source; 113 | sourceTree = ""; 114 | }; 115 | 46C64E201CC6230100FB223B /* UI */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 46C64E211CC6230100FB223B /* Screens */, 119 | ); 120 | path = UI; 121 | sourceTree = ""; 122 | }; 123 | 46C64E211CC6230100FB223B /* Screens */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 46C64E051CC6219400FB223B /* Main.storyboard */, 127 | 46C64E221CC6230100FB223B /* ViewController.h */, 128 | 46C64E231CC6230100FB223B /* ViewController.m */, 129 | ); 130 | path = Screens; 131 | sourceTree = ""; 132 | }; 133 | /* End PBXGroup section */ 134 | 135 | /* Begin PBXNativeTarget section */ 136 | 46C64DF81CC6219400FB223B /* Keyboard Example */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = 46C64E101CC6219400FB223B /* Build configuration list for PBXNativeTarget "Keyboard Example" */; 139 | buildPhases = ( 140 | 46C64DF51CC6219400FB223B /* Sources */, 141 | 46C64DF61CC6219400FB223B /* Frameworks */, 142 | 46C64DF71CC6219400FB223B /* Resources */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = "Keyboard Example"; 149 | productName = "Keyboard Example"; 150 | productReference = 46C64DF91CC6219400FB223B /* Keyboard Example.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | 46C64DF11CC6219400FB223B /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 0730; 160 | ORGANIZATIONNAME = "Merrick Sapsford"; 161 | TargetAttributes = { 162 | 46C64DF81CC6219400FB223B = { 163 | CreatedOnToolsVersion = 7.3; 164 | }; 165 | }; 166 | }; 167 | buildConfigurationList = 46C64DF41CC6219400FB223B /* Build configuration list for PBXProject "Keyboard Example" */; 168 | compatibilityVersion = "Xcode 3.2"; 169 | developmentRegion = English; 170 | hasScannedForEncodings = 0; 171 | knownRegions = ( 172 | en, 173 | Base, 174 | ); 175 | mainGroup = 46C64DF01CC6219400FB223B; 176 | productRefGroup = 46C64DFA1CC6219400FB223B /* Products */; 177 | projectDirPath = ""; 178 | projectRoot = ""; 179 | targets = ( 180 | 46C64DF81CC6219400FB223B /* Keyboard Example */, 181 | ); 182 | }; 183 | /* End PBXProject section */ 184 | 185 | /* Begin PBXResourcesBuildPhase section */ 186 | 46C64DF71CC6219400FB223B /* Resources */ = { 187 | isa = PBXResourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 46C64E0C1CC6219400FB223B /* LaunchScreen.storyboard in Resources */, 191 | 46C64E241CC6230100FB223B /* Assets.xcassets in Resources */, 192 | 46C64E071CC6219400FB223B /* Main.storyboard in Resources */, 193 | ); 194 | runOnlyForDeploymentPostprocessing = 0; 195 | }; 196 | /* End PBXResourcesBuildPhase section */ 197 | 198 | /* Begin PBXSourcesBuildPhase section */ 199 | 46C64DF51CC6219400FB223B /* Sources */ = { 200 | isa = PBXSourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | 46C64E251CC6230100FB223B /* AppDelegate.m in Sources */, 204 | 46C64DFE1CC6219400FB223B /* main.m in Sources */, 205 | 46B7EEBC1D00CCB0007368E8 /* MSSKeyboardManager.m in Sources */, 206 | 46B7EEBD1D00CCB0007368E8 /* UIView+MSSKeyboardDismiss.m in Sources */, 207 | 46C64E261CC6230100FB223B /* ViewController.m in Sources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXSourcesBuildPhase section */ 212 | 213 | /* Begin PBXVariantGroup section */ 214 | 46C64E051CC6219400FB223B /* Main.storyboard */ = { 215 | isa = PBXVariantGroup; 216 | children = ( 217 | 46C64E061CC6219400FB223B /* Base */, 218 | ); 219 | name = Main.storyboard; 220 | path = ../../..; 221 | sourceTree = ""; 222 | }; 223 | 46C64E0A1CC6219400FB223B /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 46C64E0B1CC6219400FB223B /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | path = ..; 230 | sourceTree = ""; 231 | }; 232 | /* End PBXVariantGroup section */ 233 | 234 | /* Begin XCBuildConfiguration section */ 235 | 46C64E0E1CC6219400FB223B /* Debug */ = { 236 | isa = XCBuildConfiguration; 237 | buildSettings = { 238 | ALWAYS_SEARCH_USER_PATHS = NO; 239 | CLANG_ANALYZER_NONNULL = YES; 240 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 241 | CLANG_CXX_LIBRARY = "libc++"; 242 | CLANG_ENABLE_MODULES = YES; 243 | CLANG_ENABLE_OBJC_ARC = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_CONSTANT_CONVERSION = YES; 246 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 247 | CLANG_WARN_EMPTY_BODY = YES; 248 | CLANG_WARN_ENUM_CONVERSION = YES; 249 | CLANG_WARN_INT_CONVERSION = YES; 250 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 251 | CLANG_WARN_UNREACHABLE_CODE = YES; 252 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 253 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 254 | COPY_PHASE_STRIP = NO; 255 | DEBUG_INFORMATION_FORMAT = dwarf; 256 | ENABLE_STRICT_OBJC_MSGSEND = YES; 257 | ENABLE_TESTABILITY = YES; 258 | GCC_C_LANGUAGE_STANDARD = gnu99; 259 | GCC_DYNAMIC_NO_PIC = NO; 260 | GCC_NO_COMMON_BLOCKS = YES; 261 | GCC_OPTIMIZATION_LEVEL = 0; 262 | GCC_PREPROCESSOR_DEFINITIONS = ( 263 | "DEBUG=1", 264 | "$(inherited)", 265 | ); 266 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 267 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 268 | GCC_WARN_UNDECLARED_SELECTOR = YES; 269 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 270 | GCC_WARN_UNUSED_FUNCTION = YES; 271 | GCC_WARN_UNUSED_VARIABLE = YES; 272 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 273 | MTL_ENABLE_DEBUG_INFO = YES; 274 | ONLY_ACTIVE_ARCH = YES; 275 | SDKROOT = iphoneos; 276 | }; 277 | name = Debug; 278 | }; 279 | 46C64E0F1CC6219400FB223B /* Release */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ALWAYS_SEARCH_USER_PATHS = NO; 283 | CLANG_ANALYZER_NONNULL = YES; 284 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 285 | CLANG_CXX_LIBRARY = "libc++"; 286 | CLANG_ENABLE_MODULES = YES; 287 | CLANG_ENABLE_OBJC_ARC = YES; 288 | CLANG_WARN_BOOL_CONVERSION = YES; 289 | CLANG_WARN_CONSTANT_CONVERSION = YES; 290 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 291 | CLANG_WARN_EMPTY_BODY = YES; 292 | CLANG_WARN_ENUM_CONVERSION = YES; 293 | CLANG_WARN_INT_CONVERSION = YES; 294 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 295 | CLANG_WARN_UNREACHABLE_CODE = YES; 296 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 297 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 298 | COPY_PHASE_STRIP = NO; 299 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 300 | ENABLE_NS_ASSERTIONS = NO; 301 | ENABLE_STRICT_OBJC_MSGSEND = YES; 302 | GCC_C_LANGUAGE_STANDARD = gnu99; 303 | GCC_NO_COMMON_BLOCKS = YES; 304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 306 | GCC_WARN_UNDECLARED_SELECTOR = YES; 307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 308 | GCC_WARN_UNUSED_FUNCTION = YES; 309 | GCC_WARN_UNUSED_VARIABLE = YES; 310 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 311 | MTL_ENABLE_DEBUG_INFO = NO; 312 | SDKROOT = iphoneos; 313 | VALIDATE_PRODUCT = YES; 314 | }; 315 | name = Release; 316 | }; 317 | 46C64E111CC6219400FB223B /* Debug */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 321 | INFOPLIST_FILE = "Keyboard Example/Info.plist"; 322 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 323 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 324 | PRODUCT_BUNDLE_IDENTIFIER = "com.msapsford.Keyboard-Example"; 325 | PRODUCT_NAME = "$(TARGET_NAME)"; 326 | TARGETED_DEVICE_FAMILY = 1; 327 | }; 328 | name = Debug; 329 | }; 330 | 46C64E121CC6219400FB223B /* Release */ = { 331 | isa = XCBuildConfiguration; 332 | buildSettings = { 333 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 334 | INFOPLIST_FILE = "Keyboard Example/Info.plist"; 335 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 336 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 337 | PRODUCT_BUNDLE_IDENTIFIER = "com.msapsford.Keyboard-Example"; 338 | PRODUCT_NAME = "$(TARGET_NAME)"; 339 | TARGETED_DEVICE_FAMILY = 1; 340 | }; 341 | name = Release; 342 | }; 343 | /* End XCBuildConfiguration section */ 344 | 345 | /* Begin XCConfigurationList section */ 346 | 46C64DF41CC6219400FB223B /* Build configuration list for PBXProject "Keyboard Example" */ = { 347 | isa = XCConfigurationList; 348 | buildConfigurations = ( 349 | 46C64E0E1CC6219400FB223B /* Debug */, 350 | 46C64E0F1CC6219400FB223B /* Release */, 351 | ); 352 | defaultConfigurationIsVisible = 0; 353 | defaultConfigurationName = Release; 354 | }; 355 | 46C64E101CC6219400FB223B /* Build configuration list for PBXNativeTarget "Keyboard Example" */ = { 356 | isa = XCConfigurationList; 357 | buildConfigurations = ( 358 | 46C64E111CC6219400FB223B /* Debug */, 359 | 46C64E121CC6219400FB223B /* Release */, 360 | ); 361 | defaultConfigurationIsVisible = 0; 362 | defaultConfigurationName = Release; 363 | }; 364 | /* End XCConfigurationList section */ 365 | }; 366 | rootObject = 46C64DF11CC6219400FB223B /* Project object */; 367 | } 368 | -------------------------------------------------------------------------------- /Example/Keyboard Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Keyboard Example.xcodeproj/xcshareddata/xcschemes/Keyboard Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Example/Keyboard Example/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /Example/Keyboard Example/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 | 30 | 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 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Example/Keyboard Example/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 | UIViewControllerBasedStatusBarAppearance 40 | 41 | UIStatusBarStyle 42 | UIStatusBarStyleLightContent 43 | 44 | 45 | -------------------------------------------------------------------------------- /Example/Keyboard Example/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "size" : "60x60", 25 | "idiom" : "iphone", 26 | "filename" : "Icon@2x.png", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "size" : "60x60", 31 | "idiom" : "iphone", 32 | "filename" : "Icon@3x.png", 33 | "scale" : "3x" 34 | } 35 | ], 36 | "info" : { 37 | "version" : 1, 38 | "author" : "xcode" 39 | } 40 | } -------------------------------------------------------------------------------- /Example/Keyboard Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msaps/MSSKeyboardManager/51102bb39fe554c281f8f096c62e5f86deb4fc03/Example/Keyboard Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /Example/Keyboard Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msaps/MSSKeyboardManager/51102bb39fe554c281f8f096c62e5f86deb4fc03/Example/Keyboard Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon@3x.png -------------------------------------------------------------------------------- /Example/Keyboard Example/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Keyboard Example/Resources/Assets.xcassets/background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "background.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Keyboard Example/Resources/Assets.xcassets/background.imageset/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msaps/MSSKeyboardManager/51102bb39fe554c281f8f096c62e5f86deb4fc03/Example/Keyboard Example/Resources/Assets.xcassets/background.imageset/background.jpg -------------------------------------------------------------------------------- /Example/Keyboard Example/Source/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Keyboard Example 4 | // 5 | // Created by Merrick Sapsford on 19/04/2016. 6 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Example/Keyboard Example/Source/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Keyboard Example 4 | // 5 | // Created by Merrick Sapsford on 19/04/2016. 6 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Example/Keyboard Example/Source/UI/Screens/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Keyboard Example 4 | // 5 | // Created by Merrick Sapsford on 19/04/2016. 6 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MSSKeyboardManager.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/Keyboard Example/Source/UI/Screens/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Keyboard Example 4 | // 5 | // Created by Merrick Sapsford on 19/04/2016. 6 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @property (nonatomic, strong) MSSKeyboardManager *keyboardManager; 14 | 15 | @property (nonatomic, weak) IBOutlet UITextField *textField; 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | #pragma mark - Lifecycle 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | self.keyboardManager = [MSSKeyboardManager keyboardManagerForResponder:self]; 27 | [self.view becomeKeyboardDismissalResponder]; 28 | 29 | [self.textField becomeFirstResponder]; 30 | } 31 | 32 | #pragma mark - MSSKeyboardManagerDelegate 33 | 34 | - (void)keyboardManager:(MSSKeyboardManager *)delegate willShowKeyboardWithUpdate:(MSSKeyboardUpdate *)update { 35 | } 36 | 37 | - (void)keyboardManager:(MSSKeyboardManager *)delegate didShowKeyboardWithUpdate:(MSSKeyboardUpdate *)update { 38 | NSLog(@"didShowKeyboard"); 39 | } 40 | 41 | - (void)keyboardManager:(MSSKeyboardManager *)delegate willHideKeyboardWithUpdate:(MSSKeyboardUpdate *)update { 42 | } 43 | 44 | - (void)keyboardManager:(MSSKeyboardManager *)delegate didHideKeyboardWithUpdate:(MSSKeyboardUpdate *)update { 45 | NSLog(@"didHideKeyboard"); 46 | } 47 | 48 | - (void)keyboardManager:(MSSKeyboardManager *)delegate keyboardWillUpdateFromFrame:(CGRect)frame isDocked:(BOOL)docked { 49 | } 50 | 51 | - (void)keyboardManager:(MSSKeyboardManager *)delegate keyboardDidUpdateToFrame:(CGRect)frame isDocked:(BOOL)docked { 52 | NSLog(@"keyboardDidUpdateToFrame %@ Docked: %@", NSStringFromCGRect(frame), docked ? @"YES" : @"NO"); 53 | 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Example/Keyboard Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Keyboard Example 4 | // 5 | // Created by Merrick Sapsford on 19/04/2016. 6 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Merrick Sapsford 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 | -------------------------------------------------------------------------------- /MSSKeyboardManager.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "MSSKeyboardManager" 4 | s.version = "0.1.1" 5 | s.summary = "Utility for managing and assisting with iOS keyboard updates." 6 | 7 | s.description = <<-DESC 8 | MSSKeyboardManager is a utility class that provides enhanced delegation for keyboard updates, and a keyboard dismissal component for UIView. 9 | DESC 10 | 11 | s.homepage = "https://github.com/MerrickSapsford/MSSKeyboardManager" 12 | s.license = "MIT" 13 | s.author = { "Merrick Sapsford" => "merrick@sapsford.tech" } 14 | s.social_media_url = "http://twitter.com/MerrickSapsford" 15 | 16 | s.platform = :ios, "8.0" 17 | s.source = { :git => "https://github.com/MerrickSapsford/MSSKeyboardManager.git", :tag => s.version.to_s } 18 | s.requires_arc = true 19 | s.source_files = "MSSKeyboardManager/Classes", "Source/**/*.{h,m}" 20 | s.frameworks = 'UIKit' 21 | 22 | end 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | MSSKeyboardManager 3 |

4 | 5 | # MSSKeyboardManager 6 | [![Build Status](https://travis-ci.org/MerrickSapsford/MSSKeyboardManager.svg?branch=develop)](https://travis-ci.org/MerrickSapsford/MSSKeyboardManager) 7 | [![CocoaPods](https://img.shields.io/cocoapods/v/MSSKeyboardManager.svg)]() 8 | 9 | MSSKeyboardManager is a utility class that provides enhanced delegation for keyboard updates, and a keyboard dismissal component for UIView. 10 | 11 | ## Installation 12 | MSSKeyboardManager is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile: 13 | 14 | pod 'MSSKeyboardManager' 15 | 16 | ## Usage 17 | To run the example project, clone the repo. Use `pod install` in your project. 18 | 19 | ###Keyboard Manager 20 | To use `MSSKeyboardManager`, initialise the object with a responder that implements `MSSKeyboardManagerDelegate` via `keyboardManagerForResponder:` 21 | 22 | The following protocol methods are then available to the responder: 23 | 24 | ``` 25 | - (void)keyboardManager:willShowKeyboardWithUpdate: 26 | 27 | - (void)keyboardManager:didShowKeyboardWithUpdate: 28 | 29 | - (void)keyboardManager:willHideKeyboardWithUpdate: 30 | 31 | - (void)keyboardManager:didHideKeyboardWithUpdate: 32 | 33 | - (void)keyboardManager:keyboardWillUpdateFromFrame:isDocked: 34 | 35 | - (void)keyboardManager:keyboardDidUpdateToFrame:isDocked: 36 | ``` 37 | 38 | The `MSSKeyboardUpdate` object contains all the releavent information for the active update: 39 | 40 | - `beginFrame` - The frame of the keyboard at the beginning of the update. 41 | - `endFrame` - The frame of the keyboard at the end of the update. 42 | - `animationDuration` - The duration of the update animation. 43 | - `animationCurve` - The update animation curve. 44 | - `isLocal` - Whether the update was invoked by the local app (iOS 9+) 45 | - `keyboardVisible` - Whether the keyboard will be visible as a result of the update. 46 | - `keyboardDocked` - Whether the keyboard is docked during the update (iPad). 47 | 48 | ###Keyboard Dismisser 49 | `UIView+MSSKeyboardDismiss` is a category on UIView that provides keyboard dismissal on tap. 50 | 51 | To allow a UIView to dismiss the keyboard on tap, simply call `becomeKeyboardDismissalResponder` on the view. 52 | 53 | For example, in the context of a UIViewController: 54 | ``` 55 | [self.view becomeKeyboardDismissalResponder]; 56 | ``` 57 | 58 | Subviews of the view can then opt out of allowing dismissal on tap with the `canDismissKeyboard` property (IBInspectable). 59 | 60 | The category also provides `resignFirstResponderWithCompletion:` to allow the keyboard to be dismissed with a completion block. 61 | 62 | ## Requirements 63 | Supports iOS 8 and above. 64 | 65 | ## Author 66 | Merrick Sapsford 67 | 68 | Mail: [merrick@sapsford.tech](mailto:merrick@sapsford.tech) 69 | -------------------------------------------------------------------------------- /Resource/MSSKeyboardManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msaps/MSSKeyboardManager/51102bb39fe554c281f8f096c62e5f86deb4fc03/Resource/MSSKeyboardManager.png -------------------------------------------------------------------------------- /Source/MSSKeyboardManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSSKeyboardManager.h 3 | // 4 | // Created by Merrick Sapsford on 07/04/2016. 5 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "UIView+MSSKeyboardDismiss.h" 10 | 11 | @class MSSKeyboardManager; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | @interface MSSKeyboardUpdate : NSObject 15 | 16 | /** 17 | The frame of the keyboard at the beginning of the update. 18 | */ 19 | @property (nonatomic, assign, readonly) CGRect beginFrame; 20 | /** 21 | The frame of the keyboard at the end of the update. 22 | */ 23 | @property (nonatomic, assign, readonly) CGRect endFrame; 24 | 25 | /** 26 | The duration of the update animation. 27 | */ 28 | @property (nonatomic, assign, readonly) CGFloat animationDuration; 29 | /** 30 | The update animation curve. 31 | */ 32 | @property (nonatomic, assign, readonly) UIViewAnimationCurve animationCurve; 33 | 34 | /** 35 | Whether the update was summoned by the local app in split view. 36 | */ 37 | @property (nonatomic, assign, readonly) BOOL isLocal NS_AVAILABLE_IOS(9_0); 38 | 39 | /** 40 | Whether the keyboard will be visible due to the update. 41 | */ 42 | @property (nonatomic, assign, readonly) BOOL keyboardVisible; 43 | /** 44 | Whether the keyboard is docked during the update (iPad only) 45 | */ 46 | @property (nonatomic, assign, readonly) BOOL keyboardDocked; 47 | 48 | + (instancetype)updateWithDictionary:(NSDictionary *)updateDictionary; 49 | 50 | @end 51 | 52 | @protocol MSSKeyboardManagerDelegate 53 | @optional 54 | 55 | /** 56 | The keyboard will show. 57 | 58 | @param manager 59 | The keyboard manager. 60 | 61 | @param update 62 | The keyboard update 63 | */ 64 | - (void)keyboardManager:(MSSKeyboardManager *)manager willShowKeyboardWithUpdate:(MSSKeyboardUpdate *)update; 65 | /** 66 | The keyboard did show. 67 | 68 | @param manager 69 | The keyboard manager. 70 | 71 | @param update 72 | The keyboard update 73 | */ 74 | - (void)keyboardManager:(MSSKeyboardManager *)manager didShowKeyboardWithUpdate:(MSSKeyboardUpdate *)update; 75 | /** 76 | The keyboard will hide. 77 | 78 | @param manager 79 | The keyboard manager. 80 | 81 | @param update 82 | The keyboard update 83 | */ 84 | - (void)keyboardManager:(MSSKeyboardManager *)manager willHideKeyboardWithUpdate:(MSSKeyboardUpdate *)update; 85 | /** 86 | The keyboard did hide. 87 | 88 | @param manager 89 | The keyboard manager. 90 | 91 | @param update 92 | The keyboard update 93 | */ 94 | - (void)keyboardManager:(MSSKeyboardManager *)manager didHideKeyboardWithUpdate:(MSSKeyboardUpdate *)update; 95 | /** 96 | The keyboard will update from its current frame. 97 | 98 | @param manager 99 | The keyboard manager. 100 | 101 | @param frame 102 | The keyboard current frame. 103 | 104 | @param docked 105 | Whether the keyboard is docked. 106 | */ 107 | - (void)keyboardManager:(MSSKeyboardManager *)manager keyboardWillUpdateFromFrame:(CGRect)frame isDocked:(BOOL)docked; 108 | /** 109 | The keyboard did update to a new frame. 110 | 111 | @param manager 112 | The keyboard manager. 113 | 114 | @param frame 115 | The new keyboard frame. 116 | 117 | @param docked 118 | Whether the keyboard is docked. 119 | */ 120 | - (void)keyboardManager:(MSSKeyboardManager *)manager keyboardDidUpdateToFrame:(CGRect)frame isDocked:(BOOL)docked; 121 | 122 | @end 123 | 124 | @interface MSSKeyboardManager : NSObject 125 | 126 | /** 127 | The object that responds to keyboard updates. 128 | */ 129 | @property (nonatomic, weak, readonly) id responder; 130 | 131 | /** 132 | Whether the keyboard manager is currently ignoring keyboard updates. 133 | */ 134 | @property (nonatomic, assign, readonly, getter=isIgnoringUpdates) BOOL ignoringUpdates; 135 | 136 | + (instancetype)keyboardManagerForResponder:(id)responder; 137 | 138 | /** 139 | Start ignoring any keyboard updates and stop updating responder. 140 | */ 141 | - (void)startIgnoringUpdates; 142 | /** 143 | Resume listening to keyboard updates and updating responder. 144 | */ 145 | - (void)stopIgnoringUpdates; 146 | 147 | @end 148 | NS_ASSUME_NONNULL_END 149 | -------------------------------------------------------------------------------- /Source/MSSKeyboardManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSSKeyboardManager.m 3 | // 4 | // Created by Merrick Sapsford on 07/04/2016. 5 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 6 | // 7 | 8 | #import "MSSKeyboardManager.h" 9 | 10 | @implementation MSSKeyboardUpdate 11 | 12 | + (instancetype)updateWithDictionary:(NSDictionary *)updateDictionary { 13 | return [[[self class]alloc]initWithDictionary:updateDictionary]; 14 | } 15 | 16 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary { 17 | if (self = [super init]) { 18 | _beginFrame = [[dictionary objectForKey:UIKeyboardFrameBeginUserInfoKey]CGRectValue]; 19 | _endFrame = [[dictionary objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue]; 20 | _animationDuration = [[dictionary objectForKey:UIKeyboardAnimationDurationUserInfoKey]floatValue]; 21 | _animationCurve = [[dictionary objectForKey:UIKeyboardAnimationCurveUserInfoKey]unsignedIntegerValue]; 22 | 23 | if (&UIKeyboardIsLocalUserInfoKey) { 24 | _isLocal = [[dictionary objectForKey:UIKeyboardIsLocalUserInfoKey]boolValue]; 25 | } 26 | 27 | UIInterfaceOrientation currentOrientation = [UIApplication sharedApplication].statusBarOrientation; 28 | CGSize screenSize = [UIScreen mainScreen].bounds.size; 29 | CGFloat screenHeight = UIInterfaceOrientationIsPortrait(currentOrientation) ? screenSize.height : screenSize.width; 30 | 31 | CGRect frame = CGRectEqualToRect(self.endFrame, CGRectZero) ? self.beginFrame : self.endFrame; // default to end frame 32 | CGFloat dockedYOffset = screenHeight - frame.size.height; 33 | CGFloat actualYOffset = CGRectGetMinY(frame); 34 | 35 | if (actualYOffset != screenHeight && (CGRectGetMaxY(frame) <= screenHeight)) { // if keyboard is visible 36 | _keyboardVisible = YES; 37 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad && (actualYOffset < dockedYOffset)) { // check if keyboard is docked 38 | _keyboardDocked = (dockedYOffset == actualYOffset); 39 | } else { 40 | _keyboardDocked = YES; // always docked on iPhone 41 | } 42 | } else { // keyboard not visible 43 | _keyboardVisible = NO; 44 | } 45 | } 46 | return self; 47 | } 48 | 49 | @end 50 | 51 | @implementation MSSKeyboardManager 52 | 53 | #pragma mark - Init 54 | 55 | + (instancetype)keyboardManagerForResponder:(id)responder { 56 | return [[[self class]alloc]initWithResponder:responder]; 57 | } 58 | 59 | - (instancetype)initWithResponder:(id)responder { 60 | if (self = [super init]) { 61 | _responder = responder; 62 | [self registerNotifications]; 63 | } 64 | return self; 65 | } 66 | 67 | #pragma mark - Lifecycle 68 | 69 | - (void)dealloc { 70 | [self unregisterNotifications]; 71 | } 72 | 73 | #pragma mark - Notifications 74 | 75 | - (void)keyboardWillShowNotification:(NSNotification *)notification { 76 | if ([self.responder respondsToSelector:@selector(keyboardManager:willShowKeyboardWithUpdate:)]) { 77 | MSSKeyboardUpdate *update = [MSSKeyboardUpdate updateWithDictionary:notification.userInfo]; 78 | [self.responder keyboardManager:self willShowKeyboardWithUpdate:update]; 79 | } 80 | } 81 | 82 | - (void)keyboardDidShowNotification:(NSNotification *)notification { 83 | if ([self.responder respondsToSelector:@selector(keyboardManager:didShowKeyboardWithUpdate:)]) { 84 | MSSKeyboardUpdate *update = [MSSKeyboardUpdate updateWithDictionary:notification.userInfo]; 85 | [self.responder keyboardManager:self didShowKeyboardWithUpdate:update]; 86 | } 87 | } 88 | 89 | - (void)keyboardWillHideNotification:(NSNotification *)notification { 90 | if ([self.responder respondsToSelector:@selector(keyboardManager:willHideKeyboardWithUpdate:)]) { 91 | MSSKeyboardUpdate *update = [MSSKeyboardUpdate updateWithDictionary:notification.userInfo]; 92 | [self.responder keyboardManager:self willHideKeyboardWithUpdate:update]; 93 | } 94 | } 95 | 96 | - (void)keyboardDidHideNotification:(NSNotification *)notification { 97 | if ([self.responder respondsToSelector:@selector(keyboardManager:didHideKeyboardWithUpdate:)]) { 98 | MSSKeyboardUpdate *update = [MSSKeyboardUpdate updateWithDictionary:notification.userInfo]; 99 | [self.responder keyboardManager:self didHideKeyboardWithUpdate:update]; 100 | } 101 | } 102 | 103 | - (void)keyboardWillChangeFrameNotification:(NSNotification *)notification { 104 | MSSKeyboardUpdate *update = [MSSKeyboardUpdate updateWithDictionary:notification.userInfo]; 105 | if ((update.keyboardVisible && !CGRectEqualToRect(update.beginFrame, CGRectZero)) && 106 | [self.responder respondsToSelector:@selector(keyboardManager:keyboardWillUpdateFromFrame:isDocked:)]) { 107 | 108 | [self.responder keyboardManager:self keyboardWillUpdateFromFrame:update.beginFrame isDocked:update.keyboardDocked]; 109 | } 110 | } 111 | 112 | - (void)keyboardDidChangeFrameNotification:(NSNotification *)notification { 113 | MSSKeyboardUpdate *update = [MSSKeyboardUpdate updateWithDictionary:notification.userInfo]; 114 | if ((update.keyboardVisible && !CGRectEqualToRect(update.endFrame, CGRectZero)) && 115 | [self.responder respondsToSelector:@selector(keyboardManager:keyboardDidUpdateToFrame:isDocked:)]) { 116 | 117 | [self.responder keyboardManager:self keyboardDidUpdateToFrame:update.endFrame isDocked:update.keyboardDocked]; 118 | } 119 | } 120 | 121 | #pragma mark - Public 122 | 123 | - (void)startIgnoringUpdates { 124 | if (!_ignoringUpdates) { 125 | _ignoringUpdates = YES; 126 | [self unregisterNotifications]; 127 | } 128 | } 129 | 130 | - (void)stopIgnoringUpdates { 131 | if (_ignoringUpdates) { 132 | _ignoringUpdates = NO; 133 | [self registerNotifications]; 134 | } 135 | } 136 | 137 | #pragma mark - Internal 138 | 139 | - (void)registerNotifications { 140 | [[NSNotificationCenter defaultCenter]addObserver:self 141 | selector:@selector(keyboardWillShowNotification:) 142 | name:UIKeyboardWillShowNotification object:nil]; 143 | [[NSNotificationCenter defaultCenter]addObserver:self 144 | selector:@selector(keyboardDidShowNotification:) 145 | name:UIKeyboardDidShowNotification object:nil]; 146 | [[NSNotificationCenter defaultCenter]addObserver:self 147 | selector:@selector(keyboardWillHideNotification:) 148 | name:UIKeyboardWillHideNotification object:nil]; 149 | [[NSNotificationCenter defaultCenter]addObserver:self 150 | selector:@selector(keyboardDidHideNotification:) 151 | name:UIKeyboardDidHideNotification object:nil]; 152 | [[NSNotificationCenter defaultCenter]addObserver:self 153 | selector:@selector(keyboardWillChangeFrameNotification:) 154 | name:UIKeyboardWillChangeFrameNotification object:nil]; 155 | [[NSNotificationCenter defaultCenter]addObserver:self 156 | selector:@selector(keyboardDidChangeFrameNotification:) 157 | name:UIKeyboardDidChangeFrameNotification object:nil]; 158 | } 159 | 160 | - (void)unregisterNotifications { 161 | [[NSNotificationCenter defaultCenter]removeObserver:self]; 162 | } 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /Source/UIView+MSSKeyboardDismiss.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+MSSKeyboardDismiss.h 3 | // 4 | // Created by Merrick Sapsford on 04/04/2016. 5 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | typedef void(^MSSKeyboardDismissCompetion)(BOOL resigned); 11 | 12 | IB_DESIGNABLE 13 | @interface UIView (MSSKeyboardDismiss) 14 | 15 | /** 16 | Whether the view can dismiss keyboard on tap. 17 | */ 18 | @property (nonatomic, assign) IBInspectable BOOL canDismissKeyboard; 19 | 20 | /** 21 | Become the responder for dismissing the keyboard on tap. 22 | */ 23 | - (void)becomeKeyboardDismissalResponder; 24 | /** 25 | Resign the active first responder with completion. 26 | 27 | @param completion 28 | The completion block. 29 | */ 30 | - (void)resignFirstResponderWithCompletion:(MSSKeyboardDismissCompetion)completion; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Source/UIView+MSSKeyboardDismiss.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+MSSKeyboardDismiss.m 3 | // 4 | // Created by Merrick Sapsford on 04/04/2016. 5 | // Copyright © 2016 Merrick Sapsford. All rights reserved. 6 | // 7 | 8 | #import "UIView+MSSKeyboardDismiss.h" 9 | #import 10 | 11 | @implementation UIView (MSSKeyboardDismiss) 12 | 13 | static id _currentFirstResponder; 14 | static char TAP_GESTURE_RECOGNIZER; 15 | static CGFloat _keyboardAnimationDuration; 16 | 17 | #pragma mark - Lifecycle 18 | 19 | - (BOOL)becomeFirstResponder { 20 | _currentFirstResponder = self; 21 | 22 | // if keyboard anim length is unknown attempt to get it 23 | if (_keyboardAnimationDuration == 0.0f) { 24 | [[NSNotificationCenter defaultCenter]addObserver:self 25 | selector:@selector(keyboardWillShow:) 26 | name:UIKeyboardWillShowNotification 27 | object:nil]; 28 | } 29 | 30 | return [super becomeFirstResponder]; 31 | } 32 | 33 | - (BOOL)resignFirstResponder { 34 | [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 35 | return [super resignFirstResponder]; 36 | } 37 | 38 | #pragma mark - Gesture Recognizer delegate 39 | 40 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 41 | shouldReceiveTouch:(UITouch *)touch { 42 | UIView *targetView = touch.view; 43 | 44 | BOOL shouldRespond = YES; 45 | if ([targetView respondsToSelector:@selector(canDismissKeyboard)]) { // only respond if view allows 46 | shouldRespond = [targetView canDismissKeyboard]; 47 | } 48 | 49 | // dont respond if view is the first responder 50 | if (targetView.isFirstResponder || !shouldRespond || !self.canDismissKeyboard) { 51 | return NO; 52 | } 53 | return YES; 54 | } 55 | 56 | #pragma mark - Public 57 | 58 | - (void)becomeKeyboardDismissalResponder { 59 | if (!self.tapGestureRecognizer) { 60 | [self addGestureRecognizer:self.tapRecognizer]; 61 | } 62 | } 63 | 64 | - (void)setCanDismissKeyboard:(BOOL)canDismissKeyboard { 65 | objc_setAssociatedObject(self, 66 | @selector(canDismissKeyboard), 67 | @(canDismissKeyboard), 68 | OBJC_ASSOCIATION_RETAIN_NONATOMIC); 69 | } 70 | 71 | - (BOOL)canDismissKeyboard { 72 | NSNumber *value = objc_getAssociatedObject(self, @selector(canDismissKeyboard)); 73 | if (!value) { 74 | return YES; 75 | } 76 | return [value boolValue]; 77 | } 78 | 79 | - (void)resignFirstResponderWithCompletion:(MSSKeyboardDismissCompetion)completion { 80 | BOOL resigned = [self resignFirstResponder]; 81 | 82 | if (completion) { 83 | if (resigned) { 84 | CGFloat duration = _keyboardAnimationDuration > 0.0f ? _keyboardAnimationDuration : 0.25f; 85 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, duration * NSEC_PER_SEC), 86 | dispatch_get_main_queue(), ^{ 87 | completion(resigned); 88 | }); 89 | } else { 90 | completion(resigned); 91 | } 92 | } 93 | } 94 | 95 | #pragma mark - Internal 96 | 97 | - (void)resignCurrentFirstResponder { 98 | [_currentFirstResponder resignFirstResponder]; 99 | } 100 | 101 | - (UITapGestureRecognizer *)tapRecognizer { 102 | UITapGestureRecognizer *tapRecognizer; 103 | tapRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(resignCurrentFirstResponder)]; 104 | tapRecognizer.delegate = self; 105 | tapRecognizer.cancelsTouchesInView = NO; 106 | [self setTapGestureRecognizer:tapRecognizer]; 107 | return tapRecognizer; 108 | } 109 | 110 | - (void)setTapGestureRecognizer:(UITapGestureRecognizer *)tapGestureRecognizer { 111 | objc_setAssociatedObject(self, &TAP_GESTURE_RECOGNIZER, tapGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 112 | } 113 | 114 | - (UITapGestureRecognizer *)tapGestureRecognizer { 115 | return objc_getAssociatedObject(self, &TAP_GESTURE_RECOGNIZER); 116 | } 117 | 118 | - (void)keyboardWillShow:(NSNotification *)notification { 119 | [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 120 | 121 | CGFloat animationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey]floatValue]; 122 | _keyboardAnimationDuration = animationDuration; 123 | } 124 | 125 | 126 | @end 127 | --------------------------------------------------------------------------------