Skip to content

Instantly share code, notes, and snippets.

@dustin-graham
Created November 7, 2011 17:50
Show Gist options
  • Save dustin-graham/1345647 to your computer and use it in GitHub Desktop.
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
//
// 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
//
// 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