Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 33d145f6b7
Fetching contributors…

Cannot retrieve contributors at this time

180 lines (140 sloc) 6.461 kB
//
// TPKeyboardAvoidingTableView.m
//
// Created by Michael Tyson on 11/04/2011.
// Copyright 2011 A Tasty Pixel. All rights reserved.
//
#import "TPKeyboardAvoidingTableView.h"
#define _UIKeyboardFrameEndUserInfoKey (&UIKeyboardFrameEndUserInfoKey != NULL ? UIKeyboardFrameEndUserInfoKey : @"UIKeyboardBoundsUserInfoKey")
@interface TPKeyboardAvoidingTableView ()
- (UIView*)findFirstResponderBeneathView:(UIView*)view;
- (UIEdgeInsets)contentInsetForKeyboard;
- (CGFloat)idealOffsetForView:(UIView *)view withSpace:(CGFloat)space;
- (CGRect)keyboardRect;
@end
@implementation TPKeyboardAvoidingTableView
- (void)setup {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
-(id)initWithFrame:(CGRect)frame {
if ( !(self = [super initWithFrame:frame]) ) return nil;
if ( [[[UIDevice currentDevice] systemVersion] floatValue] >= 4.3 ) {
// Not required above iOS 4.3! Just return an ordinary table view
[self release];
return (self = [[UITableView alloc] initWithFrame:frame]);
}
[self setup];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
if ( !(self = [super initWithCoder:aDecoder]) ) return nil;
if ( [[[UIDevice currentDevice] systemVersion] floatValue] >= 4.3 ) {
// Not required above iOS 4.3! Just return an ordinary table view
[self release];
return (self = [[UITableView alloc] initWithCoder:aDecoder]);
}
[self setup];
return self;
}
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
-(void)setFrame:(CGRect)frame {
[super setFrame:frame];
if ( _keyboardVisible ) {
self.contentInset = [self contentInsetForKeyboard];
}
}
-(void)setContentSize:(CGSize)contentSize {
[super setContentSize:contentSize];
if ( _keyboardVisible ) {
self.contentInset = [self contentInsetForKeyboard];
}
}
- (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];
_keyboardVisible = YES;
UIView *firstResponder = [self findFirstResponderBeneathView:self];
if ( !firstResponder ) {
// No child view is the first responder - nothing to do here
return;
}
_priorInset = self.contentInset;
// Shrink view's inset by the keyboard's height, and scroll to show the text field/view being edited
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[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])
animated:YES];
[UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification*)notification {
_keyboardRect = CGRectZero;
_keyboardVisible = NO;
// Restore dimensions to prior size
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]];
self.contentInset = _priorInset;
[UIView commitAnimations];
}
- (UIView*)findFirstResponderBeneathView:(UIView*)view {
// Search recursively for first responder
for ( UIView *childView in view.subviews ) {
if ( [childView respondsToSelector:@selector(isFirstResponder)] && [childView isFirstResponder] ) return childView;
UIView *result = [self findFirstResponderBeneathView:childView];
if ( result ) return result;
}
return nil;
}
- (UIEdgeInsets)contentInsetForKeyboard {
UIEdgeInsets newInset = self.contentInset;
CGRect keyboardRect = [self keyboardRect];
newInset.bottom = keyboardRect.size.height - ((keyboardRect.origin.y+keyboardRect.size.height) - (self.bounds.origin.y+self.bounds.size.height));
return newInset;
}
-(CGFloat)idealOffsetForView:(UIView *)view withSpace:(CGFloat)space {
// Convert the rect to get the view's distance from the top of the scrollView.
CGRect rect = [view convertRect:view.bounds toView:self];
// Set starting offset to that point
CGFloat offset = rect.origin.y;
if ( self.contentSize.height - offset < space ) {
// Scroll to the bottom
offset = self.contentSize.height - space;
} else {
if ( view.bounds.size.height < space ) {
// Center vertically if there's room
offset -= floor((space-view.bounds.size.height)/2.0);
}
if ( offset + space > self.contentSize.height ) {
// Clamp to content size
offset = self.contentSize.height - space;
}
}
if (offset < 0) offset = 0;
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 ) {
CGRect screenBounds = [self convertRect:[UIScreen mainScreen].bounds fromView:nil];
keyboardRect.origin = CGPointMake(0, screenBounds.size.height - keyboardRect.size.height);
}
return keyboardRect;
}
@end
Jump to Line
Something went wrong with that request. Please try again.