├── .gitignore ├── JPSKeyboardLayoutGuide.podspec ├── JPSKeyboardLayoutGuide ├── JPSKeyboardLayoutGuideViewController.h └── JPSKeyboardLayoutGuideViewController.m ├── JPSKeyboardLayoutGuideDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── JPSKeyboardLayoutGuideDemo ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── JPSAppDelegate.h ├── JPSAppDelegate.m ├── JPSKeyboardLayoutGuideDemo-Info.plist ├── JPSKeyboardLayoutGuideDemo-Prefix.pch ├── LoginVC.h ├── LoginVC.m ├── en.lproj │ └── InfoPlist.strings └── main.m ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | */build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | xcuserdata 12 | profile 13 | *.moved-aside 14 | DerivedData 15 | .idea/ 16 | *.hmap 17 | *.xccheckout 18 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuide.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'JPSKeyboardLayoutGuide' 3 | s.version = '0.0.2' 4 | s.platform = :ios, "7.0" 5 | s.license = 'MIT' 6 | s.summary = 'JPSKeyboardLayoutGuide lets you easily make your autolayout view controllers keyboard aware.' 7 | 8 | s.homepage = 'https://github.com/jpsim/JPSKeyboardLayoutGuide' 9 | s.author = { 'JP Simard' => 'jp@jpsim.com' } 10 | s.source = { :git => 'https://github.com/jpsim/JPSKeyboardLayoutGuide.git', :tag => s.version.to_s } 11 | 12 | s.description = "JPSKeyboardLayoutGuide lets you easily make your autolayout view controllers keyboard aware. It's bottomLayoutGuide, if it moved with the keyboard." 13 | 14 | s.source_files = 'JPSKeyboardLayoutGuide/*.{h,m}' 15 | s.requires_arc = true 16 | end 17 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuide/JPSKeyboardLayoutGuideViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JPSKeyboardLayoutGuideViewController.h 3 | // JPSKeyboardLayoutGuide 4 | // 5 | // Created by JP Simard on 2014-03-26. 6 | // Copyright (c) 2014 JP Simard. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIViewController (JPSKeyboardLayoutGuideViewController) 12 | 13 | - (void)jps_viewDidLoad; 14 | - (void)jps_viewWillAppear:(BOOL)animated; 15 | - (void)jps_viewDidDisappear:(BOOL)animated; 16 | 17 | @property (nonatomic, strong) id keyboardLayoutGuide; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuide/JPSKeyboardLayoutGuideViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JPSKeyboardLayoutGuideViewController.m 3 | // JPSKeyboardLayoutGuide 4 | // 5 | // Created by JP Simard on 2014-03-26. 6 | // Copyright (c) 2014 JP Simard. All rights reserved. 7 | // 8 | 9 | #import "JPSKeyboardLayoutGuideViewController.h" 10 | 11 | #import 12 | 13 | @interface UIViewController (JPSKeyboardLayoutGuideViewController_Internal) 14 | 15 | @property (nonatomic, strong) NSLayoutConstraint *bottomConstraint; 16 | 17 | @end 18 | 19 | @implementation UIViewController (JPSKeyboardLayoutGuideViewController) 20 | 21 | - (void)setBottomConstraint:(NSLayoutConstraint *)bottomConstraint { 22 | objc_setAssociatedObject(self, @selector(bottomConstraint), bottomConstraint, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 23 | } 24 | 25 | - (NSLayoutConstraint *)bottomConstraint { 26 | return objc_getAssociatedObject(self, @selector(bottomConstraint)); 27 | } 28 | 29 | -(void)setKeyboardLayoutGuide:(id)keyboardLayoutGuide{ 30 | objc_setAssociatedObject(self, @selector(keyboardLayoutGuide), keyboardLayoutGuide, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 31 | } 32 | 33 | -(id)keyboardLayoutGuide{ 34 | return objc_getAssociatedObject(self, @selector(keyboardLayoutGuide)); 35 | } 36 | 37 | #pragma mark - View Lifecycle 38 | 39 | -(void)_createKeyboardLayoutGuide 40 | { 41 | self.keyboardLayoutGuide = (id)[UIView new]; 42 | [(UIView *)self.keyboardLayoutGuide setTranslatesAutoresizingMaskIntoConstraints:NO]; 43 | [self.view addSubview:(UIView *)self.keyboardLayoutGuide]; 44 | } 45 | 46 | - (void)jps_viewDidLoad { 47 | [self _createKeyboardLayoutGuide]; 48 | [self setupKeyboardLayoutGuide]; 49 | } 50 | 51 | - (void)jps_viewWillAppear:(BOOL)animated { 52 | [self observeKeyboard]; 53 | } 54 | 55 | - (void)jps_viewDidDisappear:(BOOL)animated { 56 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil]; 57 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 58 | } 59 | 60 | #pragma mark - Keyboard Layout Guide 61 | 62 | - (void)setupKeyboardLayoutGuide { 63 | NSAssert(self.keyboardLayoutGuide, @"keyboardLayoutGuide needs to be created by now"); 64 | // Constraints 65 | NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self.keyboardLayoutGuide attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:0.0f]; 66 | NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self.keyboardLayoutGuide attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:0.0f]; 67 | NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:self.keyboardLayoutGuide attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]; 68 | self.bottomConstraint = [NSLayoutConstraint constraintWithItem:self.keyboardLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]; 69 | [self.view addConstraints:@[width, height, centerX, self.bottomConstraint]]; 70 | } 71 | 72 | #pragma mark - Keyboard Methods 73 | 74 | - (void)observeKeyboard { 75 | [[NSNotificationCenter defaultCenter] addObserver:self 76 | selector:@selector(keyboardWillShow:) 77 | name:UIKeyboardWillChangeFrameNotification 78 | object:nil]; 79 | [[NSNotificationCenter defaultCenter] addObserver:self 80 | selector:@selector(keyboardWillHide:) 81 | name:UIKeyboardWillHideNotification 82 | object:nil]; 83 | } 84 | 85 | - (void)keyboardWillShow:(NSNotification *)notification { 86 | NSDictionary *info = notification.userInfo; 87 | NSValue *kbFrame = info[UIKeyboardFrameEndUserInfoKey]; 88 | NSTimeInterval animationDuration = [info[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 89 | UIViewAnimationCurve curve = [info[UIKeyboardAnimationCurveUserInfoKey] integerValue]; 90 | CGRect keyboardFrame = kbFrame.CGRectValue; 91 | 92 | self.bottomConstraint.constant = -keyboardFrame.size.height; 93 | 94 | [UIView beginAnimations:nil context:nil]; 95 | [UIView setAnimationDuration:animationDuration]; 96 | [UIView setAnimationCurve:curve]; 97 | [self.view layoutIfNeeded]; 98 | [UIView commitAnimations]; 99 | } 100 | 101 | - (void)keyboardWillHide:(NSNotification *)notification { 102 | NSDictionary *info = notification.userInfo; 103 | NSTimeInterval animationDuration = [info[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 104 | UIViewAnimationCurve curve = [info[UIKeyboardAnimationCurveUserInfoKey] integerValue]; 105 | 106 | self.bottomConstraint.constant = 0; 107 | 108 | [UIView beginAnimations:nil context:nil]; 109 | [UIView setAnimationDuration:animationDuration]; 110 | [UIView setAnimationCurve:curve]; 111 | [self.view layoutIfNeeded]; 112 | [UIView commitAnimations]; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E8FF75B718E3506800402A82 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8FF75B618E3506800402A82 /* Foundation.framework */; }; 11 | E8FF75B918E3506800402A82 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8FF75B818E3506800402A82 /* CoreGraphics.framework */; }; 12 | E8FF75BB18E3506800402A82 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8FF75BA18E3506800402A82 /* UIKit.framework */; }; 13 | E8FF75C118E3506800402A82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E8FF75BF18E3506800402A82 /* InfoPlist.strings */; }; 14 | E8FF75C318E3506800402A82 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E8FF75C218E3506800402A82 /* main.m */; }; 15 | E8FF75C718E3506800402A82 /* JPSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8FF75C618E3506800402A82 /* JPSAppDelegate.m */; }; 16 | E8FF75C918E3506800402A82 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E8FF75C818E3506800402A82 /* Images.xcassets */; }; 17 | E8FF75E818E350D400402A82 /* JPSKeyboardLayoutGuideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E8FF75E718E350D400402A82 /* JPSKeyboardLayoutGuideViewController.m */; }; 18 | E8FF75EB18E350E800402A82 /* LoginVC.m in Sources */ = {isa = PBXBuildFile; fileRef = E8FF75EA18E350E800402A82 /* LoginVC.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | E8FF75B318E3506800402A82 /* JPSKeyboardLayoutGuideDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JPSKeyboardLayoutGuideDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | E8FF75B618E3506800402A82 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 24 | E8FF75B818E3506800402A82 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 25 | E8FF75BA18E3506800402A82 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 26 | E8FF75BE18E3506800402A82 /* JPSKeyboardLayoutGuideDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JPSKeyboardLayoutGuideDemo-Info.plist"; sourceTree = ""; }; 27 | E8FF75C018E3506800402A82 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 28 | E8FF75C218E3506800402A82 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | E8FF75C418E3506800402A82 /* JPSKeyboardLayoutGuideDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JPSKeyboardLayoutGuideDemo-Prefix.pch"; sourceTree = ""; }; 30 | E8FF75C518E3506800402A82 /* JPSAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JPSAppDelegate.h; sourceTree = ""; }; 31 | E8FF75C618E3506800402A82 /* JPSAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JPSAppDelegate.m; sourceTree = ""; }; 32 | E8FF75C818E3506800402A82 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 33 | E8FF75CF18E3506800402A82 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 34 | E8FF75E618E350D400402A82 /* JPSKeyboardLayoutGuideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPSKeyboardLayoutGuideViewController.h; sourceTree = ""; }; 35 | E8FF75E718E350D400402A82 /* JPSKeyboardLayoutGuideViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPSKeyboardLayoutGuideViewController.m; sourceTree = ""; }; 36 | E8FF75E918E350E800402A82 /* LoginVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginVC.h; sourceTree = ""; }; 37 | E8FF75EA18E350E800402A82 /* LoginVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginVC.m; sourceTree = ""; }; 38 | /* End PBXFileReference section */ 39 | 40 | /* Begin PBXFrameworksBuildPhase section */ 41 | E8FF75B018E3506800402A82 /* Frameworks */ = { 42 | isa = PBXFrameworksBuildPhase; 43 | buildActionMask = 2147483647; 44 | files = ( 45 | E8FF75B918E3506800402A82 /* CoreGraphics.framework in Frameworks */, 46 | E8FF75BB18E3506800402A82 /* UIKit.framework in Frameworks */, 47 | E8FF75B718E3506800402A82 /* Foundation.framework in Frameworks */, 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | E8FF75AA18E3506800402A82 = { 55 | isa = PBXGroup; 56 | children = ( 57 | E8FF75E518E350D400402A82 /* JPSKeyboardLayoutGuide */, 58 | E8FF75BC18E3506800402A82 /* JPSKeyboardLayoutGuideDemo */, 59 | E8FF75B518E3506800402A82 /* Frameworks */, 60 | E8FF75B418E3506800402A82 /* Products */, 61 | ); 62 | sourceTree = ""; 63 | }; 64 | E8FF75B418E3506800402A82 /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | E8FF75B318E3506800402A82 /* JPSKeyboardLayoutGuideDemo.app */, 68 | ); 69 | name = Products; 70 | sourceTree = ""; 71 | }; 72 | E8FF75B518E3506800402A82 /* Frameworks */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | E8FF75B618E3506800402A82 /* Foundation.framework */, 76 | E8FF75B818E3506800402A82 /* CoreGraphics.framework */, 77 | E8FF75BA18E3506800402A82 /* UIKit.framework */, 78 | E8FF75CF18E3506800402A82 /* XCTest.framework */, 79 | ); 80 | name = Frameworks; 81 | sourceTree = ""; 82 | }; 83 | E8FF75BC18E3506800402A82 /* JPSKeyboardLayoutGuideDemo */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | E8FF75C518E3506800402A82 /* JPSAppDelegate.h */, 87 | E8FF75C618E3506800402A82 /* JPSAppDelegate.m */, 88 | E8FF75E918E350E800402A82 /* LoginVC.h */, 89 | E8FF75EA18E350E800402A82 /* LoginVC.m */, 90 | E8FF75C818E3506800402A82 /* Images.xcassets */, 91 | E8FF75BD18E3506800402A82 /* Supporting Files */, 92 | ); 93 | path = JPSKeyboardLayoutGuideDemo; 94 | sourceTree = ""; 95 | }; 96 | E8FF75BD18E3506800402A82 /* Supporting Files */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | E8FF75BE18E3506800402A82 /* JPSKeyboardLayoutGuideDemo-Info.plist */, 100 | E8FF75BF18E3506800402A82 /* InfoPlist.strings */, 101 | E8FF75C218E3506800402A82 /* main.m */, 102 | E8FF75C418E3506800402A82 /* JPSKeyboardLayoutGuideDemo-Prefix.pch */, 103 | ); 104 | name = "Supporting Files"; 105 | sourceTree = ""; 106 | }; 107 | E8FF75E518E350D400402A82 /* JPSKeyboardLayoutGuide */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | E8FF75E618E350D400402A82 /* JPSKeyboardLayoutGuideViewController.h */, 111 | E8FF75E718E350D400402A82 /* JPSKeyboardLayoutGuideViewController.m */, 112 | ); 113 | path = JPSKeyboardLayoutGuide; 114 | sourceTree = ""; 115 | }; 116 | /* End PBXGroup section */ 117 | 118 | /* Begin PBXNativeTarget section */ 119 | E8FF75B218E3506800402A82 /* JPSKeyboardLayoutGuideDemo */ = { 120 | isa = PBXNativeTarget; 121 | buildConfigurationList = E8FF75DF18E3506800402A82 /* Build configuration list for PBXNativeTarget "JPSKeyboardLayoutGuideDemo" */; 122 | buildPhases = ( 123 | E8FF75AF18E3506800402A82 /* Sources */, 124 | E8FF75B018E3506800402A82 /* Frameworks */, 125 | E8FF75B118E3506800402A82 /* Resources */, 126 | ); 127 | buildRules = ( 128 | ); 129 | dependencies = ( 130 | ); 131 | name = JPSKeyboardLayoutGuideDemo; 132 | productName = JPSKeyboardLayoutGuideDemo; 133 | productReference = E8FF75B318E3506800402A82 /* JPSKeyboardLayoutGuideDemo.app */; 134 | productType = "com.apple.product-type.application"; 135 | }; 136 | /* End PBXNativeTarget section */ 137 | 138 | /* Begin PBXProject section */ 139 | E8FF75AB18E3506800402A82 /* Project object */ = { 140 | isa = PBXProject; 141 | attributes = { 142 | CLASSPREFIX = JPS; 143 | LastUpgradeCheck = 0830; 144 | ORGANIZATIONNAME = "JP Simard"; 145 | }; 146 | buildConfigurationList = E8FF75AE18E3506800402A82 /* Build configuration list for PBXProject "JPSKeyboardLayoutGuideDemo" */; 147 | compatibilityVersion = "Xcode 3.2"; 148 | developmentRegion = English; 149 | hasScannedForEncodings = 0; 150 | knownRegions = ( 151 | en, 152 | ); 153 | mainGroup = E8FF75AA18E3506800402A82; 154 | productRefGroup = E8FF75B418E3506800402A82 /* Products */; 155 | projectDirPath = ""; 156 | projectRoot = ""; 157 | targets = ( 158 | E8FF75B218E3506800402A82 /* JPSKeyboardLayoutGuideDemo */, 159 | ); 160 | }; 161 | /* End PBXProject section */ 162 | 163 | /* Begin PBXResourcesBuildPhase section */ 164 | E8FF75B118E3506800402A82 /* Resources */ = { 165 | isa = PBXResourcesBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | E8FF75C118E3506800402A82 /* InfoPlist.strings in Resources */, 169 | E8FF75C918E3506800402A82 /* Images.xcassets in Resources */, 170 | ); 171 | runOnlyForDeploymentPostprocessing = 0; 172 | }; 173 | /* End PBXResourcesBuildPhase section */ 174 | 175 | /* Begin PBXSourcesBuildPhase section */ 176 | E8FF75AF18E3506800402A82 /* Sources */ = { 177 | isa = PBXSourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | E8FF75E818E350D400402A82 /* JPSKeyboardLayoutGuideViewController.m in Sources */, 181 | E8FF75C718E3506800402A82 /* JPSAppDelegate.m in Sources */, 182 | E8FF75EB18E350E800402A82 /* LoginVC.m in Sources */, 183 | E8FF75C318E3506800402A82 /* main.m in Sources */, 184 | ); 185 | runOnlyForDeploymentPostprocessing = 0; 186 | }; 187 | /* End PBXSourcesBuildPhase section */ 188 | 189 | /* Begin PBXVariantGroup section */ 190 | E8FF75BF18E3506800402A82 /* InfoPlist.strings */ = { 191 | isa = PBXVariantGroup; 192 | children = ( 193 | E8FF75C018E3506800402A82 /* en */, 194 | ); 195 | name = InfoPlist.strings; 196 | sourceTree = ""; 197 | }; 198 | /* End PBXVariantGroup section */ 199 | 200 | /* Begin XCBuildConfiguration section */ 201 | E8FF75DD18E3506800402A82 /* Debug */ = { 202 | isa = XCBuildConfiguration; 203 | buildSettings = { 204 | ALWAYS_SEARCH_USER_PATHS = NO; 205 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 206 | CLANG_CXX_LIBRARY = "libc++"; 207 | CLANG_ENABLE_MODULES = YES; 208 | CLANG_ENABLE_OBJC_ARC = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_CONSTANT_CONVERSION = YES; 211 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 212 | CLANG_WARN_EMPTY_BODY = YES; 213 | CLANG_WARN_ENUM_CONVERSION = YES; 214 | CLANG_WARN_INFINITE_RECURSION = YES; 215 | CLANG_WARN_INT_CONVERSION = YES; 216 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 217 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 218 | CLANG_WARN_UNREACHABLE_CODE = YES; 219 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 220 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 221 | COPY_PHASE_STRIP = NO; 222 | ENABLE_STRICT_OBJC_MSGSEND = YES; 223 | ENABLE_TESTABILITY = YES; 224 | GCC_C_LANGUAGE_STANDARD = gnu99; 225 | GCC_DYNAMIC_NO_PIC = NO; 226 | GCC_NO_COMMON_BLOCKS = YES; 227 | GCC_OPTIMIZATION_LEVEL = 0; 228 | GCC_PREPROCESSOR_DEFINITIONS = ( 229 | "DEBUG=1", 230 | "$(inherited)", 231 | ); 232 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 233 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 234 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 235 | GCC_WARN_UNDECLARED_SELECTOR = YES; 236 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 237 | GCC_WARN_UNUSED_FUNCTION = YES; 238 | GCC_WARN_UNUSED_VARIABLE = YES; 239 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 240 | ONLY_ACTIVE_ARCH = YES; 241 | SDKROOT = iphoneos; 242 | }; 243 | name = Debug; 244 | }; 245 | E8FF75DE18E3506800402A82 /* Release */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | ALWAYS_SEARCH_USER_PATHS = NO; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 250 | CLANG_CXX_LIBRARY = "libc++"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_WARN_BOOL_CONVERSION = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 256 | CLANG_WARN_EMPTY_BODY = YES; 257 | CLANG_WARN_ENUM_CONVERSION = YES; 258 | CLANG_WARN_INFINITE_RECURSION = YES; 259 | CLANG_WARN_INT_CONVERSION = YES; 260 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 261 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 262 | CLANG_WARN_UNREACHABLE_CODE = YES; 263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 264 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 265 | COPY_PHASE_STRIP = YES; 266 | ENABLE_NS_ASSERTIONS = NO; 267 | ENABLE_STRICT_OBJC_MSGSEND = YES; 268 | GCC_C_LANGUAGE_STANDARD = gnu99; 269 | GCC_NO_COMMON_BLOCKS = YES; 270 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 271 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 272 | GCC_WARN_UNDECLARED_SELECTOR = YES; 273 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 274 | GCC_WARN_UNUSED_FUNCTION = YES; 275 | GCC_WARN_UNUSED_VARIABLE = YES; 276 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 277 | SDKROOT = iphoneos; 278 | VALIDATE_PRODUCT = YES; 279 | }; 280 | name = Release; 281 | }; 282 | E8FF75E018E3506800402A82 /* Debug */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 286 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 287 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 288 | GCC_PREFIX_HEADER = "JPSKeyboardLayoutGuideDemo/JPSKeyboardLayoutGuideDemo-Prefix.pch"; 289 | INFOPLIST_FILE = "JPSKeyboardLayoutGuideDemo/JPSKeyboardLayoutGuideDemo-Info.plist"; 290 | PRODUCT_BUNDLE_IDENTIFIER = "com.jpsim.${PRODUCT_NAME:rfc1034identifier}"; 291 | PRODUCT_NAME = "$(TARGET_NAME)"; 292 | WRAPPER_EXTENSION = app; 293 | }; 294 | name = Debug; 295 | }; 296 | E8FF75E118E3506800402A82 /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 300 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 301 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 302 | GCC_PREFIX_HEADER = "JPSKeyboardLayoutGuideDemo/JPSKeyboardLayoutGuideDemo-Prefix.pch"; 303 | INFOPLIST_FILE = "JPSKeyboardLayoutGuideDemo/JPSKeyboardLayoutGuideDemo-Info.plist"; 304 | PRODUCT_BUNDLE_IDENTIFIER = "com.jpsim.${PRODUCT_NAME:rfc1034identifier}"; 305 | PRODUCT_NAME = "$(TARGET_NAME)"; 306 | WRAPPER_EXTENSION = app; 307 | }; 308 | name = Release; 309 | }; 310 | /* End XCBuildConfiguration section */ 311 | 312 | /* Begin XCConfigurationList section */ 313 | E8FF75AE18E3506800402A82 /* Build configuration list for PBXProject "JPSKeyboardLayoutGuideDemo" */ = { 314 | isa = XCConfigurationList; 315 | buildConfigurations = ( 316 | E8FF75DD18E3506800402A82 /* Debug */, 317 | E8FF75DE18E3506800402A82 /* Release */, 318 | ); 319 | defaultConfigurationIsVisible = 0; 320 | defaultConfigurationName = Release; 321 | }; 322 | E8FF75DF18E3506800402A82 /* Build configuration list for PBXNativeTarget "JPSKeyboardLayoutGuideDemo" */ = { 323 | isa = XCConfigurationList; 324 | buildConfigurations = ( 325 | E8FF75E018E3506800402A82 /* Debug */, 326 | E8FF75E118E3506800402A82 /* Release */, 327 | ); 328 | defaultConfigurationIsVisible = 0; 329 | defaultConfigurationName = Release; 330 | }; 331 | /* End XCConfigurationList section */ 332 | }; 333 | rootObject = E8FF75AB18E3506800402A82 /* Project object */; 334 | } 335 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/Images.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" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/JPSAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // JPSAppDelegate.h 3 | // JPSKeyboardLayoutGuideDemo 4 | // 5 | // Created by JP Simard on 2014-03-26. 6 | // Copyright (c) 2014 JP Simard. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JPSAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/JPSAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // JPSAppDelegate.m 3 | // JPSKeyboardLayoutGuideDemo 4 | // 5 | // Created by JP Simard on 2014-03-26. 6 | // Copyright (c) 2014 JP Simard. All rights reserved. 7 | // 8 | 9 | #import "JPSAppDelegate.h" 10 | #import "LoginVC.h" 11 | 12 | @implementation JPSAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 15 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 16 | self.window.rootViewController = [[LoginVC alloc] init]; 17 | [self.window makeKeyAndVisible]; 18 | return YES; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/JPSKeyboardLayoutGuideDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/JPSKeyboardLayoutGuideDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/LoginVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // LoginVC.h 3 | // JPSKeyboardLayoutGuideDemo 4 | // 5 | // Created by JP Simard on 2014-03-26. 6 | // Copyright (c) 2014 JP Simard. All rights reserved. 7 | // 8 | 9 | #import "JPSKeyboardLayoutGuideViewController.h" 10 | 11 | @interface LoginVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/LoginVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // LoginVC.m 3 | // JPSKeyboardLayoutGuideDemo 4 | // 5 | // Created by JP Simard on 2014-03-26. 6 | // Copyright (c) 2014 JP Simard. All rights reserved. 7 | // 8 | 9 | #import "LoginVC.h" 10 | #import "JPSKeyboardLayoutGuideViewController.h" 11 | 12 | @interface LoginVC () 13 | 14 | @property (nonatomic, strong) UITextField *loginField; 15 | 16 | @end 17 | 18 | @implementation LoginVC 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | [self jps_viewDidLoad]; 24 | 25 | self.view.backgroundColor = [UIColor whiteColor]; 26 | [self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)]]; 27 | 28 | [self setupLoginField]; 29 | } 30 | 31 | -(void)viewWillAppear:(BOOL)animated { 32 | [super viewWillAppear:animated]; 33 | [self jps_viewWillAppear:animated]; 34 | } 35 | 36 | -(void)viewWillDisappear:(BOOL)animated { 37 | [super viewWillDisappear:animated]; 38 | [self jps_viewDidDisappear:animated]; 39 | } 40 | 41 | - (void)setupLoginField { 42 | self.loginField = [[UITextField alloc] init]; 43 | self.loginField.translatesAutoresizingMaskIntoConstraints = NO; 44 | [self.view addSubview:self.loginField]; 45 | self.loginField.placeholder = @"username"; 46 | 47 | // Constraints 48 | NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:self.loginField 49 | attribute:NSLayoutAttributeCenterX 50 | relatedBy:NSLayoutRelationEqual 51 | toItem:self.view 52 | attribute:NSLayoutAttributeCenterX 53 | multiplier:1.0f 54 | constant:0.0f]; 55 | NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:self.loginField 56 | attribute:NSLayoutAttributeBottom 57 | relatedBy:NSLayoutRelationEqual 58 | toItem:self.keyboardLayoutGuide 59 | attribute:NSLayoutAttributeTop 60 | multiplier:1.0f 61 | constant:-10.0f]; 62 | [self.view addConstraints:@[centerX, bottom]]; 63 | } 64 | 65 | - (void)dismissKeyboard { 66 | [self.loginField resignFirstResponder]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /JPSKeyboardLayoutGuideDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JPSKeyboardLayoutGuideDemo 4 | // 5 | // Created by JP Simard on 2014-03-26. 6 | // Copyright (c) 2014 JP Simard. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JPSAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([JPSAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 JP Simard - http://jpsim.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JPSKeyboardLayoutGuide 2 | 3 | `JPSKeyboardLayoutGuide` lets you easily make your autolayout view controllers keyboard aware. Think of it as `bottomLayoutGuide`, if it moved along with the keyboard frame. 4 | 5 | This makes it dead simple to vertically center items in a view and have them stay centered when the keyboard appears/disappears. 6 | 7 | ## Installation 8 | 9 | ### From CocoaPods 10 | 11 | Add `pod 'JPSKeyboardLayoutGuide'` to your Podfile. 12 | 13 | ### Manually 14 | 15 | Drag the `JPSKeyboardLayoutGuide` folder into your project. 16 | 17 | ## Usage 18 | 19 | `JPSKeyboardLayoutGuide` is a category of `UIViewController`, so for any controller where you want to adopt the behaviour previously described, you must override and call these methods in it's respective callbacks: 20 | 21 | ```objective-c 22 | - (void)jps_viewDidLoad; 23 | - (void)jps_viewWillAppear:(BOOL)animated; 24 | - (void)jps_viewDidDisappear:(BOOL)animated; 25 | 26 | ``` 27 | 28 | A sample implementation would be like this: 29 | 30 | ```objective-c 31 | #import "JPSKeyboardLayoutGuideViewController.h" 32 | 33 | @interface LoginVC : UIViewController 34 | @end 35 | 36 | @implementation LoginVC 37 | 38 | - (void)viewDidLoad { 39 | [super viewDidLoad]; 40 | [self jps_viewDidLoad]; 41 | [self setupLoginField]; 42 | } 43 | 44 | -(void)viewWillAppear:(BOOL)animated { 45 | [super viewWillAppear:animated]; 46 | [self jps_viewWillAppear:animated]; 47 | } 48 | 49 | -(void)viewWillDisappear:(BOOL)animated { 50 | [super viewWillDisappear:animated]; 51 | [self jps_viewDidDisappear:animated]; 52 | } 53 | 54 | - (void)setupLoginField { 55 | self.loginField = [[UITextField alloc] init]; 56 | self.loginField.translatesAutoresizingMaskIntoConstraints = NO; 57 | [self.view addSubview:self.loginField]; 58 | self.loginField.placeholder = @"username"; 59 | 60 | // Constraints 61 | NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:self.loginField 62 | attribute:NSLayoutAttributeCenterX 63 | relatedBy:NSLayoutRelationEqual 64 | toItem:self.view 65 | attribute:NSLayoutAttributeCenterX 66 | multiplier:1.0f 67 | constant:0.0f]; 68 | NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:self.loginField 69 | attribute:NSLayoutAttributeBottom 70 | relatedBy:NSLayoutRelationEqual 71 | toItem:self.keyboardLayoutGuide 72 | attribute:NSLayoutAttributeTop 73 | multiplier:1.0f 74 | constant:-10.0f]; 75 | [self.view addConstraints:@[centerX, bottom]]; 76 | } 77 | 78 | 79 | @end 80 | ``` 81 | 82 | When importing `JPSKeyboardLayoutGuide`, you'll see `keyboardLayoutGuide` in addition to `topLayoutGuide` and `bottomLayoutGuide`. All these guides behave in the same way. 83 | 84 | For more details on layout guides, refer to Apple's documentation on the [`UILayoutSupport` Protocol](https://developer.apple.com/library/ios/documentation/uikit/reference/UILayoutSupport_Protocol/Reference/Reference.html) 85 | 86 | ## License 87 | 88 | MIT Licensed. 89 | --------------------------------------------------------------------------------