Created
November 7, 2011 17:50
-
-
Save dustin-graham/1345647 to your computer and use it in GitHub Desktop.
Automated iOS form component for tabbing through text fields and auto scrolling/sizing scroll views
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// OrderedForm.h | |
// KSLClassifieds | |
// | |
// Created by Dustin Graham on 11/4/11. | |
// Copyright (c) 2011 __MyCompanyName__. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "StandardInputAccessory.h" | |
@interface OrderedForm : NSObject <UITextFieldDelegate, UITextViewDelegate, StandardInputAccessoryDelegate>{ | |
float _scrollFrameHeight; | |
BOOL _keyboardShown; | |
} | |
@property (nonatomic, strong) NSMutableArray *orderedResponders; | |
@property (nonatomic, strong) UIScrollView *scrollView; | |
@property (nonatomic, strong) StandardInputAccessory *inputAccessory; | |
@property(nonatomic, strong) id activeField; | |
- (id)initWithOrderedResponders:(NSArray *)responders scrollView:(UIScrollView *)formContainer; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// OrderedForm.m | |
// KSLClassifieds | |
// | |
// Created by Dustin Graham on 11/4/11. | |
// Copyright (c) 2011 __MyCompanyName__. All rights reserved. | |
// | |
#import "OrderedForm.h" | |
#define KEYBOARD_FIELD_PADDING 25.0f | |
@interface OrderedForm () | |
- (void)registerForKeyboardNotifications; | |
- (void)scrollToActiveField; | |
@end | |
@implementation OrderedForm | |
@synthesize orderedResponders, inputAccessory; | |
@synthesize activeField = _activeField; | |
@synthesize scrollView = _scrollView; | |
- (id)initWithOrderedResponders:(NSArray *)responders scrollView:(UIScrollView *)formContainer { | |
self = [super init]; | |
if (self) { | |
// custom initialization | |
self.orderedResponders = [[NSMutableArray alloc] init]; | |
inputAccessory = [[StandardInputAccessory alloc] init]; | |
inputAccessory.delegate = self; | |
for (id formElement in responders) { | |
if ([formElement conformsToProtocol:@protocol(UITextInput)] || [formElement conformsToProtocol:@protocol(UITextInputTraits)]) { | |
[formElement setInputAccessoryView:inputAccessory]; | |
[formElement performSelector:@selector(setDelegate:) withObject:self]; | |
[self.orderedResponders addObject:formElement]; | |
} | |
} | |
self.scrollView = formContainer; | |
[self registerForKeyboardNotifications]; | |
} | |
return self; | |
} | |
- (void)dealloc { | |
[[NSNotificationCenter defaultCenter] removeObserver:self]; | |
} | |
- (void)setActiveField:(id)activeField { | |
_activeField = activeField; | |
int index = [self.orderedResponders indexOfObject:activeField]; | |
if (index == 0) { | |
[inputAccessory hasPrevious:NO]; | |
[inputAccessory hasNext:YES]; | |
} else if (index == [self.orderedResponders count] - 1) { | |
[inputAccessory hasPrevious:YES]; | |
[inputAccessory hasNext:NO]; | |
} else { | |
[inputAccessory hasPrevious:YES]; | |
[inputAccessory hasNext:YES]; | |
} | |
} | |
#pragma mark - first responder methods | |
- (void)enableNextResponder { | |
if (self.activeField) { | |
NSInteger indexOfActiveField = [self.orderedResponders indexOfObject:self.activeField]; | |
indexOfActiveField++; | |
UITextField *nextResponder = [self.orderedResponders objectAtIndex:indexOfActiveField]; | |
[nextResponder becomeFirstResponder]; | |
[self scrollToActiveField]; | |
} | |
} | |
- (void)enablePrevResponder { | |
if (self.activeField) { | |
NSInteger indexOfActiveField = [self.orderedResponders indexOfObject:self.activeField]; | |
indexOfActiveField--; | |
UITextField *previousResponder = [self.orderedResponders objectAtIndex:indexOfActiveField]; | |
[previousResponder becomeFirstResponder]; | |
[self scrollToActiveField]; | |
} | |
} | |
#pragma mark - UITextFieldDelegate methods | |
- (void)textFieldDidBeginEditing:(UITextField *)textField { | |
self.activeField = textField; | |
} | |
- (void)textFieldDidEndEditing:(UITextField *)textField { | |
self.activeField = nil; | |
} | |
#pragma mark - UITextViewDelegate methods | |
- (void)textViewDidBeginEditing:(UITextView *)textView { | |
self.activeField = textView; | |
} | |
- (void)textViewDidEndEditing:(UITextView *)textView { | |
self.activeField = nil; | |
} | |
- (void)inputAccessory:(StandardInputAccessory *)accessory pushedButton:(int)which { | |
if (which == PREVIOUS_BUTTON) { | |
[self enablePrevResponder]; | |
} else if (which == NEXT_BUTTON) { | |
[self enableNextResponder]; | |
} else { | |
[self.orderedResponders makeObjectsPerformSelector:@selector(resignFirstResponder)]; | |
} | |
} | |
#pragma mark - ScrollView and Keyboard management | |
- (void)scrollToActiveField { | |
if (self.activeField) { | |
CGPoint point = ((UIView *) _activeField).frame.origin; | |
point = [((UIView *) _activeField) convertPoint:point toView:_scrollView]; | |
CGPoint lowerPoint = CGPointMake(point.x, point.y + ((UIView *) _activeField).frame.size.height); | |
CGPoint upperPoint = point; | |
if (!([_scrollView pointInside:upperPoint withEvent:nil] && [_scrollView pointInside:lowerPoint withEvent:nil])) { | |
point.x = 0; | |
if (_activeField == [self.orderedResponders objectAtIndex:0]) { | |
point.y = 0; | |
} else { | |
if (((UIView *) _activeField).frame.size.height + 25.0f < _scrollView.frame.size.height) { | |
point.y -= _scrollView.frame.size.height - ((UIView *) _activeField).frame.size.height - 25.0f; | |
} | |
if (point.y > _scrollView.contentSize.height - _scrollView.bounds.size.height) | |
point.y = _scrollView.contentSize.height - _scrollView.bounds.size.height; | |
} | |
[_scrollView setContentOffset:point animated: YES]; | |
} | |
} | |
} | |
- (void)registerForKeyboardNotifications { | |
[[NSNotificationCenter defaultCenter] addObserver:self | |
selector:@selector(keyboardWasShown:) | |
name:UIKeyboardDidShowNotification object:nil]; | |
[[NSNotificationCenter defaultCenter] addObserver:self | |
selector:@selector(keyboardWillHide:) | |
name:UIKeyboardWillHideNotification object:nil]; | |
} | |
- (void)keyboardWasShown:(NSNotification *)notification { | |
if (_keyboardShown) | |
return; | |
CGFloat keyboardOriginY = [[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y - 44.0f - 20.0f; | |
// TODO: Why does 20 work here? | |
// Resize the scroll view | |
CGRect scrollFrame = _scrollView.frame; | |
// Save the original height of the scroll view so we can restore it when the keyboard is hidden | |
_scrollFrameHeight = scrollFrame.size.height; | |
scrollFrame.size.height = keyboardOriginY; | |
_scrollView.frame = scrollFrame; | |
[self scrollToActiveField]; | |
_keyboardShown = YES; | |
} | |
- (void)keyboardWillHide:(NSNotification *)notification { | |
NSTimeInterval animationDuration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; | |
// Reset the height of the scroll view to its original value | |
[UIView animateWithDuration:animationDuration animations:^{ | |
CGRect scrollFrame = _scrollView.frame; | |
scrollFrame.size.height = _scrollFrameHeight; | |
_scrollView.frame = scrollFrame; | |
}]; | |
_keyboardShown = NO; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment