Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Implemented automatic "next" buttons on keyboard to switch text fields

  • Loading branch information...
commit 383478ef34703312978f580b5f362ea3f59f3062 1 parent 9ee481c
@michaeltyson authored
View
7 TPKeyboardAvoidingSample/FirstViewController.h
@@ -10,11 +10,6 @@
@class TPKeyboardAvoidingScrollView;
-@interface FirstViewController : UIViewController <UITextFieldDelegate>
+@interface FirstViewController : UIViewController
@property (nonatomic, retain) IBOutlet TPKeyboardAvoidingScrollView *scrollView;
-@property (nonatomic, retain) IBOutlet UITextField *txtIggle;
-@property (nonatomic, retain) IBOutlet UITextField *txtNiggle;
-@property (nonatomic, retain) IBOutlet UITextField *txtOggle;
-@property (nonatomic, retain) IBOutlet UITextField *txtBogle;
-@property (nonatomic, retain) IBOutlet UITextField *txtSplat;
@end
View
45 TPKeyboardAvoidingSample/FirstViewController.m
@@ -11,12 +11,6 @@
@implementation FirstViewController
@synthesize scrollView;
-@synthesize txtIggle;
-@synthesize txtNiggle;
-@synthesize txtOggle;
-@synthesize txtBogle;
-@synthesize txtSplat;
-
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
@@ -50,11 +44,6 @@ - (void)didReceiveMemoryWarning
- (void)viewDidUnload
{
[self setScrollView:nil];
- [self setTxtIggle:nil];
- [self setTxtNiggle:nil];
- [self setTxtOggle:nil];
- [self setTxtBogle:nil];
- [self setTxtSplat:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
@@ -65,41 +54,7 @@ - (void)viewDidUnload
- (void)dealloc
{
[scrollView release];
- [txtIggle release];
- [txtNiggle release];
- [txtOggle release];
- [txtBogle release];
- [txtSplat release];
[super dealloc];
}
--(BOOL)textFieldShouldReturn:(UITextField *)textField {
- if (textField == txtIggle) {
- [txtNiggle becomeFirstResponder];
- }
-
- else if (textField == txtNiggle) {
- [txtOggle becomeFirstResponder];
- }
-
- else if (textField == txtOggle) {
- [txtBogle becomeFirstResponder];
- }
-
- else if (textField == txtBogle) {
- [txtSplat becomeFirstResponder];
- }
- else{
- [textField resignFirstResponder];
- }
-
-
- return YES;
-}
-
--(void)textFieldDidBeginEditing:(UITextField *)textField
-{
- [scrollView adjustOffsetToIdealIfNeeded];
-}
-
@end
View
2  TPKeyboardAvoidingSample/SecondViewController.h
@@ -9,5 +9,5 @@
#import <UIKit/UIKit.h>
-@interface SecondViewController : UITableViewController <UITextFieldDelegate>
+@interface SecondViewController : UITableViewController
@end
View
9 TPKeyboardAvoidingSample/SecondViewController.m
@@ -99,10 +99,10 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UITextField *textField = [[[UITextField alloc] initWithFrame:CGRectMake(0, 0, 150, 30)] autorelease];
- textField.delegate = self;
textField.returnKeyType = UIReturnKeyDone;
textField.borderStyle = UITextBorderStyleRoundedRect;
cell.accessoryView = textField;
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
cell.textLabel.text = [NSString stringWithFormat:@"Order %d", indexPath.row];
@@ -111,11 +111,4 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
return cell;
}
-#pragma mark - Text field delegate
-
--(BOOL)textFieldShouldReturn:(UITextField *)textField {
- [textField resignFirstResponder];
- return YES;
-}
-
@end
View
140 TPKeyboardAvoidingSample/en.lproj/FirstView.xib
@@ -51,6 +51,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 30}, {280, 31}}</string>
<reference key="NSSuperview" ref="206876602"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="89122733"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
@@ -87,6 +88,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 281}, {280, 31}}</string>
<reference key="NSSuperview" ref="206876602"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="283243135"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
@@ -114,6 +116,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 337}, {280, 37}}</string>
<reference key="NSSuperview" ref="206876602"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="364961226"/>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
@@ -121,7 +124,7 @@
<int key="IBUIContentVerticalAlignment">0</int>
<int key="IBUIButtonType">1</int>
<string key="IBUINormalTitle">Do A Thing</string>
- <object class="NSColor" key="IBUIHighlightedTitleColor">
+ <object class="NSColor" key="IBUIHighlightedTitleColor" id="460154988">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
@@ -150,6 +153,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 94}, {280, 31}}</string>
<reference key="NSSuperview" ref="206876602"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="141163670"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
@@ -177,6 +181,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 156}, {280, 31}}</string>
<reference key="NSSuperview" ref="206876602"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="331828899"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
@@ -204,6 +209,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 218}, {280, 31}}</string>
<reference key="NSSuperview" ref="206876602"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="325075817"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
@@ -231,7 +237,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 400}, {280, 31}}</string>
<reference key="NSSuperview" ref="206876602"/>
- <reference key="NSNextKeyView"/>
+ <reference key="NSWindow"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
@@ -258,6 +264,7 @@
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 500}, {280, 31}}</string>
<reference key="NSSuperview" ref="206876602"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="1024068394"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
@@ -283,6 +290,7 @@
</object>
<string key="NSFrameSize">{320, 411}</string>
<reference key="NSSuperview" ref="191373211"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="696973480"/>
<bool key="IBUIClipsSubviews">YES</bool>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
@@ -291,12 +299,9 @@
</object>
<string key="NSFrame">{{0, 20}, {320, 411}}</string>
<reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="206876602"/>
- <object class="NSColor" key="IBUIBackgroundColor">
- <int key="NSColorSpace">1</int>
- <bytes key="NSRGB">MCAwIDAgMAA</bytes>
- <string key="IBUIColorCocoaTouchKeyPath">groupTableViewBackgroundColor</string>
- </object>
+ <reference key="IBUIBackgroundColor" ref="460154988"/>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<object class="IBUISimulatedTabBarMetrics" key="IBUISimulatedBottomBarMetrics"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
@@ -321,94 +326,6 @@
</object>
<int key="connectionID">21</int>
</object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">txtIggle</string>
- <reference key="source" ref="372490531"/>
- <reference key="destination" ref="696973480"/>
- </object>
- <int key="connectionID">22</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">txtNiggle</string>
- <reference key="source" ref="372490531"/>
- <reference key="destination" ref="89122733"/>
- </object>
- <int key="connectionID">23</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">txtOggle</string>
- <reference key="source" ref="372490531"/>
- <reference key="destination" ref="141163670"/>
- </object>
- <int key="connectionID">24</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">txtBogle</string>
- <reference key="source" ref="372490531"/>
- <reference key="destination" ref="1024068394"/>
- </object>
- <int key="connectionID">25</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">txtSplat</string>
- <reference key="source" ref="372490531"/>
- <reference key="destination" ref="325075817"/>
- </object>
- <int key="connectionID">26</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">delegate</string>
- <reference key="source" ref="696973480"/>
- <reference key="destination" ref="372490531"/>
- </object>
- <int key="connectionID">13</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">delegate</string>
- <reference key="source" ref="325075817"/>
- <reference key="destination" ref="372490531"/>
- </object>
- <int key="connectionID">14</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">delegate</string>
- <reference key="source" ref="89122733"/>
- <reference key="destination" ref="372490531"/>
- </object>
- <int key="connectionID">16</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">delegate</string>
- <reference key="source" ref="141163670"/>
- <reference key="destination" ref="372490531"/>
- </object>
- <int key="connectionID">18</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">delegate</string>
- <reference key="source" ref="1024068394"/>
- <reference key="destination" ref="372490531"/>
- </object>
- <int key="connectionID">20</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBCocoaTouchOutletConnection" key="connection">
- <string key="label">delegate</string>
- <reference key="source" ref="364961226"/>
- <reference key="destination" ref="372490531"/>
- </object>
- <int key="connectionID">28</int>
- </object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -552,7 +469,38 @@
<nil key="sourceID"/>
<int key="maxID">29</int>
</object>
- <object class="IBClassDescriber" key="IBDocument.Classes"/>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">FirstViewController</string>
+ <string key="superclassName">UIViewController</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">scrollView</string>
+ <string key="NS.object.0">TPKeyboardAvoidingScrollView</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">scrollView</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">scrollView</string>
+ <string key="candidateClassName">TPKeyboardAvoidingScrollView</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/FirstViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">TPKeyboardAvoidingScrollView</string>
+ <string key="superclassName">UIScrollView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/TPKeyboardAvoidingScrollView.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
View
13 TPKeyboardAvoidingScrollView.h
@@ -7,14 +7,7 @@
#import <UIKit/UIKit.h>
-@interface TPKeyboardAvoidingScrollView : UIScrollView {
- UIEdgeInsets _priorInset;
- BOOL _priorInsetSaved;
- BOOL _keyboardVisible;
- CGRect _keyboardRect;
- CGSize _originalContentSize;
- CGPoint _originalContentOffset;
-}
-
-- (void)adjustOffsetToIdealIfNeeded;
+@interface TPKeyboardAvoidingScrollView : UIScrollView
+- (BOOL)focusNextTextField;
+- (void)scrollToActiveTextField;
@end
View
123 TPKeyboardAvoidingScrollView.m
@@ -9,7 +9,14 @@
#define _UIKeyboardFrameEndUserInfoKey (&UIKeyboardFrameEndUserInfoKey != NULL ? UIKeyboardFrameEndUserInfoKey : @"UIKeyboardBoundsUserInfoKey")
-@interface TPKeyboardAvoidingScrollView ()
+@interface TPKeyboardAvoidingScrollView () <UITextFieldDelegate, UITextViewDelegate> {
+ UIEdgeInsets _priorInset;
+ BOOL _priorInsetSaved;
+ BOOL _keyboardVisible;
+ CGRect _keyboardRect;
+ CGSize _originalContentSize;
+ CGPoint _originalContentOffset;
+}
- (UIView*)findFirstResponderBeneathView:(UIView*)view;
- (UIEdgeInsets)contentInsetForKeyboard;
- (CGFloat)idealOffsetForView:(UIView *)view withSpace:(CGFloat)space;
@@ -18,6 +25,8 @@ - (CGRect)keyboardRect;
@implementation TPKeyboardAvoidingScrollView
+#pragma mark - Setup/Teardown
+
- (void)setup {
_priorInsetSaved = NO;
if ( CGSizeEqualToSize(self.contentSize, CGSizeZero) ) {
@@ -69,6 +78,8 @@ -(void)setContentSize:(CGSize)contentSize {
}
}
+#pragma mark - Responders, events
+
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[[self findFirstResponderBeneathView:self] resignFirstResponder];
[super touchesEnded:touches withEvent:event];
@@ -120,6 +131,60 @@ - (void)keyboardWillHide:(NSNotification*)notification {
[UIView commitAnimations];
}
+-(BOOL)textFieldShouldReturn:(UITextField *)textField {
+ if ( ![self focusNextTextField] ) {
+ [textField resignFirstResponder];
+ }
+ return YES;
+}
+
+-(void)textFieldDidBeginEditing:(UITextField *)textField {
+ [self scrollToActiveTextField];
+}
+
+-(void)textViewDidBeginEditing:(UITextView *)textView {
+ [self scrollToActiveTextField];
+}
+
+-(void)layoutSubviews {
+ [super layoutSubviews];
+ [self initializeViewsBeneathView:self];
+}
+
+#pragma mark - Utilities
+
+- (BOOL)focusNextTextField {
+ UIView *firstResponder = [self findFirstResponderBeneathView:self];
+ if ( !firstResponder ) {
+ return NO;
+ }
+
+ CGFloat minY = CGFLOAT_MAX;
+ UIView *view = nil;
+ [self findTextFieldAfterTextField:firstResponder beneathView:self minY:&minY foundView:&view];
+
+ if ( view ) {
+ [view becomeFirstResponder];
+ return YES;
+ }
+
+ return NO;
+}
+
+-(void)scrollToActiveTextField {
+ if ( !_keyboardVisible ) return;
+
+ CGFloat visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom;
+
+ CGPoint idealOffset = CGPointMake(0, [self idealOffsetForView:[self findFirstResponderBeneathView:self] withSpace:visibleSpace]);
+
+ [self setContentOffset:idealOffset animated:YES];
+
+ _originalContentOffset = self.contentOffset;
+}
+
+#pragma mark - Helpers
+
- (UIView*)findFirstResponderBeneathView:(UIView*)view {
// Search recursively for first responder
for ( UIView *childView in view.subviews ) {
@@ -130,6 +195,33 @@ - (UIView*)findFirstResponderBeneathView:(UIView*)view {
return nil;
}
+- (void)findTextFieldAfterTextField:(UIView*)priorTextField beneathView:(UIView*)view minY:(CGFloat*)minY foundView:(UIView**)foundView {
+ // Search recursively for text field or text view below priorTextField
+ CGFloat priorFieldOffset = CGRectGetMinY([self convertRect:priorTextField.frame fromView:priorTextField.superview]);
+ for ( UIView *childView in view.subviews ) {
+ if ( childView.hidden ) continue;
+ if ( ([childView isKindOfClass:[UITextField class]] || [childView isKindOfClass:[UITextView class]]) ) {
+ CGRect frame = [self convertRect:childView.frame fromView:view];
+ if ( childView != priorTextField && CGRectGetMinY(frame) >= priorFieldOffset && CGRectGetMinY(frame) < *minY ) {
+ *minY = CGRectGetMinY(frame);
+ *foundView = childView;
+ }
+ } else {
+ [self findTextFieldAfterTextField:priorTextField beneathView:childView minY:minY foundView:foundView];
+ }
+ }
+}
+
+- (void)initializeViewsBeneathView:(UIView*)view {
+ for ( UIView *childView in view.subviews ) {
+ if ( ([childView isKindOfClass:[UITextField class]] || [childView isKindOfClass:[UITextView class]]) ) {
+ [self initializeView:childView];
+ } else {
+ [self initializeViewsBeneathView:childView];
+ }
+ }
+}
+
- (UIEdgeInsets)contentInsetForKeyboard {
UIEdgeInsets newInset = self.contentInset;
CGRect keyboardRect = [self keyboardRect];
@@ -165,18 +257,6 @@ -(CGFloat)idealOffsetForView:(UIView *)view withSpace:(CGFloat)space {
return offset;
}
--(void)adjustOffsetToIdealIfNeeded {
-
- // Only do this if the keyboard is already visible
- if ( !_keyboardVisible ) return;
-
- CGFloat visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom;
-
- CGPoint idealOffset = CGPointMake(0, [self idealOffsetForView:[self findFirstResponderBeneathView:self] withSpace:visibleSpace]);
-
- [self setContentOffset:idealOffset animated:YES];
-}
-
- (CGRect)keyboardRect {
CGRect keyboardRect = [self convertRect:_keyboardRect fromView:nil];
if ( keyboardRect.origin.y == 0 ) {
@@ -186,4 +266,21 @@ - (CGRect)keyboardRect {
return keyboardRect;
}
+- (void)initializeView:(UIView*)view {
+ if ( ([view isKindOfClass:[UITextField class]] || [view isKindOfClass:[UITextView class]]) && (![(id)view delegate] || [(id)view delegate] == self) ) {
+ [(id)view setDelegate:self];
+
+ if ( [view isKindOfClass:[UITextField class]] ) {
+ UIView *otherView = nil;
+ CGFloat minY = CGFLOAT_MAX;
+ [self findTextFieldAfterTextField:view beneathView:self minY:&minY foundView:&otherView];
+ if ( otherView ) {
+ ((UITextField*)view).returnKeyType = UIReturnKeyNext;
+ } else {
+ ((UITextField*)view).returnKeyType = UIReturnKeyDone;
+ }
+ }
+ }
+}
+
@end
View
11 TPKeyboardAvoidingTableView.h
@@ -7,12 +7,7 @@
#import <UIKit/UIKit.h>
-@interface TPKeyboardAvoidingTableView : UITableView {
- UIEdgeInsets _priorInset;
- BOOL _priorInsetSaved;
- BOOL _keyboardVisible;
- CGRect _keyboardRect;
-}
-
-- (void)adjustOffsetToIdealIfNeeded;
+@interface TPKeyboardAvoidingTableView : UITableView
+- (BOOL)focusNextTextField;
+- (void)scrollToActiveTextField;
@end
View
157 TPKeyboardAvoidingTableView.m
@@ -9,7 +9,14 @@
#define _UIKeyboardFrameEndUserInfoKey (&UIKeyboardFrameEndUserInfoKey != NULL ? UIKeyboardFrameEndUserInfoKey : @"UIKeyboardBoundsUserInfoKey")
-@interface TPKeyboardAvoidingTableView ()
+@interface TPKeyboardAvoidingTableView () <UITextFieldDelegate, UITextViewDelegate> {
+ UIEdgeInsets _priorInset;
+ BOOL _priorInsetSaved;
+ BOOL _keyboardVisible;
+ CGRect _keyboardRect;
+ CGSize _originalContentSize;
+ CGPoint _originalContentOffset;
+}
- (UIView*)findFirstResponderBeneathView:(UIView*)view;
- (UIEdgeInsets)contentInsetForKeyboard;
- (CGFloat)idealOffsetForView:(UIView *)view withSpace:(CGFloat)space;
@@ -18,7 +25,10 @@ - (CGRect)keyboardRect;
@implementation TPKeyboardAvoidingTableView
+#pragma mark - Setup/Teardown
+
- (void)setup {
+ _priorInsetSaved = NO;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
@@ -29,43 +39,48 @@ -(id)initWithFrame:(CGRect)frame {
return self;
}
--(id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
- if ( !(self = [super initWithFrame:frame style:style]) ) return nil;
+-(void)awakeFromNib {
[self setup];
- return self;
-}
-
--(id)initWithCoder:(NSCoder *)aDecoder {
- if ( !(self = [super initWithCoder:aDecoder]) ) return nil;
- [self setup];
- return self;
}
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
-#if !__has_feature(objc_arc)
+#if !__has_feature(objc_arc)
[super dealloc];
#endif
}
-(void)setFrame:(CGRect)frame {
[super setFrame:frame];
+
+ CGSize contentSize = _originalContentSize;
+ contentSize.width = MAX(contentSize.width, self.frame.size.width);
+ contentSize.height = MAX(contentSize.height, self.frame.size.height);
+ [super setContentSize:contentSize];
+
if ( _keyboardVisible ) {
self.contentInset = [self contentInsetForKeyboard];
}
}
-(void)setContentSize:(CGSize)contentSize {
+ _originalContentSize = contentSize;
+
+ contentSize.width = MAX(contentSize.width, self.frame.size.width);
+ contentSize.height = MAX(contentSize.height, self.frame.size.height);
[super setContentSize:contentSize];
+
if ( _keyboardVisible ) {
self.contentInset = [self contentInsetForKeyboard];
}
}
+#pragma mark - Responders, events
+
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[[self findFirstResponderBeneathView:self] resignFirstResponder];
[super touchesEnded:touches withEvent:event];
-}
+}
- (void)keyboardWillShow:(NSNotification*)notification {
_keyboardRect = [[[notification userInfo] objectForKey:_UIKeyboardFrameEndUserInfoKey] CGRectValue];
@@ -77,6 +92,8 @@ - (void)keyboardWillShow:(NSNotification*)notification {
return;
}
+ _originalContentOffset = self.contentOffset;
+
if (!_priorInsetSaved) {
_priorInset = self.contentInset;
_priorInsetSaved = YES;
@@ -88,8 +105,8 @@ - (void)keyboardWillShow:(NSNotification*)notification {
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]];
self.contentInset = [self contentInsetForKeyboard];
- [self setContentOffset:CGPointMake(self.contentOffset.x,
- [self idealOffsetForView:firstResponder withSpace:[self keyboardRect].origin.y - self.bounds.origin.y])
+ [self setContentOffset:CGPointMake(self.contentOffset.x,
+ [self idealOffsetForView:firstResponder withSpace:[self keyboardRect].origin.y - self.bounds.origin.y])
animated:YES];
[self setScrollIndicatorInsets:self.contentInset];
@@ -105,11 +122,66 @@ - (void)keyboardWillHide:(NSNotification*)notification {
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]];
self.contentInset = _priorInset;
+ self.contentOffset = _originalContentOffset;
[self setScrollIndicatorInsets:self.contentInset];
_priorInsetSaved = NO;
[UIView commitAnimations];
}
+-(BOOL)textFieldShouldReturn:(UITextField *)textField {
+ if ( ![self focusNextTextField] ) {
+ [textField resignFirstResponder];
+ }
+ return YES;
+}
+
+-(void)textFieldDidBeginEditing:(UITextField *)textField {
+ [self scrollToActiveTextField];
+}
+
+-(void)textViewDidBeginEditing:(UITextView *)textView {
+ [self scrollToActiveTextField];
+}
+
+-(void)layoutSubviews {
+ [super layoutSubviews];
+ [self initializeViewsBeneathView:self];
+}
+
+#pragma mark - Utilities
+
+- (BOOL)focusNextTextField {
+ UIView *firstResponder = [self findFirstResponderBeneathView:self];
+ if ( !firstResponder ) {
+ return NO;
+ }
+
+ CGFloat minY = CGFLOAT_MAX;
+ UIView *view = nil;
+ [self findTextFieldAfterTextField:firstResponder beneathView:self minY:&minY foundView:&view];
+
+ if ( view ) {
+ [view becomeFirstResponder];
+ return YES;
+ }
+
+ return NO;
+}
+
+-(void)scrollToActiveTextField {
+ if ( !_keyboardVisible ) return;
+
+ CGFloat visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom;
+
+ CGPoint idealOffset = CGPointMake(0, [self idealOffsetForView:[self findFirstResponderBeneathView:self] withSpace:visibleSpace]);
+
+ [self setContentOffset:idealOffset animated:YES];
+
+ _originalContentOffset = self.contentOffset;
+}
+
+#pragma mark - Helpers
+
- (UIView*)findFirstResponderBeneathView:(UIView*)view {
// Search recursively for first responder
for ( UIView *childView in view.subviews ) {
@@ -120,6 +192,33 @@ - (UIView*)findFirstResponderBeneathView:(UIView*)view {
return nil;
}
+- (void)findTextFieldAfterTextField:(UIView*)priorTextField beneathView:(UIView*)view minY:(CGFloat*)minY foundView:(UIView**)foundView {
+ // Search recursively for text field or text view below priorTextField
+ CGFloat priorFieldOffset = CGRectGetMinY([self convertRect:priorTextField.frame fromView:priorTextField.superview]);
+ for ( UIView *childView in view.subviews ) {
+ if ( childView.hidden ) continue;
+ if ( ([childView isKindOfClass:[UITextField class]] || [childView isKindOfClass:[UITextView class]]) ) {
+ CGRect frame = [self convertRect:childView.frame fromView:view];
+ if ( childView != priorTextField && CGRectGetMinY(frame) >= priorFieldOffset && CGRectGetMinY(frame) < *minY ) {
+ *minY = CGRectGetMinY(frame);
+ *foundView = childView;
+ }
+ } else {
+ [self findTextFieldAfterTextField:priorTextField beneathView:childView minY:minY foundView:foundView];
+ }
+ }
+}
+
+- (void)initializeViewsBeneathView:(UIView*)view {
+ for ( UIView *childView in view.subviews ) {
+ if ( ([childView isKindOfClass:[UITextField class]] || [childView isKindOfClass:[UITextView class]]) ) {
+ [self initializeView:childView];
+ } else {
+ [self initializeViewsBeneathView:childView];
+ }
+ }
+}
+
- (UIEdgeInsets)contentInsetForKeyboard {
UIEdgeInsets newInset = self.contentInset;
CGRect keyboardRect = [self keyboardRect];
@@ -155,18 +254,6 @@ -(CGFloat)idealOffsetForView:(UIView *)view withSpace:(CGFloat)space {
return offset;
}
--(void)adjustOffsetToIdealIfNeeded {
-
- // Only do this if the keyboard is already visible
- if ( !_keyboardVisible ) return;
-
- CGFloat visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom;
-
- CGPoint idealOffset = CGPointMake(0, [self idealOffsetForView:[self findFirstResponderBeneathView:self] withSpace:visibleSpace]);
-
- [self setContentOffset:idealOffset animated:YES];
-}
-
- (CGRect)keyboardRect {
CGRect keyboardRect = [self convertRect:_keyboardRect fromView:nil];
if ( keyboardRect.origin.y == 0 ) {
@@ -176,4 +263,22 @@ - (CGRect)keyboardRect {
return keyboardRect;
}
+- (void)initializeView:(UIView*)view {
+ if ( ([view isKindOfClass:[UITextField class]] || [view isKindOfClass:[UITextView class]]) && (![(id)view delegate] || [(id)view delegate] == self) ) {
+ [(id)view setDelegate:self];
+
+ if ( [view isKindOfClass:[UITextField class]] ) {
+ UIView *otherView = nil;
+ CGFloat minY = CGFLOAT_MAX;
+ [self findTextFieldAfterTextField:view beneathView:self minY:&minY foundView:&otherView];
+
+ if ( otherView ) {
+ ((UITextField*)view).returnKeyType = UIReturnKeyNext;
+ } else {
+ ((UITextField*)view).returnKeyType = UIReturnKeyDone;
+ }
+ }
+ }
+}
+
@end
Please sign in to comment.
Something went wrong with that request. Please try again.