├── .gitignore ├── LICENSE.txt ├── Package.swift ├── README.md ├── TPKeyboardAvoiding.podspec ├── TPKeyboardAvoiding ├── TPKeyboardAvoidingCollectionView.h ├── TPKeyboardAvoidingCollectionView.m ├── TPKeyboardAvoidingScrollView.h ├── TPKeyboardAvoidingScrollView.m ├── TPKeyboardAvoidingTableView.h ├── TPKeyboardAvoidingTableView.m ├── UIScrollView+TPKeyboardAvoidingAdditions.h ├── UIScrollView+TPKeyboardAvoidingAdditions.m └── include │ └── module.modulemap ├── TPKeyboardAvoidingKit ├── Info.plist └── TPKeyboardAvoidingKit.h ├── TPKeyboardAvoidingSample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── TPKeyboardAvoidingKit.xcscheme └── TPKeyboardAvoidingSample ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj ├── LaunchScreen.xib └── Main.storyboard ├── Images.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Collection.imageset │ ├── Collection.png │ ├── Collection@2x.png │ └── Contents.json ├── Contents.json ├── Scroll.imageset │ ├── Contents.json │ ├── Scroll.png │ └── Scroll@2x.png ├── Table.imageset │ ├── Contents.json │ ├── Table.png │ └── Table@2x.png └── Text.imageset │ ├── Contents.json │ ├── Text.png │ └── Text@2x.png ├── Info.plist ├── TPKACollectionViewController.h ├── TPKACollectionViewController.m ├── TPKAScrollViewController.h ├── TPKAScrollViewController.m ├── TPKATableViewController.h ├── TPKATableViewController.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | Icon 6 | ._* 7 | .Spotlight-V100 8 | .Trashes 9 | 10 | # Xcode 11 | # 12 | build/ 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | *.xccheckout 23 | profile 24 | *.moved-aside 25 | DerivedData 26 | *.hmap 27 | *.ipa 28 | *.xcuserstate 29 | 30 | # CocoaPods 31 | Pods 32 | 33 | # Bundler 34 | .bundle 35 | 36 | # Carthage 37 | Carthage/ 38 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Michael Tyson 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "TPKeyboardAvoiding", 6 | products: [ 7 | .library(name: "TPKeyboardAvoiding", targets: ["TPKeyboardAvoiding"]) 8 | ], 9 | targets: [ 10 | .target( 11 | name: "TPKeyboardAvoiding", 12 | path: "TPKeyboardAvoiding" 13 | ) 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TPKeyboardAvoiding 2 | ================== 3 | 4 | A drop-in universal solution for moving text fields out of the way of the keyboard in iOS. 5 | 6 | Introduction 7 | ------------ 8 | 9 | There are a hundred and one proposed solutions out there for how to move `UITextField` and `UITextView` out of the way of the keyboard during editing -- usually, it comes down to observing `UIKeyboardWillShowNotification` and `UIKeyboardWillHideNotification`, or implementing `UITextFieldDelegate` delegate methods, and adjusting the frame of the superview, or using `UITableView`'s `scrollToRowAtIndexPath:atScrollPosition:animated:`, but most proposed solutions tend to be quite DIY, and have to be implemented for each view controller that needs it. 10 | 11 | This is a relatively universal, drop-in solution: `UIScrollView` and `UITableView` subclasses that handle everything. 12 | 13 | When the keyboard is about to appear, the subclass will find the subview that's about to be edited, and adjust its frame and content offset to make sure that view is visible, with an animation to match the keyboard pop-up. When the keyboard disappears, it restores its prior size. 14 | 15 | It should work with basically any setup, either a UITableView-based interface, or one consisting of views placed manually. 16 | 17 | It also automatically hooks up "Next" buttons on the keyboard to switch through the text fields. 18 | 19 | Usage 20 | ----- 21 | 22 | For use with `UITableViewController` classes, drop `TPKeyboardAvoidingTableView.m` and `TPKeyboardAvoidingTableView.h` into your project, and make your UITableView a `TPKeyboardAvoidingTableView` in the xib. If you're not using a xib with your controller, I know of no easy way to make its UITableView a custom class: The path of least resistance is to create a xib for it. 23 | 24 | For non-UITableViewControllers, drop the `TPKeyboardAvoidingScrollView.m` and `TPKeyboardAvoidingScrollView.h` source files into your project, pop a `UIScrollView` into your view controller's xib, set the scroll view's class to `TPKeyboardAvoidingScrollView`, and put all your controls within that scroll view. You can also create it programmatically, without using a xib - just use the TPKeyboardAvoidingScrollView as your top-level view. 25 | 26 | To disable the automatic "Next" button functionality, change the UITextField's return key type to anything but UIReturnKeyDefault. 27 | 28 | Notes 29 | ----- 30 | 31 | These classes currently adjust the contentInset parameter to avoid content moving beneath the keyboard. This is done, as opposed to adjusting the frame, in order to work around an iOS bug that results in a jerky animation where the view jumps upwards, before settling down. In order to facilitate this workaround, the contentSize is maintained to be at least same size as the view's frame. 32 | 33 | Licence (Zlib) 34 | -------------- 35 | 36 | Copyright (c) 2013 Michael Tyson 37 | 38 | This software is provided 'as-is', without any express or implied 39 | warranty. In no event will the authors be held liable for any damages 40 | arising from the use of this software. 41 | 42 | Permission is granted to anyone to use this software for any purpose, 43 | including commercial applications, and to alter it and redistribute it 44 | freely, subject to the following restrictions: 45 | 46 | 1. The origin of this software must not be misrepresented; you must not 47 | claim that you wrote the original software. If you use this software 48 | in a product, an acknowledgment in the product documentation would be 49 | appreciated but is not required. 50 | 51 | 2. Altered source versions must be plainly marked as such, and must not be 52 | misrepresented as being the original software. 53 | 54 | 3. This notice may not be removed or altered from any source 55 | distribution. 56 | 57 | 58 | ------------ 59 | 60 | Michael Tyson, A Tasty Pixel 61 | michael@atastypixel.com 62 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'TPKeyboardAvoiding' 3 | s.version = '1.3.5' 4 | s.license = 'zlib' 5 | s.platform = :ios, '9.0' 6 | s.summary = 'A drop-in universal solution for moving text fields out of the way of the keyboard in iOS.' 7 | s.homepage = 'https://github.com/michaeltyson/TPKeyboardAvoiding' 8 | s.author = { 'Michael Tyson' => 'michael@atastypixel.com' } 9 | s.source = { :git => 'https://github.com/michaeltyson/TPKeyboardAvoiding.git', :tag => '1.3.5' } 10 | 11 | s.description = "This is a relatively universal, drop-in solution: UIScrollView and UITableView subclasses that handle everything." \ 12 | "When the keyboard is about to appear, the subclass will find the subview that's about to be edited, " \ 13 | "and adjust its frame and content offset to make sure that view is visible, with an animation to match the keyboard pop-up. "\ 14 | "When the keyboard disappears, it restores its prior size." 15 | 16 | s.source_files = 'TPKeyboardAvoiding/*.{h,m}' 17 | s.requires_arc = false 18 | end 19 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TPKeyboardAvoidingCollectionView.h 3 | // TPKeyboardAvoiding 4 | // 5 | // Created by Michael Tyson on 30/09/2013. 6 | // Copyright 2015 A Tasty Pixel & The CocoaBots. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #if ! TARGET_OS_TV 12 | 13 | #import "UIScrollView+TPKeyboardAvoidingAdditions.h" 14 | 15 | @interface TPKeyboardAvoidingCollectionView : UICollectionView 16 | - (BOOL)focusNextTextField; 17 | - (void)scrollToActiveTextField; 18 | @end 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TPKeyboardAvoidingCollectionView.m 3 | // TPKeyboardAvoiding 4 | // 5 | // Created by Michael Tyson on 30/09/2013. 6 | // Copyright 2015 A Tasty Pixel & The CocoaBots. All rights reserved. 7 | // 8 | 9 | #import "TPKeyboardAvoidingCollectionView.h" 10 | 11 | #if ! TARGET_OS_TV 12 | 13 | @interface TPKeyboardAvoidingCollectionView () 14 | @end 15 | 16 | @implementation TPKeyboardAvoidingCollectionView 17 | 18 | #pragma mark - Setup/Teardown 19 | 20 | - (void)setupKeyboardAvoiding { 21 | if ( [self hasAutomaticKeyboardAvoidingBehaviour] ) return; 22 | 23 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil]; 24 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; 25 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToActiveTextField) name:UITextViewTextDidBeginEditingNotification object:nil]; 26 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToActiveTextField) name:UITextFieldTextDidBeginEditingNotification object:nil]; 27 | } 28 | 29 | -(id)initWithFrame:(CGRect)frame { 30 | if ( !(self = [super initWithFrame:frame]) ) return nil; 31 | [self setupKeyboardAvoiding]; 32 | return self; 33 | } 34 | 35 | - (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { 36 | if ( !(self = [super initWithFrame:frame collectionViewLayout:layout]) ) return nil; 37 | [self setupKeyboardAvoiding]; 38 | return self; 39 | } 40 | 41 | -(void)awakeFromNib { 42 | [super awakeFromNib]; 43 | [self setupKeyboardAvoiding]; 44 | } 45 | 46 | -(void)dealloc { 47 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 48 | #if !__has_feature(objc_arc) 49 | [super dealloc]; 50 | #endif 51 | } 52 | 53 | 54 | -(BOOL)hasAutomaticKeyboardAvoidingBehaviour { 55 | if ( [[[UIDevice currentDevice] systemVersion] integerValue] >= 9 56 | && [self.delegate isKindOfClass:[UICollectionViewController class]] ) { 57 | // Theory: It looks like iOS 9's collection views automatically avoid the keyboard. As usual 58 | // Apple have totally failed to document this anywhere, so this is just a guess. 59 | return YES; 60 | } 61 | 62 | return NO; 63 | } 64 | 65 | -(void)setFrame:(CGRect)frame { 66 | [super setFrame:frame]; 67 | [self TPKeyboardAvoiding_updateContentInset]; 68 | } 69 | 70 | -(void)setContentSize:(CGSize)contentSize { 71 | if (CGSizeEqualToSize(contentSize, self.contentSize)) { 72 | // Prevent triggering contentSize when it's already the same that 73 | // cause weird infinte scrolling and locking bug 74 | return; 75 | } 76 | [super setContentSize:contentSize]; 77 | [self TPKeyboardAvoiding_updateContentInset]; 78 | } 79 | 80 | - (BOOL)focusNextTextField { 81 | return [self TPKeyboardAvoiding_focusNextTextField]; 82 | 83 | } 84 | - (void)scrollToActiveTextField { 85 | return [self TPKeyboardAvoiding_scrollToActiveTextField]; 86 | } 87 | 88 | #pragma mark - Responders, events 89 | 90 | -(void)willMoveToSuperview:(UIView *)newSuperview { 91 | [super willMoveToSuperview:newSuperview]; 92 | if ( !newSuperview ) { 93 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; 94 | } 95 | } 96 | 97 | - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 98 | [[self TPKeyboardAvoiding_findFirstResponderBeneathView:self] resignFirstResponder]; 99 | [super touchesEnded:touches withEvent:event]; 100 | } 101 | 102 | -(BOOL)textFieldShouldReturn:(UITextField *)textField { 103 | if ( ![self focusNextTextField] ) { 104 | [textField resignFirstResponder]; 105 | } 106 | return YES; 107 | } 108 | 109 | -(void)layoutSubviews { 110 | [super layoutSubviews]; 111 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; 112 | [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; 113 | } 114 | 115 | @end 116 | 117 | #endif 118 | 119 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TPKeyboardAvoidingScrollView.h 3 | // TPKeyboardAvoiding 4 | // 5 | // Created by Michael Tyson on 30/09/2013. 6 | // Copyright 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIScrollView+TPKeyboardAvoidingAdditions.h" 11 | 12 | @interface TPKeyboardAvoidingScrollView : UIScrollView 13 | - (void)contentSizeToFit; 14 | - (BOOL)focusNextTextField; 15 | - (void)scrollToActiveTextField; 16 | @end 17 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TPKeyboardAvoidingScrollView.m 3 | // TPKeyboardAvoiding 4 | // 5 | // Created by Michael Tyson on 30/09/2013. 6 | // Copyright 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import "TPKeyboardAvoidingScrollView.h" 10 | 11 | #if ! TARGET_OS_TV 12 | 13 | @interface TPKeyboardAvoidingScrollView () 14 | @end 15 | 16 | @implementation TPKeyboardAvoidingScrollView 17 | 18 | #pragma mark - Setup/Teardown 19 | 20 | - (void)setupKeyboardAvoiding { 21 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil]; 22 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; 23 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToActiveTextField) name:UITextViewTextDidBeginEditingNotification object:nil]; 24 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToActiveTextField) name:UITextFieldTextDidBeginEditingNotification object:nil]; 25 | } 26 | 27 | -(id)initWithFrame:(CGRect)frame { 28 | if ( !(self = [super initWithFrame:frame]) ) return nil; 29 | [self setupKeyboardAvoiding]; 30 | return self; 31 | } 32 | 33 | -(void)awakeFromNib { 34 | [super awakeFromNib]; 35 | [self setupKeyboardAvoiding]; 36 | } 37 | 38 | -(void)dealloc { 39 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 40 | #if !__has_feature(objc_arc) 41 | [super dealloc]; 42 | #endif 43 | } 44 | 45 | -(void)setFrame:(CGRect)frame { 46 | [super setFrame:frame]; 47 | [self TPKeyboardAvoiding_updateContentInset]; 48 | } 49 | 50 | -(void)setContentSize:(CGSize)contentSize { 51 | [super setContentSize:contentSize]; 52 | [self TPKeyboardAvoiding_updateFromContentSizeChange]; 53 | } 54 | 55 | - (void)contentSizeToFit { 56 | self.contentSize = [self TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames]; 57 | } 58 | 59 | - (BOOL)focusNextTextField { 60 | return [self TPKeyboardAvoiding_focusNextTextField]; 61 | 62 | } 63 | - (void)scrollToActiveTextField { 64 | return [self TPKeyboardAvoiding_scrollToActiveTextField]; 65 | } 66 | 67 | #pragma mark - Responders, events 68 | 69 | -(void)willMoveToSuperview:(UIView *)newSuperview { 70 | [super willMoveToSuperview:newSuperview]; 71 | if ( !newSuperview ) { 72 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; 73 | } 74 | } 75 | 76 | - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 77 | [[self TPKeyboardAvoiding_findFirstResponderBeneathView:self] resignFirstResponder]; 78 | [super touchesEnded:touches withEvent:event]; 79 | } 80 | 81 | -(BOOL)textFieldShouldReturn:(UITextField *)textField { 82 | if ( ![self focusNextTextField] ) { 83 | [textField resignFirstResponder]; 84 | } 85 | return YES; 86 | } 87 | 88 | -(void)layoutSubviews { 89 | [super layoutSubviews]; 90 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; 91 | [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; 92 | } 93 | 94 | @end 95 | 96 | #endif 97 | 98 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/TPKeyboardAvoidingTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TPKeyboardAvoidingTableView.h 3 | // TPKeyboardAvoiding 4 | // 5 | // Created by Michael Tyson on 30/09/2013. 6 | // Copyright 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIScrollView+TPKeyboardAvoidingAdditions.h" 11 | 12 | @interface TPKeyboardAvoidingTableView : UITableView 13 | - (BOOL)focusNextTextField; 14 | - (void)scrollToActiveTextField; 15 | @end 16 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TPKeyboardAvoidingTableView.m 3 | // TPKeyboardAvoiding 4 | // 5 | // Created by Michael Tyson on 30/09/2013. 6 | // Copyright 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import "TPKeyboardAvoidingTableView.h" 10 | 11 | #if ! TARGET_OS_TV 12 | 13 | @interface TPKeyboardAvoidingTableView () 14 | @end 15 | 16 | @implementation TPKeyboardAvoidingTableView 17 | 18 | #pragma mark - Setup/Teardown 19 | 20 | - (void)setupKeyboardAvoiding { 21 | if ( [self hasAutomaticKeyboardAvoidingBehaviour] ) return; 22 | 23 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil]; 24 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; 25 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToActiveTextField) name:UITextViewTextDidBeginEditingNotification object:nil]; 26 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToActiveTextField) name:UITextFieldTextDidBeginEditingNotification object:nil]; 27 | } 28 | 29 | -(id)initWithFrame:(CGRect)frame { 30 | if ( !(self = [super initWithFrame:frame]) ) return nil; 31 | [self setupKeyboardAvoiding]; 32 | return self; 33 | } 34 | 35 | -(id)initWithFrame:(CGRect)frame style:(UITableViewStyle)withStyle { 36 | if ( !(self = [super initWithFrame:frame style:withStyle]) ) return nil; 37 | [self setupKeyboardAvoiding]; 38 | return self; 39 | } 40 | 41 | -(void)awakeFromNib { 42 | [super awakeFromNib]; 43 | [self setupKeyboardAvoiding]; 44 | } 45 | 46 | -(void)dealloc { 47 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 48 | #if !__has_feature(objc_arc) 49 | [super dealloc]; 50 | #endif 51 | } 52 | 53 | -(BOOL)hasAutomaticKeyboardAvoidingBehaviour { 54 | if ( [self.delegate isKindOfClass:[UITableViewController class]] ) { 55 | // Theory: Apps built using the iOS 8.3 SDK (probably: older SDKs not tested) seem to handle keyboard 56 | // avoiding automatically with UITableViewController. This doesn't seem to be documented anywhere 57 | // by Apple, so results obtained only empirically. 58 | return YES; 59 | } 60 | 61 | return NO; 62 | } 63 | 64 | -(void)setFrame:(CGRect)frame { 65 | [super setFrame:frame]; 66 | if ( [self hasAutomaticKeyboardAvoidingBehaviour] ) return; 67 | [self TPKeyboardAvoiding_updateContentInset]; 68 | } 69 | 70 | -(void)setContentSize:(CGSize)contentSize { 71 | if ( [self hasAutomaticKeyboardAvoidingBehaviour] ) { 72 | [super setContentSize:contentSize]; 73 | return; 74 | } 75 | if (CGSizeEqualToSize(contentSize, self.contentSize)) { 76 | // Prevent triggering contentSize when it's already the same 77 | // this cause table view to scroll to top on contentInset changes 78 | return; 79 | } 80 | [super setContentSize:contentSize]; 81 | [self TPKeyboardAvoiding_updateContentInset]; 82 | } 83 | 84 | - (BOOL)focusNextTextField { 85 | return [self TPKeyboardAvoiding_focusNextTextField]; 86 | 87 | } 88 | - (void)scrollToActiveTextField { 89 | return [self TPKeyboardAvoiding_scrollToActiveTextField]; 90 | } 91 | 92 | #pragma mark - Responders, events 93 | 94 | -(void)willMoveToSuperview:(UIView *)newSuperview { 95 | [super willMoveToSuperview:newSuperview]; 96 | if ( !newSuperview ) { 97 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; 98 | } 99 | } 100 | 101 | - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 102 | [[self TPKeyboardAvoiding_findFirstResponderBeneathView:self] resignFirstResponder]; 103 | [super touchesEnded:touches withEvent:event]; 104 | } 105 | 106 | -(BOOL)textFieldShouldReturn:(UITextField *)textField { 107 | if ( ![self focusNextTextField] ) { 108 | [textField resignFirstResponder]; 109 | } 110 | return YES; 111 | } 112 | 113 | -(void)layoutSubviews { 114 | [super layoutSubviews]; 115 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; 116 | [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; 117 | } 118 | 119 | @end 120 | 121 | #endif 122 | 123 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+TPKeyboardAvoidingAdditions.h 3 | // TPKeyboardAvoiding 4 | // 5 | // Created by Michael Tyson on 30/09/2013. 6 | // Copyright 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol TPKeyboardAvoidingAdditionsOptions 12 | - (BOOL)TPKeyboardAvoiding_idealOffsetForViewAlwaysTop; 13 | @end 14 | 15 | @interface UIScrollView (TPKeyboardAvoidingAdditions) 16 | - (BOOL)TPKeyboardAvoiding_focusNextTextField; 17 | - (void)TPKeyboardAvoiding_scrollToActiveTextField; 18 | 19 | - (void)TPKeyboardAvoiding_keyboardWillShow:(NSNotification*)notification; 20 | - (void)TPKeyboardAvoiding_keyboardWillHide:(NSNotification*)notification; 21 | - (void)TPKeyboardAvoiding_updateContentInset; 22 | - (void)TPKeyboardAvoiding_updateFromContentSizeChange; 23 | - (void)TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:(UIView*)view; 24 | - (UIView*)TPKeyboardAvoiding_findFirstResponderBeneathView:(UIView*)view; 25 | -(CGSize)TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames; 26 | @end 27 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+TPKeyboardAvoidingAdditions.m 3 | // TPKeyboardAvoiding 4 | // 5 | // Created by Michael Tyson on 30/09/2013. 6 | // Copyright 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import "UIScrollView+TPKeyboardAvoidingAdditions.h" 10 | 11 | #if ! TARGET_OS_TV 12 | 13 | #import "TPKeyboardAvoidingScrollView.h" 14 | #import 15 | 16 | static const CGFloat kCalculatedContentPadding = 10; 17 | static const CGFloat kMinimumScrollOffsetPadding = 20; 18 | 19 | static NSString * const kUIKeyboardAnimationDurationUserInfoKey = @"UIKeyboardAnimationDurationUserInfoKey"; 20 | 21 | static const int kStateKey; 22 | 23 | #define _UIKeyboardFrameEndUserInfoKey (&UIKeyboardFrameEndUserInfoKey != NULL ? UIKeyboardFrameEndUserInfoKey : @"UIKeyboardBoundsUserInfoKey") 24 | #define _UIKeyboardFrameBeginUserInfoKey (&UIKeyboardFrameBeginUserInfoKey != NULL ? UIKeyboardFrameBeginUserInfoKey : @"UIKeyboardBoundsUserInfoKey") 25 | 26 | @interface TPKeyboardAvoidingState : NSObject 27 | @property (nonatomic, assign) UIEdgeInsets priorInset; 28 | @property (nonatomic, assign) UIEdgeInsets priorScrollIndicatorInsets; 29 | @property (nonatomic, assign) BOOL keyboardVisible; 30 | @property (nonatomic, assign) CGRect keyboardRect; 31 | @property (nonatomic, assign) CGSize priorContentSize; 32 | @property (nonatomic, assign) BOOL priorPagingEnabled; 33 | @property (nonatomic, assign) BOOL ignoringNotifications; 34 | @property (nonatomic, assign) BOOL keyboardAnimationInProgress; 35 | @property (nonatomic, assign) CGFloat animationDuration; 36 | @end 37 | 38 | @implementation UIScrollView (TPKeyboardAvoidingAdditions) 39 | 40 | - (TPKeyboardAvoidingState*)keyboardAvoidingState { 41 | TPKeyboardAvoidingState *state = objc_getAssociatedObject(self, &kStateKey); 42 | if ( !state ) { 43 | state = [[TPKeyboardAvoidingState alloc] init]; 44 | objc_setAssociatedObject(self, &kStateKey, state, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 45 | #if !__has_feature(objc_arc) 46 | [state release]; 47 | #endif 48 | } 49 | return state; 50 | } 51 | 52 | - (void)TPKeyboardAvoiding_keyboardWillShow:(NSNotification*)notification { 53 | NSDictionary *info = [notification userInfo]; 54 | TPKeyboardAvoidingState *state = self.keyboardAvoidingState; 55 | 56 | state.animationDuration = [[info objectForKey:kUIKeyboardAnimationDurationUserInfoKey] doubleValue]; 57 | 58 | CGRect beginKeyboardRect = [self convertRect:[[info objectForKey:_UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:nil]; 59 | CGRect endKeyboardRect = [self convertRect:[[info objectForKey:_UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil]; 60 | if (CGRectIsEmpty(endKeyboardRect)) { 61 | self.keyboardAvoidingState.keyboardVisible = NO; 62 | return; 63 | } 64 | 65 | if ( CGRectEqualToRect(beginKeyboardRect, endKeyboardRect) && state.ignoringNotifications ) { 66 | return; 67 | } 68 | 69 | state.keyboardRect = endKeyboardRect; 70 | 71 | if ( !state.keyboardVisible ) { 72 | state.priorInset = self.contentInset; 73 | state.priorScrollIndicatorInsets = self.scrollIndicatorInsets; 74 | #if TARGET_OS_IOS 75 | state.priorPagingEnabled = self.pagingEnabled; 76 | #endif 77 | } 78 | 79 | state.keyboardVisible = YES; 80 | #if TARGET_OS_IOS 81 | self.pagingEnabled = NO; 82 | #endif 83 | 84 | if ( [self isKindOfClass:[TPKeyboardAvoidingScrollView class]] ) { 85 | state.priorContentSize = self.contentSize; 86 | 87 | if ( CGSizeEqualToSize(self.contentSize, CGSizeZero) ) { 88 | // Set the content size, if it's not set. Do not set content size explicitly if auto-layout 89 | // is being used to manage subviews 90 | self.contentSize = [self TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames]; 91 | } 92 | } 93 | 94 | // Delay until a future run loop such that the cursor position is available in a text view 95 | // In other words, it's not available (specifically, the prior cursor position is returned) when the first keyboard position change notification fires 96 | // NOTE: Unfortunately, using dispatch_async(main_queue) did not result in a sufficient-enough delay 97 | // for the text view's current cursor position to be available 98 | dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)); 99 | dispatch_after(delay, dispatch_get_main_queue(), ^{ 100 | 101 | // Shrink view's inset by the keyboard's height, and scroll to show the text field/view being edited 102 | [UIView beginAnimations:nil context:NULL]; 103 | 104 | [UIView setAnimationDelegate:self]; 105 | [UIView setAnimationWillStartSelector:@selector(keyboardViewAppear:context:)]; 106 | [UIView setAnimationDidStopSelector:@selector(keyboardViewDisappear:finished:context:)]; 107 | 108 | [UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; 109 | [UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]]; 110 | [UIView setAnimationBeginsFromCurrentState:YES]; 111 | 112 | self.contentInset = [self TPKeyboardAvoiding_contentInsetForKeyboard]; 113 | 114 | UIView *firstResponder = [self TPKeyboardAvoiding_findFirstResponderBeneathView:self]; 115 | if ( firstResponder ) { 116 | CGFloat viewableHeight = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom; 117 | [self setContentOffset:CGPointMake(self.contentOffset.x, 118 | [self TPKeyboardAvoiding_idealOffsetForView:firstResponder 119 | withViewingAreaHeight:viewableHeight]) 120 | animated:NO]; 121 | } 122 | 123 | self.scrollIndicatorInsets = self.contentInset; 124 | [self layoutIfNeeded]; 125 | 126 | [UIView commitAnimations]; 127 | }); 128 | } 129 | 130 | - (void)keyboardViewAppear:(NSString *)animationID context:(void *)context { 131 | self.keyboardAvoidingState.keyboardAnimationInProgress = true; 132 | } 133 | 134 | - (void)keyboardViewDisappear:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { 135 | if (finished.boolValue) { 136 | self.keyboardAvoidingState.keyboardAnimationInProgress = false; 137 | } 138 | } 139 | 140 | - (void)TPKeyboardAvoiding_keyboardWillHide:(NSNotification*)notification { 141 | CGRect beginKeyboardRect = [self convertRect:[[[notification userInfo] objectForKey:_UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:nil]; 142 | CGRect endKeyboardRect = [self convertRect:[[[notification userInfo] objectForKey:_UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil]; 143 | if (CGRectIsEmpty(beginKeyboardRect) && !self.keyboardAvoidingState.keyboardAnimationInProgress) { 144 | return; 145 | } 146 | 147 | TPKeyboardAvoidingState *state = self.keyboardAvoidingState; 148 | 149 | if ( CGRectEqualToRect(beginKeyboardRect, endKeyboardRect) && state.ignoringNotifications ) { 150 | return; 151 | } 152 | 153 | if ( !state.keyboardVisible ) { 154 | return; 155 | } 156 | 157 | state.keyboardRect = CGRectZero; 158 | state.keyboardVisible = NO; 159 | 160 | // Restore dimensions to prior size 161 | [UIView beginAnimations:nil context:NULL]; 162 | [UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; 163 | [UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]]; 164 | [UIView setAnimationBeginsFromCurrentState:YES]; 165 | 166 | if ( [self isKindOfClass:[TPKeyboardAvoidingScrollView class]] ) { 167 | self.contentSize = state.priorContentSize; 168 | } 169 | 170 | self.contentInset = state.priorInset; 171 | self.scrollIndicatorInsets = state.priorScrollIndicatorInsets; 172 | #if TARGET_OS_IOS 173 | self.pagingEnabled = state.priorPagingEnabled; 174 | #endif 175 | [self layoutIfNeeded]; 176 | [UIView commitAnimations]; 177 | } 178 | 179 | - (void)TPKeyboardAvoiding_updateContentInset { 180 | TPKeyboardAvoidingState *state = self.keyboardAvoidingState; 181 | if ( state.keyboardVisible ) { 182 | self.contentInset = [self TPKeyboardAvoiding_contentInsetForKeyboard]; 183 | } 184 | } 185 | 186 | - (void)TPKeyboardAvoiding_updateFromContentSizeChange { 187 | TPKeyboardAvoidingState *state = self.keyboardAvoidingState; 188 | if ( state.keyboardVisible ) { 189 | state.priorContentSize = self.contentSize; 190 | self.contentInset = [self TPKeyboardAvoiding_contentInsetForKeyboard]; 191 | } 192 | } 193 | 194 | #pragma mark - Utilities 195 | 196 | - (BOOL)TPKeyboardAvoiding_focusNextTextField { 197 | UIView *firstResponder = [self TPKeyboardAvoiding_findFirstResponderBeneathView:self]; 198 | if ( !firstResponder ) { 199 | return NO; 200 | } 201 | 202 | UIView *view = [self TPKeyboardAvoiding_findNextInputViewAfterView:firstResponder beneathView:self]; 203 | 204 | if ( view ) { 205 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{ 206 | TPKeyboardAvoidingState *state = self.keyboardAvoidingState; 207 | state.ignoringNotifications = YES; 208 | [view becomeFirstResponder]; 209 | state.ignoringNotifications = NO; 210 | }); 211 | return YES; 212 | } 213 | 214 | return NO; 215 | } 216 | 217 | -(void)TPKeyboardAvoiding_scrollToActiveTextField { 218 | TPKeyboardAvoidingState *state = self.keyboardAvoidingState; 219 | 220 | if ( !state.keyboardVisible ) return; 221 | 222 | UIView *firstResponder = [self TPKeyboardAvoiding_findFirstResponderBeneathView:self]; 223 | if ( !firstResponder ) { 224 | return; 225 | } 226 | // Ignore any keyboard notification that occur while we scroll 227 | // (seems to be an iOS 9 bug that causes jumping text in UITextField) 228 | state.ignoringNotifications = YES; 229 | 230 | CGFloat visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom; 231 | 232 | CGPoint idealOffset 233 | = CGPointMake(self.contentOffset.x, 234 | [self TPKeyboardAvoiding_idealOffsetForView:firstResponder 235 | withViewingAreaHeight:visibleSpace]); 236 | 237 | // Ordinarily we'd use -setContentOffset:animated:YES here, but it interferes with UIScrollView 238 | // behavior which automatically ensures that the first responder is within its bounds 239 | [UIView animateWithDuration:state.animationDuration animations:^{ 240 | self.contentOffset = idealOffset; 241 | } completion:^(BOOL finished) { 242 | state.ignoringNotifications = NO; 243 | }]; 244 | } 245 | 246 | #pragma mark - Helpers 247 | 248 | - (UIView*)TPKeyboardAvoiding_findFirstResponderBeneathView:(UIView*)view { 249 | // Search recursively for first responder 250 | for ( UIView *childView in view.subviews ) { 251 | if ( [childView respondsToSelector:@selector(isFirstResponder)] && [childView isFirstResponder] ) return childView; 252 | UIView *result = [self TPKeyboardAvoiding_findFirstResponderBeneathView:childView]; 253 | if ( result ) return result; 254 | } 255 | return nil; 256 | } 257 | 258 | - (UIView*)TPKeyboardAvoiding_findNextInputViewAfterView:(UIView*)priorView beneathView:(UIView*)view { 259 | UIView * candidate = nil; 260 | [self TPKeyboardAvoiding_findNextInputViewAfterView:priorView beneathView:view bestCandidate:&candidate]; 261 | return candidate; 262 | } 263 | 264 | - (void)TPKeyboardAvoiding_findNextInputViewAfterView:(UIView*)priorView beneathView:(UIView*)view bestCandidate:(UIView**)bestCandidate { 265 | // Search recursively for input view below/to right of priorTextField 266 | CGRect priorFrame = [self convertRect:priorView.frame fromView:priorView.superview]; 267 | CGRect candidateFrame = *bestCandidate ? [self convertRect:(*bestCandidate).frame fromView:(*bestCandidate).superview] : CGRectZero; 268 | CGFloat bestCandidateHeuristic = [self TPKeyboardAvoiding_nextInputViewHeuristicForViewFrame:candidateFrame]; 269 | 270 | for ( UIView *childView in view.subviews ) { 271 | if ( [self TPKeyboardAvoiding_viewIsValidKeyViewCandidate:childView] ) { 272 | CGRect frame = [self convertRect:childView.frame fromView:view]; 273 | 274 | // Use a heuristic to evaluate candidates 275 | CGFloat heuristic = [self TPKeyboardAvoiding_nextInputViewHeuristicForViewFrame:frame]; 276 | 277 | // Find views beneath, or to the right. For those views that match, choose the view closest to the top left 278 | if ( childView != priorView 279 | && ((fabs(CGRectGetMinY(frame) - CGRectGetMinY(priorFrame)) < FLT_EPSILON && CGRectGetMinX(frame) > CGRectGetMinX(priorFrame)) 280 | || CGRectGetMinY(frame) > CGRectGetMinY(priorFrame)) 281 | && (!*bestCandidate || heuristic > bestCandidateHeuristic) ) { 282 | 283 | *bestCandidate = childView; 284 | bestCandidateHeuristic = heuristic; 285 | } 286 | } else { 287 | [self TPKeyboardAvoiding_findNextInputViewAfterView:priorView beneathView:childView bestCandidate:bestCandidate]; 288 | } 289 | } 290 | } 291 | 292 | - (CGFloat)TPKeyboardAvoiding_nextInputViewHeuristicForViewFrame:(CGRect)frame { 293 | return (-frame.origin.y * 1000.0) // Prefer elements closest to top (most important) 294 | + (-frame.origin.x); // Prefer elements closest to left 295 | } 296 | 297 | - (BOOL)TPKeyboardAvoiding_viewHiddenOrUserInteractionNotEnabled:(UIView *)view { 298 | while ( view ) { 299 | if ( view.hidden || !view.userInteractionEnabled ) { 300 | return YES; 301 | } 302 | view = view.superview; 303 | } 304 | return NO; 305 | } 306 | 307 | - (BOOL)TPKeyboardAvoiding_viewIsValidKeyViewCandidate:(UIView *)view { 308 | if ( [self TPKeyboardAvoiding_viewHiddenOrUserInteractionNotEnabled:view] ) return NO; 309 | 310 | if ( [view isKindOfClass:[UITextField class]] && ((UITextField*)view).enabled ) { 311 | return YES; 312 | } 313 | #if TARGET_OS_IOS 314 | if ( [view isKindOfClass:[UITextView class]] && ((UITextView*)view).isEditable ) { 315 | return YES; 316 | } 317 | #endif 318 | return NO; 319 | } 320 | 321 | - (void)TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:(UIView*)view { 322 | for ( UIView *childView in view.subviews ) { 323 | if ( ([childView isKindOfClass:[UITextField class]] || [childView isKindOfClass:[UITextView class]]) ) { 324 | [self TPKeyboardAvoiding_initializeView:childView]; 325 | } else { 326 | [self TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:childView]; 327 | } 328 | } 329 | } 330 | 331 | -(CGSize)TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames { 332 | 333 | BOOL wasShowingVerticalScrollIndicator = self.showsVerticalScrollIndicator; 334 | BOOL wasShowingHorizontalScrollIndicator = self.showsHorizontalScrollIndicator; 335 | 336 | self.showsVerticalScrollIndicator = NO; 337 | self.showsHorizontalScrollIndicator = NO; 338 | 339 | CGRect rect = CGRectZero; 340 | for ( UIView *view in self.subviews ) { 341 | rect = CGRectUnion(rect, view.frame); 342 | } 343 | rect.size.height += kCalculatedContentPadding; 344 | 345 | self.showsVerticalScrollIndicator = wasShowingVerticalScrollIndicator; 346 | self.showsHorizontalScrollIndicator = wasShowingHorizontalScrollIndicator; 347 | 348 | return rect.size; 349 | } 350 | 351 | 352 | - (UIEdgeInsets)TPKeyboardAvoiding_contentInsetForKeyboard { 353 | TPKeyboardAvoidingState *state = self.keyboardAvoidingState; 354 | UIEdgeInsets newInset = self.contentInset; 355 | CGRect keyboardRect = state.keyboardRect; 356 | 357 | if (keyboardRect.size.height == 0) { 358 | newInset.bottom = state.priorInset.bottom; 359 | } else { 360 | newInset.bottom = MAX(keyboardRect.size.height - MAX((CGRectGetMaxY(keyboardRect) - CGRectGetMaxY(self.bounds)), 0), 0); 361 | } 362 | 363 | return newInset; 364 | } 365 | 366 | -(CGFloat)TPKeyboardAvoiding_idealOffsetForView:(UIView *)view withViewingAreaHeight:(CGFloat)viewAreaHeight { 367 | CGSize contentSize = self.contentSize; 368 | __block CGFloat offset = 0.0; 369 | 370 | CGRect subviewRect = [view convertRect:view.bounds toView:self]; 371 | 372 | // Allow views to specify their need to float to the top instead of towards the middle. 373 | if ( [view respondsToSelector:@selector(TPKeyboardAvoiding_idealOffsetForViewAlwaysTop)] && [((id)view) TPKeyboardAvoiding_idealOffsetForViewAlwaysTop] ) { 374 | return subviewRect.origin.y; 375 | } 376 | 377 | __block CGFloat padding = 0.0; 378 | __block UIEdgeInsets contentInset; 379 | 380 | #ifdef __IPHONE_11_0 381 | if (@available(iOS 11.0, *)) { 382 | contentInset = self.adjustedContentInset; 383 | } else { 384 | contentInset = self.contentInset; 385 | } 386 | #else 387 | contentInset = self.contentInset; 388 | #endif 389 | 390 | void(^centerViewInViewableArea)(void) = ^ { 391 | // Attempt to center the subview in the visible space 392 | padding = (viewAreaHeight - subviewRect.size.height) / 2; 393 | 394 | // But if that means there will be less than kMinimumScrollOffsetPadding 395 | // pixels above the view, then substitute kMinimumScrollOffsetPadding 396 | if (padding < kMinimumScrollOffsetPadding ) { 397 | padding = kMinimumScrollOffsetPadding; 398 | } 399 | 400 | // Ideal offset places the subview rectangle origin "padding" points from the top of the scrollview. 401 | // If there is a top contentInset, also compensate for this so that subviewRect will not be placed under 402 | // things like navigation bars. 403 | offset = subviewRect.origin.y - padding - contentInset.top; 404 | }; 405 | 406 | // If possible, center the caret in the visible space. Otherwise, center the entire view in the visible space. 407 | if ([view conformsToProtocol:@protocol(UITextInput)]) { 408 | UIView *textInput = (UIView *)view; 409 | UITextPosition *caretPosition = [textInput selectedTextRange].start; 410 | if (caretPosition) { 411 | CGRect caretRect = [self convertRect:[textInput caretRectForPosition:caretPosition] fromView:textInput]; 412 | 413 | // Attempt to center the cursor in the visible space 414 | // pixels above the view, then substitute kMinimumScrollOffsetPadding 415 | padding = (viewAreaHeight - caretRect.size.height) / 2; 416 | 417 | // But if that means there will be less than kMinimumScrollOffsetPadding 418 | // pixels above the view, then substitute kMinimumScrollOffsetPadding 419 | if (padding < kMinimumScrollOffsetPadding ) { 420 | padding = kMinimumScrollOffsetPadding; 421 | } 422 | 423 | // Ideal offset places the subview rectangle origin "padding" points from the top of the scrollview. 424 | // If there is a top contentInset, also compensate for this so that subviewRect will not be placed under 425 | // things like navigation bars. 426 | offset = caretRect.origin.y - padding - contentInset.top; 427 | } else { 428 | centerViewInViewableArea(); 429 | } 430 | } else { 431 | centerViewInViewableArea(); 432 | } 433 | 434 | // Constrain the new contentOffset so we can't scroll past the bottom. Note that we don't take the bottom 435 | // inset into account, as this is manipulated to make space for the keyboard. 436 | CGFloat maxOffset = contentSize.height - viewAreaHeight - contentInset.top; 437 | if (offset > maxOffset) { 438 | offset = maxOffset; 439 | } 440 | 441 | // Constrain the new contentOffset so we can't scroll past the top, taking contentInsets into account 442 | if ( offset < -contentInset.top ) { 443 | offset = -contentInset.top; 444 | } 445 | 446 | return offset; 447 | } 448 | 449 | - (void)TPKeyboardAvoiding_initializeView:(UIView*)view { 450 | if ( [view isKindOfClass:[UITextField class]] 451 | && (((UITextField*)view).returnKeyType == UIReturnKeyDefault || (((UITextField*)view).returnKeyType == UIReturnKeyNext)) 452 | && (![(UITextField*)view delegate] || [(UITextField*)view delegate] == (id)self) ) { 453 | [(UITextField*)view setDelegate:(id)self]; 454 | UIView *otherView = [self TPKeyboardAvoiding_findNextInputViewAfterView:view beneathView:self]; 455 | 456 | if ( otherView ) { 457 | ((UITextField*)view).returnKeyType = UIReturnKeyNext; 458 | } else { 459 | ((UITextField*)view).returnKeyType = UIReturnKeyDone; 460 | } 461 | } 462 | } 463 | 464 | @end 465 | 466 | 467 | @implementation TPKeyboardAvoidingState 468 | @end 469 | 470 | #endif 471 | -------------------------------------------------------------------------------- /TPKeyboardAvoiding/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module TPKeyboardAvoiding { 2 | header "../TPKeyboardAvoidingCollectionView.h" 3 | header "../TPKeyboardAvoidingScrollView.h" 4 | header "../TPKeyboardAvoidingTableView.h" 5 | header "../UIScrollView+TPKeyboardAvoidingAdditions.h" 6 | 7 | export * 8 | } 9 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingKit/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingKit/TPKeyboardAvoidingKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // TPKeyboardAvoidingKit.h 3 | // TPKeyboardAvoidingKit 4 | // 5 | // Created by Jerson Michael Perpetua on 12/2/15. 6 | // Copyright © 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for TPKeyboardAvoidingKit. 12 | FOUNDATION_EXPORT double TPKeyboardAvoidingKitVersionNumber; 13 | 14 | //! Project version string for TPKeyboardAvoidingKit. 15 | FOUNDATION_EXPORT const unsigned char TPKeyboardAvoidingKitVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | #import 23 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3E081E4B1C0E63A40003DFF8 /* TPKeyboardAvoidingKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 3E081E4A1C0E63A40003DFF8 /* TPKeyboardAvoidingKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 3E081E501C0E63D70003DFF8 /* TPKeyboardAvoidingCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0ADD1B3CF35400090084 /* TPKeyboardAvoidingCollectionView.m */; }; 12 | 3E081E511C0E63D70003DFF8 /* TPKeyboardAvoidingScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0ADF1B3CF35400090084 /* TPKeyboardAvoidingScrollView.m */; }; 13 | 3E081E521C0E63D70003DFF8 /* TPKeyboardAvoidingTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0AE11B3CF35400090084 /* TPKeyboardAvoidingTableView.m */; }; 14 | 3E081E531C0E63D70003DFF8 /* UIScrollView+TPKeyboardAvoidingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0AE31B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.m */; }; 15 | 3E081E581C0E6ACA0003DFF8 /* TPKeyboardAvoidingCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C9B0ADC1B3CF35400090084 /* TPKeyboardAvoidingCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 3E081E591C0E6ACA0003DFF8 /* TPKeyboardAvoidingScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C9B0ADE1B3CF35400090084 /* TPKeyboardAvoidingScrollView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 3E081E5A1C0E6ACA0003DFF8 /* TPKeyboardAvoidingTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C9B0AE01B3CF35400090084 /* TPKeyboardAvoidingTableView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 3E081E5B1C0E6ACA0003DFF8 /* UIScrollView+TPKeyboardAvoidingAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C9B0AE21B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | 4C9B0A9F1B3CEE6E00090084 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0A9E1B3CEE6E00090084 /* main.m */; }; 20 | 4C9B0AA21B3CEE6E00090084 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0AA11B3CEE6E00090084 /* AppDelegate.m */; }; 21 | 4C9B0AAB1B3CEE6E00090084 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4C9B0AA91B3CEE6E00090084 /* Main.storyboard */; }; 22 | 4C9B0AAD1B3CEE6E00090084 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4C9B0AAC1B3CEE6E00090084 /* Images.xcassets */; }; 23 | 4C9B0AB01B3CEE6E00090084 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C9B0AAE1B3CEE6E00090084 /* LaunchScreen.xib */; }; 24 | 4C9B0AD41B3CF29100090084 /* TPKAScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0AD31B3CF29100090084 /* TPKAScrollViewController.m */; }; 25 | 4C9B0AD71B3CF2B900090084 /* TPKATableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0AD61B3CF2B900090084 /* TPKATableViewController.m */; }; 26 | 4C9B0ADA1B3CF2C600090084 /* TPKACollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0AD91B3CF2C600090084 /* TPKACollectionViewController.m */; }; 27 | 4C9B0AE41B3CF35400090084 /* TPKeyboardAvoidingCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0ADD1B3CF35400090084 /* TPKeyboardAvoidingCollectionView.m */; }; 28 | 4C9B0AE51B3CF35400090084 /* TPKeyboardAvoidingScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0ADF1B3CF35400090084 /* TPKeyboardAvoidingScrollView.m */; }; 29 | 4C9B0AE61B3CF35400090084 /* TPKeyboardAvoidingTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0AE11B3CF35400090084 /* TPKeyboardAvoidingTableView.m */; }; 30 | 4C9B0AE71B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C9B0AE31B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.m */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 3E081E481C0E63A40003DFF8 /* TPKeyboardAvoidingKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TPKeyboardAvoidingKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 3E081E4A1C0E63A40003DFF8 /* TPKeyboardAvoidingKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TPKeyboardAvoidingKit.h; sourceTree = ""; }; 36 | 3E081E4C1C0E63A40003DFF8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 4C9B0A991B3CEE6E00090084 /* TPKeyboardAvoidingSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TPKeyboardAvoidingSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 4C9B0A9D1B3CEE6E00090084 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | 4C9B0A9E1B3CEE6E00090084 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 40 | 4C9B0AA01B3CEE6E00090084 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 41 | 4C9B0AA11B3CEE6E00090084 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 42 | 4C9B0AAA1B3CEE6E00090084 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | 4C9B0AAC1B3CEE6E00090084 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 44 | 4C9B0AAF1B3CEE6E00090084 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 45 | 4C9B0AD21B3CF29100090084 /* TPKAScrollViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPKAScrollViewController.h; sourceTree = ""; }; 46 | 4C9B0AD31B3CF29100090084 /* TPKAScrollViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKAScrollViewController.m; sourceTree = ""; }; 47 | 4C9B0AD51B3CF2B900090084 /* TPKATableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPKATableViewController.h; sourceTree = ""; }; 48 | 4C9B0AD61B3CF2B900090084 /* TPKATableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKATableViewController.m; sourceTree = ""; }; 49 | 4C9B0AD81B3CF2C600090084 /* TPKACollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPKACollectionViewController.h; sourceTree = ""; }; 50 | 4C9B0AD91B3CF2C600090084 /* TPKACollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKACollectionViewController.m; sourceTree = ""; }; 51 | 4C9B0ADC1B3CF35400090084 /* TPKeyboardAvoidingCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPKeyboardAvoidingCollectionView.h; sourceTree = ""; }; 52 | 4C9B0ADD1B3CF35400090084 /* TPKeyboardAvoidingCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKeyboardAvoidingCollectionView.m; sourceTree = ""; }; 53 | 4C9B0ADE1B3CF35400090084 /* TPKeyboardAvoidingScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPKeyboardAvoidingScrollView.h; sourceTree = ""; }; 54 | 4C9B0ADF1B3CF35400090084 /* TPKeyboardAvoidingScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKeyboardAvoidingScrollView.m; sourceTree = ""; }; 55 | 4C9B0AE01B3CF35400090084 /* TPKeyboardAvoidingTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPKeyboardAvoidingTableView.h; sourceTree = ""; }; 56 | 4C9B0AE11B3CF35400090084 /* TPKeyboardAvoidingTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKeyboardAvoidingTableView.m; sourceTree = ""; }; 57 | 4C9B0AE21B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+TPKeyboardAvoidingAdditions.h"; sourceTree = ""; }; 58 | 4C9B0AE31B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+TPKeyboardAvoidingAdditions.m"; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 3E081E441C0E63A40003DFF8 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | 4C9B0A961B3CEE6E00090084 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | /* End PBXFrameworksBuildPhase section */ 77 | 78 | /* Begin PBXGroup section */ 79 | 3E081E491C0E63A40003DFF8 /* TPKeyboardAvoidingKit */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 3E081E4A1C0E63A40003DFF8 /* TPKeyboardAvoidingKit.h */, 83 | 3E081E4C1C0E63A40003DFF8 /* Info.plist */, 84 | ); 85 | path = TPKeyboardAvoidingKit; 86 | sourceTree = ""; 87 | }; 88 | 4C9B0A901B3CEE6E00090084 = { 89 | isa = PBXGroup; 90 | children = ( 91 | 4C9B0ADB1B3CF35400090084 /* TPKeyboardAvoiding */, 92 | 4C9B0A9B1B3CEE6E00090084 /* TPKeyboardAvoidingSample */, 93 | 3E081E491C0E63A40003DFF8 /* TPKeyboardAvoidingKit */, 94 | 4C9B0A9A1B3CEE6E00090084 /* Products */, 95 | ); 96 | sourceTree = ""; 97 | }; 98 | 4C9B0A9A1B3CEE6E00090084 /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 4C9B0A991B3CEE6E00090084 /* TPKeyboardAvoidingSample.app */, 102 | 3E081E481C0E63A40003DFF8 /* TPKeyboardAvoidingKit.framework */, 103 | ); 104 | name = Products; 105 | sourceTree = ""; 106 | }; 107 | 4C9B0A9B1B3CEE6E00090084 /* TPKeyboardAvoidingSample */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 4C9B0AA01B3CEE6E00090084 /* AppDelegate.h */, 111 | 4C9B0AA11B3CEE6E00090084 /* AppDelegate.m */, 112 | 4C9B0AD21B3CF29100090084 /* TPKAScrollViewController.h */, 113 | 4C9B0AD31B3CF29100090084 /* TPKAScrollViewController.m */, 114 | 4C9B0AD51B3CF2B900090084 /* TPKATableViewController.h */, 115 | 4C9B0AD61B3CF2B900090084 /* TPKATableViewController.m */, 116 | 4C9B0AD81B3CF2C600090084 /* TPKACollectionViewController.h */, 117 | 4C9B0AD91B3CF2C600090084 /* TPKACollectionViewController.m */, 118 | 4C9B0AAC1B3CEE6E00090084 /* Images.xcassets */, 119 | 4C9B0AA91B3CEE6E00090084 /* Main.storyboard */, 120 | 4C9B0AAE1B3CEE6E00090084 /* LaunchScreen.xib */, 121 | 4C9B0A9C1B3CEE6E00090084 /* Supporting Files */, 122 | ); 123 | path = TPKeyboardAvoidingSample; 124 | sourceTree = ""; 125 | }; 126 | 4C9B0A9C1B3CEE6E00090084 /* Supporting Files */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 4C9B0A9D1B3CEE6E00090084 /* Info.plist */, 130 | 4C9B0A9E1B3CEE6E00090084 /* main.m */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | 4C9B0ADB1B3CF35400090084 /* TPKeyboardAvoiding */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 4C9B0ADC1B3CF35400090084 /* TPKeyboardAvoidingCollectionView.h */, 139 | 4C9B0ADD1B3CF35400090084 /* TPKeyboardAvoidingCollectionView.m */, 140 | 4C9B0ADE1B3CF35400090084 /* TPKeyboardAvoidingScrollView.h */, 141 | 4C9B0ADF1B3CF35400090084 /* TPKeyboardAvoidingScrollView.m */, 142 | 4C9B0AE01B3CF35400090084 /* TPKeyboardAvoidingTableView.h */, 143 | 4C9B0AE11B3CF35400090084 /* TPKeyboardAvoidingTableView.m */, 144 | 4C9B0AE21B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.h */, 145 | 4C9B0AE31B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.m */, 146 | E402EFF123F59482009429E7 /* include */, 147 | ); 148 | path = TPKeyboardAvoiding; 149 | sourceTree = ""; 150 | }; 151 | E402EFF123F59482009429E7 /* include */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | ); 155 | path = include; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXHeadersBuildPhase section */ 161 | 3E081E451C0E63A40003DFF8 /* Headers */ = { 162 | isa = PBXHeadersBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | 3E081E4B1C0E63A40003DFF8 /* TPKeyboardAvoidingKit.h in Headers */, 166 | 3E081E581C0E6ACA0003DFF8 /* TPKeyboardAvoidingCollectionView.h in Headers */, 167 | 3E081E591C0E6ACA0003DFF8 /* TPKeyboardAvoidingScrollView.h in Headers */, 168 | 3E081E5A1C0E6ACA0003DFF8 /* TPKeyboardAvoidingTableView.h in Headers */, 169 | 3E081E5B1C0E6ACA0003DFF8 /* UIScrollView+TPKeyboardAvoidingAdditions.h in Headers */, 170 | ); 171 | runOnlyForDeploymentPostprocessing = 0; 172 | }; 173 | /* End PBXHeadersBuildPhase section */ 174 | 175 | /* Begin PBXNativeTarget section */ 176 | 3E081E471C0E63A40003DFF8 /* TPKeyboardAvoidingKit */ = { 177 | isa = PBXNativeTarget; 178 | buildConfigurationList = 3E081E4F1C0E63A40003DFF8 /* Build configuration list for PBXNativeTarget "TPKeyboardAvoidingKit" */; 179 | buildPhases = ( 180 | 3E081E431C0E63A40003DFF8 /* Sources */, 181 | 3E081E441C0E63A40003DFF8 /* Frameworks */, 182 | 3E081E451C0E63A40003DFF8 /* Headers */, 183 | 3E081E461C0E63A40003DFF8 /* Resources */, 184 | ); 185 | buildRules = ( 186 | ); 187 | dependencies = ( 188 | ); 189 | name = TPKeyboardAvoidingKit; 190 | productName = TPKeyboardAvoidingKit; 191 | productReference = 3E081E481C0E63A40003DFF8 /* TPKeyboardAvoidingKit.framework */; 192 | productType = "com.apple.product-type.framework"; 193 | }; 194 | 4C9B0A981B3CEE6E00090084 /* TPKeyboardAvoidingSample */ = { 195 | isa = PBXNativeTarget; 196 | buildConfigurationList = 4C9B0ABF1B3CEE6E00090084 /* Build configuration list for PBXNativeTarget "TPKeyboardAvoidingSample" */; 197 | buildPhases = ( 198 | 4C9B0A951B3CEE6E00090084 /* Sources */, 199 | 4C9B0A961B3CEE6E00090084 /* Frameworks */, 200 | 4C9B0A971B3CEE6E00090084 /* Resources */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | ); 206 | name = TPKeyboardAvoidingSample; 207 | productName = TPKeyboardAvoidingSample; 208 | productReference = 4C9B0A991B3CEE6E00090084 /* TPKeyboardAvoidingSample.app */; 209 | productType = "com.apple.product-type.application"; 210 | }; 211 | /* End PBXNativeTarget section */ 212 | 213 | /* Begin PBXProject section */ 214 | 4C9B0A911B3CEE6E00090084 /* Project object */ = { 215 | isa = PBXProject; 216 | attributes = { 217 | LastUpgradeCheck = 1210; 218 | ORGANIZATIONNAME = "A Tasty Pixel"; 219 | TargetAttributes = { 220 | 3E081E471C0E63A40003DFF8 = { 221 | CreatedOnToolsVersion = 7.1.1; 222 | }; 223 | 4C9B0A981B3CEE6E00090084 = { 224 | CreatedOnToolsVersion = 6.3.1; 225 | }; 226 | }; 227 | }; 228 | buildConfigurationList = 4C9B0A941B3CEE6E00090084 /* Build configuration list for PBXProject "TPKeyboardAvoidingSample" */; 229 | compatibilityVersion = "Xcode 3.2"; 230 | developmentRegion = en; 231 | hasScannedForEncodings = 0; 232 | knownRegions = ( 233 | en, 234 | Base, 235 | ); 236 | mainGroup = 4C9B0A901B3CEE6E00090084; 237 | productRefGroup = 4C9B0A9A1B3CEE6E00090084 /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | 4C9B0A981B3CEE6E00090084 /* TPKeyboardAvoidingSample */, 242 | 3E081E471C0E63A40003DFF8 /* TPKeyboardAvoidingKit */, 243 | ); 244 | }; 245 | /* End PBXProject section */ 246 | 247 | /* Begin PBXResourcesBuildPhase section */ 248 | 3E081E461C0E63A40003DFF8 /* Resources */ = { 249 | isa = PBXResourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | }; 255 | 4C9B0A971B3CEE6E00090084 /* Resources */ = { 256 | isa = PBXResourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | 4C9B0AAB1B3CEE6E00090084 /* Main.storyboard in Resources */, 260 | 4C9B0AB01B3CEE6E00090084 /* LaunchScreen.xib in Resources */, 261 | 4C9B0AAD1B3CEE6E00090084 /* Images.xcassets in Resources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXResourcesBuildPhase section */ 266 | 267 | /* Begin PBXSourcesBuildPhase section */ 268 | 3E081E431C0E63A40003DFF8 /* Sources */ = { 269 | isa = PBXSourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | 3E081E521C0E63D70003DFF8 /* TPKeyboardAvoidingTableView.m in Sources */, 273 | 3E081E531C0E63D70003DFF8 /* UIScrollView+TPKeyboardAvoidingAdditions.m in Sources */, 274 | 3E081E501C0E63D70003DFF8 /* TPKeyboardAvoidingCollectionView.m in Sources */, 275 | 3E081E511C0E63D70003DFF8 /* TPKeyboardAvoidingScrollView.m in Sources */, 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | }; 279 | 4C9B0A951B3CEE6E00090084 /* Sources */ = { 280 | isa = PBXSourcesBuildPhase; 281 | buildActionMask = 2147483647; 282 | files = ( 283 | 4C9B0AE51B3CF35400090084 /* TPKeyboardAvoidingScrollView.m in Sources */, 284 | 4C9B0AE61B3CF35400090084 /* TPKeyboardAvoidingTableView.m in Sources */, 285 | 4C9B0ADA1B3CF2C600090084 /* TPKACollectionViewController.m in Sources */, 286 | 4C9B0AA21B3CEE6E00090084 /* AppDelegate.m in Sources */, 287 | 4C9B0AE71B3CF35400090084 /* UIScrollView+TPKeyboardAvoidingAdditions.m in Sources */, 288 | 4C9B0AD71B3CF2B900090084 /* TPKATableViewController.m in Sources */, 289 | 4C9B0AD41B3CF29100090084 /* TPKAScrollViewController.m in Sources */, 290 | 4C9B0AE41B3CF35400090084 /* TPKeyboardAvoidingCollectionView.m in Sources */, 291 | 4C9B0A9F1B3CEE6E00090084 /* main.m in Sources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | /* End PBXSourcesBuildPhase section */ 296 | 297 | /* Begin PBXVariantGroup section */ 298 | 4C9B0AA91B3CEE6E00090084 /* Main.storyboard */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 4C9B0AAA1B3CEE6E00090084 /* Base */, 302 | ); 303 | name = Main.storyboard; 304 | sourceTree = ""; 305 | }; 306 | 4C9B0AAE1B3CEE6E00090084 /* LaunchScreen.xib */ = { 307 | isa = PBXVariantGroup; 308 | children = ( 309 | 4C9B0AAF1B3CEE6E00090084 /* Base */, 310 | ); 311 | name = LaunchScreen.xib; 312 | sourceTree = ""; 313 | }; 314 | /* End PBXVariantGroup section */ 315 | 316 | /* Begin XCBuildConfiguration section */ 317 | 3E081E4D1C0E63A40003DFF8 /* Debug */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 321 | CURRENT_PROJECT_VERSION = 1; 322 | DEBUG_INFORMATION_FORMAT = dwarf; 323 | DEFINES_MODULE = YES; 324 | DYLIB_COMPATIBILITY_VERSION = 1; 325 | DYLIB_CURRENT_VERSION = 1; 326 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 327 | INFOPLIST_FILE = TPKeyboardAvoidingKit/Info.plist; 328 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 329 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 330 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 331 | PRODUCT_BUNDLE_IDENTIFIER = com.atastypixel.TPKeyboardAvoidingKit; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | SKIP_INSTALL = YES; 334 | VERSIONING_SYSTEM = "apple-generic"; 335 | VERSION_INFO_PREFIX = ""; 336 | }; 337 | name = Debug; 338 | }; 339 | 3E081E4E1C0E63A40003DFF8 /* Release */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 343 | CURRENT_PROJECT_VERSION = 1; 344 | DEFINES_MODULE = YES; 345 | DYLIB_COMPATIBILITY_VERSION = 1; 346 | DYLIB_CURRENT_VERSION = 1; 347 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 348 | INFOPLIST_FILE = TPKeyboardAvoidingKit/Info.plist; 349 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 350 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 351 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 352 | PRODUCT_BUNDLE_IDENTIFIER = com.atastypixel.TPKeyboardAvoidingKit; 353 | PRODUCT_NAME = "$(TARGET_NAME)"; 354 | SKIP_INSTALL = YES; 355 | VERSIONING_SYSTEM = "apple-generic"; 356 | VERSION_INFO_PREFIX = ""; 357 | }; 358 | name = Release; 359 | }; 360 | 4C9B0ABD1B3CEE6E00090084 /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 366 | CLANG_CXX_LIBRARY = "libc++"; 367 | CLANG_ENABLE_MODULES = YES; 368 | CLANG_ENABLE_OBJC_ARC = YES; 369 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 370 | CLANG_WARN_BOOL_CONVERSION = YES; 371 | CLANG_WARN_COMMA = YES; 372 | CLANG_WARN_CONSTANT_CONVERSION = YES; 373 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 374 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 375 | CLANG_WARN_EMPTY_BODY = YES; 376 | CLANG_WARN_ENUM_CONVERSION = YES; 377 | CLANG_WARN_INFINITE_RECURSION = YES; 378 | CLANG_WARN_INT_CONVERSION = YES; 379 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 380 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 381 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 382 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 383 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 384 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 385 | CLANG_WARN_STRICT_PROTOTYPES = YES; 386 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 387 | CLANG_WARN_UNREACHABLE_CODE = YES; 388 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 389 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 390 | COPY_PHASE_STRIP = NO; 391 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 392 | ENABLE_STRICT_OBJC_MSGSEND = YES; 393 | ENABLE_TESTABILITY = YES; 394 | GCC_C_LANGUAGE_STANDARD = gnu99; 395 | GCC_DYNAMIC_NO_PIC = NO; 396 | GCC_NO_COMMON_BLOCKS = YES; 397 | GCC_OPTIMIZATION_LEVEL = 0; 398 | GCC_PREPROCESSOR_DEFINITIONS = ( 399 | "DEBUG=1", 400 | "$(inherited)", 401 | ); 402 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 403 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 404 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 405 | GCC_WARN_UNDECLARED_SELECTOR = YES; 406 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 407 | GCC_WARN_UNUSED_FUNCTION = YES; 408 | GCC_WARN_UNUSED_VARIABLE = YES; 409 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 410 | MTL_ENABLE_DEBUG_INFO = YES; 411 | ONLY_ACTIVE_ARCH = YES; 412 | SDKROOT = iphoneos; 413 | TARGETED_DEVICE_FAMILY = "1,2"; 414 | }; 415 | name = Debug; 416 | }; 417 | 4C9B0ABE1B3CEE6E00090084 /* Release */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ALWAYS_SEARCH_USER_PATHS = NO; 421 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 422 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 423 | CLANG_CXX_LIBRARY = "libc++"; 424 | CLANG_ENABLE_MODULES = YES; 425 | CLANG_ENABLE_OBJC_ARC = YES; 426 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 427 | CLANG_WARN_BOOL_CONVERSION = YES; 428 | CLANG_WARN_COMMA = YES; 429 | CLANG_WARN_CONSTANT_CONVERSION = YES; 430 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 431 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 432 | CLANG_WARN_EMPTY_BODY = YES; 433 | CLANG_WARN_ENUM_CONVERSION = YES; 434 | CLANG_WARN_INFINITE_RECURSION = YES; 435 | CLANG_WARN_INT_CONVERSION = YES; 436 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 437 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 438 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 439 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 440 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 441 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 442 | CLANG_WARN_STRICT_PROTOTYPES = YES; 443 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 444 | CLANG_WARN_UNREACHABLE_CODE = YES; 445 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 446 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 447 | COPY_PHASE_STRIP = NO; 448 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 449 | ENABLE_NS_ASSERTIONS = NO; 450 | ENABLE_STRICT_OBJC_MSGSEND = YES; 451 | GCC_C_LANGUAGE_STANDARD = gnu99; 452 | GCC_NO_COMMON_BLOCKS = YES; 453 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 454 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 455 | GCC_WARN_UNDECLARED_SELECTOR = YES; 456 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 457 | GCC_WARN_UNUSED_FUNCTION = YES; 458 | GCC_WARN_UNUSED_VARIABLE = YES; 459 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 460 | MTL_ENABLE_DEBUG_INFO = NO; 461 | SDKROOT = iphoneos; 462 | TARGETED_DEVICE_FAMILY = "1,2"; 463 | VALIDATE_PRODUCT = YES; 464 | }; 465 | name = Release; 466 | }; 467 | 4C9B0AC01B3CEE6E00090084 /* Debug */ = { 468 | isa = XCBuildConfiguration; 469 | buildSettings = { 470 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 471 | INFOPLIST_FILE = TPKeyboardAvoidingSample/Info.plist; 472 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 473 | PRODUCT_BUNDLE_IDENTIFIER = "com.atastypixel.$(PRODUCT_NAME:rfc1034identifier)"; 474 | PRODUCT_NAME = "$(TARGET_NAME)"; 475 | }; 476 | name = Debug; 477 | }; 478 | 4C9B0AC11B3CEE6E00090084 /* Release */ = { 479 | isa = XCBuildConfiguration; 480 | buildSettings = { 481 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 482 | INFOPLIST_FILE = TPKeyboardAvoidingSample/Info.plist; 483 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 484 | PRODUCT_BUNDLE_IDENTIFIER = "com.atastypixel.$(PRODUCT_NAME:rfc1034identifier)"; 485 | PRODUCT_NAME = "$(TARGET_NAME)"; 486 | }; 487 | name = Release; 488 | }; 489 | /* End XCBuildConfiguration section */ 490 | 491 | /* Begin XCConfigurationList section */ 492 | 3E081E4F1C0E63A40003DFF8 /* Build configuration list for PBXNativeTarget "TPKeyboardAvoidingKit" */ = { 493 | isa = XCConfigurationList; 494 | buildConfigurations = ( 495 | 3E081E4D1C0E63A40003DFF8 /* Debug */, 496 | 3E081E4E1C0E63A40003DFF8 /* Release */, 497 | ); 498 | defaultConfigurationIsVisible = 0; 499 | defaultConfigurationName = Release; 500 | }; 501 | 4C9B0A941B3CEE6E00090084 /* Build configuration list for PBXProject "TPKeyboardAvoidingSample" */ = { 502 | isa = XCConfigurationList; 503 | buildConfigurations = ( 504 | 4C9B0ABD1B3CEE6E00090084 /* Debug */, 505 | 4C9B0ABE1B3CEE6E00090084 /* Release */, 506 | ); 507 | defaultConfigurationIsVisible = 0; 508 | defaultConfigurationName = Release; 509 | }; 510 | 4C9B0ABF1B3CEE6E00090084 /* Build configuration list for PBXNativeTarget "TPKeyboardAvoidingSample" */ = { 511 | isa = XCConfigurationList; 512 | buildConfigurations = ( 513 | 4C9B0AC01B3CEE6E00090084 /* Debug */, 514 | 4C9B0AC11B3CEE6E00090084 /* Release */, 515 | ); 516 | defaultConfigurationIsVisible = 0; 517 | defaultConfigurationName = Release; 518 | }; 519 | /* End XCConfigurationList section */ 520 | }; 521 | rootObject = 4C9B0A911B3CEE6E00090084 /* Project object */; 522 | } 523 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample.xcodeproj/xcshareddata/xcschemes/TPKeyboardAvoidingKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TPKeyboardAvoidingSample 4 | // 5 | // Created by Michael Tyson on 26/06/2015. 6 | // Copyright (c) 2015 A Tasty Pixel. 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 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TPKeyboardAvoidingSample 4 | // 5 | // Created by Michael Tyson on 26/06/2015. 6 | // Copyright (c) 2015 A Tasty Pixel. 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 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/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 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/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 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 156 | 157 | 158 | 159 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 160 | 161 | 162 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 163 | 164 | 165 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 166 | 167 | 168 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 169 | 170 | 171 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 172 | 173 | 174 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/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" : "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 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Collection.imageset/Collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeltyson/TPKeyboardAvoiding/3217d6287a6b0ecb1db65a214a2e0fba9934e417/TPKeyboardAvoidingSample/Images.xcassets/Collection.imageset/Collection.png -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Collection.imageset/Collection@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeltyson/TPKeyboardAvoiding/3217d6287a6b0ecb1db65a214a2e0fba9934e417/TPKeyboardAvoidingSample/Images.xcassets/Collection.imageset/Collection@2x.png -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Collection.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Collection.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Collection@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Scroll.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Scroll.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Scroll@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Scroll.imageset/Scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeltyson/TPKeyboardAvoiding/3217d6287a6b0ecb1db65a214a2e0fba9934e417/TPKeyboardAvoidingSample/Images.xcassets/Scroll.imageset/Scroll.png -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Scroll.imageset/Scroll@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeltyson/TPKeyboardAvoiding/3217d6287a6b0ecb1db65a214a2e0fba9934e417/TPKeyboardAvoidingSample/Images.xcassets/Scroll.imageset/Scroll@2x.png -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Table.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Table.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Table@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Table.imageset/Table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeltyson/TPKeyboardAvoiding/3217d6287a6b0ecb1db65a214a2e0fba9934e417/TPKeyboardAvoidingSample/Images.xcassets/Table.imageset/Table.png -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Table.imageset/Table@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeltyson/TPKeyboardAvoiding/3217d6287a6b0ecb1db65a214a2e0fba9934e417/TPKeyboardAvoidingSample/Images.xcassets/Table.imageset/Table@2x.png -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Text.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Text.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Text@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Text.imageset/Text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeltyson/TPKeyboardAvoiding/3217d6287a6b0ecb1db65a214a2e0fba9934e417/TPKeyboardAvoidingSample/Images.xcassets/Text.imageset/Text.png -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/Images.xcassets/Text.imageset/Text@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeltyson/TPKeyboardAvoiding/3217d6287a6b0ecb1db65a214a2e0fba9934e417/TPKeyboardAvoidingSample/Images.xcassets/Text.imageset/Text@2x.png -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/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 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UISupportedInterfaceOrientations~ipad 50 | 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/TPKACollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TPKACollectionViewController.h 3 | // TPKeyboardAvoidingSample 4 | // 5 | // Created by Michael Tyson on 26/06/2015. 6 | // Copyright (c) 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TPKACollectionViewController : UICollectionViewController 12 | 13 | @end 14 | 15 | @interface TPKACollectionViewControllerCell : UICollectionViewCell 16 | @property (nonatomic, strong) IBOutlet UILabel * label; 17 | @property (nonatomic, strong) IBOutlet UITextField * textField; 18 | @end -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/TPKACollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TPKACollectionViewController.m 3 | // TPKeyboardAvoidingSample 4 | // 5 | // Created by Michael Tyson on 26/06/2015. 6 | // Copyright (c) 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import "TPKACollectionViewController.h" 10 | 11 | @implementation TPKACollectionViewController 12 | 13 | static NSString * const reuseIdentifier = @"Cell"; 14 | 15 | #pragma mark Collection view data source 16 | 17 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 18 | return 1; 19 | } 20 | 21 | 22 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 23 | return 30; 24 | } 25 | 26 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 27 | TPKACollectionViewControllerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; 28 | 29 | cell.label.text = [NSString stringWithFormat:@"Label %d", (int)indexPath.row]; 30 | cell.textField.placeholder = [NSString stringWithFormat:@"Field %d", (int)indexPath.row]; 31 | 32 | return cell; 33 | } 34 | 35 | @end 36 | 37 | @implementation TPKACollectionViewControllerCell 38 | @end 39 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/TPKAScrollViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TPKAScrollViewController.h 3 | // TPKeyboardAvoidingSample 4 | // 5 | // Created by Michael Tyson on 26/06/2015. 6 | // Copyright (c) 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TPKAScrollViewController : UIViewController 12 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; 13 | @end 14 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/TPKAScrollViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TPKAScrollViewController.m 3 | // TPKeyboardAvoidingSample 4 | // 5 | // Created by Michael Tyson on 26/06/2015. 6 | // Copyright (c) 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import "TPKAScrollViewController.h" 10 | 11 | static const int kRowCount = 40; 12 | static const int kGroupCount = 5; 13 | 14 | @interface TPKAScrollViewController () 15 | 16 | @end 17 | 18 | @implementation TPKAScrollViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | // Add some text fields in rows 24 | UIView * priorView = nil; 25 | for ( int i=0; i 10 | 11 | @interface TPKATableViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/TPKATableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TPKATableViewController.m 3 | // TPKeyboardAvoidingSample 4 | // 5 | // Created by Michael Tyson on 26/06/2015. 6 | // Copyright (c) 2015 A Tasty Pixel. All rights reserved. 7 | // 8 | 9 | #import "TPKATableViewController.h" 10 | 11 | @implementation TPKATableViewController 12 | 13 | #pragma mark - Table view data source 14 | 15 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 16 | return 1; 17 | } 18 | 19 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 20 | return 30; 21 | } 22 | 23 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 24 | static NSString *CellIdentifier = @"Cell"; 25 | 26 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 27 | if (cell == nil) { 28 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 29 | 30 | UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 30)]; 31 | textField.borderStyle = UITextBorderStyleRoundedRect; 32 | cell.accessoryView = textField; 33 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 34 | } 35 | 36 | cell.textLabel.text = [NSString stringWithFormat:@"Label %d", (int)indexPath.row]; 37 | ((UITextField*)cell.accessoryView).placeholder = [NSString stringWithFormat:@"Field %d", (int)indexPath.row]; 38 | 39 | return cell; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /TPKeyboardAvoidingSample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TPKeyboardAvoidingSample 4 | // 5 | // Created by Michael Tyson on 26/06/2015. 6 | // Copyright (c) 2015 A Tasty Pixel. 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 | --------------------------------------------------------------------------------