iphone storyboard
Change-Id: I08ffe80fc228997b8a6618248116b52506100a79
This commit is contained in:
Binary file not shown.
BIN
ios/iosremote/iosremote/.DS_Store
vendored
BIN
ios/iosremote/iosremote/.DS_Store
vendored
Binary file not shown.
@@ -1,64 +0,0 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "MyListController.h"
|
||||
|
||||
@class Book;
|
||||
@class EditableTableViewCell;
|
||||
|
||||
// Constants representing the book's fields.
|
||||
//
|
||||
enum {
|
||||
BookTitle,
|
||||
BookLastName,
|
||||
BookFirstName,
|
||||
BookYear,
|
||||
BookImagePath
|
||||
};
|
||||
|
||||
// Constants representing the various sections of our grouped table view.
|
||||
//
|
||||
enum {
|
||||
TitleSection,
|
||||
AuthorSection,
|
||||
YearSection,
|
||||
ImageSection
|
||||
};
|
||||
|
||||
typedef NSUInteger BookAttribute;
|
||||
|
||||
@interface MyDetailController : UITableViewController <UITextFieldDelegate>
|
||||
{
|
||||
Book *_book;
|
||||
MyListController *_listController;
|
||||
EditableDetailCell *_titleCell;
|
||||
EditableDetailCell *_firstNameCell;
|
||||
EditableDetailCell *_lastNameCell;
|
||||
EditableDetailCell *_yearCell;
|
||||
EditableDetailCell *_imagePathCell;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) Book *book;
|
||||
|
||||
// Watch out for retain cycles (objects that retain each other can never
|
||||
// be deallocated). If the list controller were to retain the detail controller,
|
||||
// we should change 'retain' to 'assign' below to avoid a cycle. (Note that in
|
||||
// that case, dealloc shouldn't release the list controller.)
|
||||
//
|
||||
@property (nonatomic, retain) MyListController *listController;
|
||||
|
||||
@property (nonatomic, retain) EditableDetailCell *titleCell;
|
||||
@property (nonatomic, retain) EditableDetailCell *firstNameCell;
|
||||
@property (nonatomic, retain) EditableDetailCell *lastNameCell;
|
||||
@property (nonatomic, retain) EditableDetailCell *yearCell;
|
||||
@property (nonatomic, retain) EditableDetailCell *imagePathCell;
|
||||
|
||||
- (BOOL)isModal;
|
||||
|
||||
- (EditableDetailCell *)newDetailCellWithTag:(NSInteger)tag;
|
||||
|
||||
// Action Methods
|
||||
//
|
||||
- (void)save;
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
@@ -1,14 +0,0 @@
|
||||
//
|
||||
// TPKeyboardAvoidingScrollView.h
|
||||
//
|
||||
// Created by Michael Tyson on 11/04/2011.
|
||||
// Copyright 2011 A Tasty Pixel. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TPKeyboardAvoidingScrollView : UIScrollView
|
||||
@property (assign, nonatomic) BOOL autoAdjustsContentSizeToBounds;
|
||||
- (BOOL)focusNextTextField;
|
||||
- (void)scrollToActiveTextField;
|
||||
@end
|
@@ -1,285 +0,0 @@
|
||||
//
|
||||
// TPKeyboardAvoidingScrollView.m
|
||||
//
|
||||
// Created by Michael Tyson on 11/04/2011.
|
||||
// Copyright 2011 A Tasty Pixel. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TPKeyboardAvoidingScrollView.h"
|
||||
|
||||
#define _UIKeyboardFrameEndUserInfoKey (&UIKeyboardFrameEndUserInfoKey != NULL ? UIKeyboardFrameEndUserInfoKey : @"UIKeyboardBoundsUserInfoKey")
|
||||
|
||||
@interface TPKeyboardAvoidingScrollView () <UITextFieldDelegate, UITextViewDelegate> {
|
||||
UIEdgeInsets _priorInset;
|
||||
BOOL _priorInsetSaved;
|
||||
BOOL _keyboardVisible;
|
||||
CGRect _keyboardRect;
|
||||
CGSize _originalContentSize;
|
||||
}
|
||||
- (UIView*)findFirstResponderBeneathView:(UIView*)view;
|
||||
- (UIEdgeInsets)contentInsetForKeyboard;
|
||||
- (CGFloat)idealOffsetForView:(UIView *)view withSpace:(CGFloat)space;
|
||||
- (CGRect)keyboardRect;
|
||||
@end
|
||||
|
||||
@implementation TPKeyboardAvoidingScrollView
|
||||
|
||||
#pragma mark - Setup/Teardown
|
||||
|
||||
- (void)setup {
|
||||
_priorInsetSaved = NO;
|
||||
if ( CGSizeEqualToSize(self.contentSize, CGSizeZero) ) {
|
||||
self.contentSize = self.bounds.size;
|
||||
}
|
||||
[[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;
|
||||
[self setup];
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)awakeFromNib {
|
||||
[self setup];
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
#if !__has_feature(objc_arc)
|
||||
[super dealloc];
|
||||
#endif
|
||||
}
|
||||
|
||||
-(void)setFrame:(CGRect)frame {
|
||||
[super setFrame:frame];
|
||||
|
||||
[self handleContentSize];
|
||||
}
|
||||
|
||||
-(void)setContentSize:(CGSize)contentSize {
|
||||
_originalContentSize = contentSize;
|
||||
|
||||
[self handleContentSize];
|
||||
}
|
||||
|
||||
-(void)setAutoAdjustsContentSizeToBounds:(BOOL)autoAdjustsContentSizeToBounds {
|
||||
_autoAdjustsContentSizeToBounds = autoAdjustsContentSizeToBounds;
|
||||
|
||||
[self handleContentSize];
|
||||
}
|
||||
|
||||
#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];
|
||||
_keyboardVisible = YES;
|
||||
|
||||
UIView *firstResponder = [self findFirstResponderBeneathView:self];
|
||||
if ( !firstResponder ) {
|
||||
// No child view is the first responder - nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_priorInsetSaved) {
|
||||
_priorInset = self.contentInset;
|
||||
_priorInsetSaved = YES;
|
||||
}
|
||||
|
||||
// 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];
|
||||
[self setScrollIndicatorInsets:self.contentInset];
|
||||
|
||||
[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;
|
||||
[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];
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
-(void)handleContentSize {
|
||||
CGSize contentSize = _autoAdjustsContentSizeToBounds ? self.bounds.size : _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];
|
||||
}
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
- (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];
|
||||
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;
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
- (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
|
@@ -1,13 +0,0 @@
|
||||
//
|
||||
// TPKeyboardAvoidingTableView.h
|
||||
//
|
||||
// Created by Michael Tyson on 11/04/2011.
|
||||
// Copyright 2011 A Tasty Pixel. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TPKeyboardAvoidingTableView : UITableView
|
||||
- (BOOL)focusNextTextField;
|
||||
- (void)scrollToActiveTextField;
|
||||
@end
|
@@ -1,284 +0,0 @@
|
||||
//
|
||||
// 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 () <UITextFieldDelegate, UITextViewDelegate> {
|
||||
UIEdgeInsets _priorInset;
|
||||
BOOL _priorInsetSaved;
|
||||
BOOL _keyboardVisible;
|
||||
CGRect _keyboardRect;
|
||||
CGSize _originalContentSize;
|
||||
}
|
||||
- (UIView*)findFirstResponderBeneathView:(UIView*)view;
|
||||
- (UIEdgeInsets)contentInsetForKeyboard;
|
||||
- (CGFloat)idealOffsetForView:(UIView *)view withSpace:(CGFloat)space;
|
||||
- (CGRect)keyboardRect;
|
||||
@end
|
||||
|
||||
@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];
|
||||
}
|
||||
|
||||
-(id)initWithFrame:(CGRect)frame {
|
||||
if ( !(self = [super initWithFrame:frame]) ) return nil;
|
||||
[self setup];
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id)initWithFrame:(CGRect)frame style:(UITableViewStyle)withStyle {
|
||||
if ( !(self = [super initWithFrame:frame style:withStyle]) ) return nil;
|
||||
[self setup];
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)awakeFromNib {
|
||||
[self setup];
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
#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];
|
||||
_keyboardVisible = YES;
|
||||
|
||||
UIView *firstResponder = [self findFirstResponderBeneathView:self];
|
||||
if ( !firstResponder ) {
|
||||
// No child view is the first responder - nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_priorInsetSaved) {
|
||||
_priorInset = self.contentInset;
|
||||
_priorInsetSaved = YES;
|
||||
}
|
||||
|
||||
// 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];
|
||||
[self setScrollIndicatorInsets:self.contentInset];
|
||||
|
||||
[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;
|
||||
[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];
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
- (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];
|
||||
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;
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
- (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
|
Reference in New Issue
Block a user