From 4a1cd25a6f0a62bf0b40e6db6e4028c0f2e7c9f1 Mon Sep 17 00:00:00 2001 From: Antonio Scardigno Date: Wed, 15 Jan 2020 15:46:54 +0100 Subject: [PATCH] added: * possibility to set custom delegate * possibility to select previous/next textfield from custom button with notification --- TPKeyboardAvoiding/TPKeyboardAvoidingAction.h | 15 ++++ TPKeyboardAvoiding/TPKeyboardAvoidingAction.m | 12 +++ .../TPKeyboardAvoidingCollectionView.h | 0 .../TPKeyboardAvoidingCollectionView.m | 64 +++++++++++++- .../TPKeyboardAvoidingScrollView.m | 64 +++++++++++++- .../TPKeyboardAvoidingTableView.h | 0 .../TPKeyboardAvoidingTableView.m | 80 ++++++++++++++++-- ...UIScrollView+TPKeyboardAvoidingAdditions.h | 7 +- ...UIScrollView+TPKeyboardAvoidingAdditions.m | 83 ++++++++++++++++++- 9 files changed, 308 insertions(+), 17 deletions(-) create mode 100644 TPKeyboardAvoiding/TPKeyboardAvoidingAction.h create mode 100644 TPKeyboardAvoiding/TPKeyboardAvoidingAction.m mode change 100644 => 100755 TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.h mode change 100644 => 100755 TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m mode change 100644 => 100755 TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m mode change 100644 => 100755 TPKeyboardAvoiding/TPKeyboardAvoidingTableView.h mode change 100644 => 100755 TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m mode change 100644 => 100755 TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h mode change 100644 => 100755 TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m diff --git a/TPKeyboardAvoiding/TPKeyboardAvoidingAction.h b/TPKeyboardAvoiding/TPKeyboardAvoidingAction.h new file mode 100644 index 00000000..ddbfe9d8 --- /dev/null +++ b/TPKeyboardAvoiding/TPKeyboardAvoidingAction.h @@ -0,0 +1,15 @@ +// +// TPKeyboardAvoidingAction.h +// postepay-Simulatore +// +// Created by Antonio Scardigno on 10/01/2020. +// Copyright © 2020 Poste Italiane SPA. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +typedef NSString *TPKeyboardAvoidingAction NS_TYPED_ENUM; +extern TPKeyboardAvoidingAction const TPKeyboardAvoidingActionNextTextField; +extern TPKeyboardAvoidingAction const TPKeyboardAvoidingActionPrevTextField; + +NS_ASSUME_NONNULL_END diff --git a/TPKeyboardAvoiding/TPKeyboardAvoidingAction.m b/TPKeyboardAvoiding/TPKeyboardAvoidingAction.m new file mode 100644 index 00000000..7924509f --- /dev/null +++ b/TPKeyboardAvoiding/TPKeyboardAvoidingAction.m @@ -0,0 +1,12 @@ +// +// TPKeyboardAvoidingAction.m +// postepay-Simulatore +// +// Created by Antonio Scardigno on 10/01/2020. +// Copyright © 2020 Poste Italiane SPA. All rights reserved. +// + +#import "TPKeyboardAvoidingAction.h" + +TPKeyboardAvoidingAction const TPKeyboardAvoidingActionNextTextField = @"TPKeyboardAvoidingActionNextTextField"; +TPKeyboardAvoidingAction const TPKeyboardAvoidingActionPrevTextField = @"TPKeyboardAvoidingActionPrevTextField"; diff --git a/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.h b/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.h old mode 100644 new mode 100755 diff --git a/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m b/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m old mode 100644 new mode 100755 index 24755127..c913914a --- a/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m +++ b/TPKeyboardAvoiding/TPKeyboardAvoidingCollectionView.m @@ -97,17 +97,73 @@ - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; } +-(void)layoutSubviews { + [super layoutSubviews]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; + [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +} + +#pragma mark - UITextField delegate methods + -(BOOL)textFieldShouldReturn:(UITextField *)textField { if ( ![self focusNextTextField] ) { [textField resignFirstResponder]; } + + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldReturn:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldReturn:textField]; + } + return YES; } --(void)layoutSubviews { - [super layoutSubviews]; - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; - [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +-(void)textFieldDidBeginEditing:(UITextField *)textField { + if ([[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldDidBeginEditing:)]) { + [[[self textFieldDelegates] objectForKey:textField] textFieldDidBeginEditing:textField]; + } +} + +-(void)textFieldDidEndEditing:(UITextField *)textField { + if ([[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldDidEndEditing:)]) { + [[[self textFieldDelegates] objectForKey:textField] textFieldDidEndEditing:textField]; + } +} + +-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldBeginEditing:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldBeginEditing:textField]; + } + + return YES; +} + +-(BOOL)textFieldShouldEndEditing:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldEndEditing:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldEndEditing:textField]; + } + + return YES; +} + +-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textField:textField shouldChangeCharactersInRange:range replacementString:string]; + } + + return YES; +} + +-(BOOL)textFieldShouldClear:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldClear:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldClear:textField]; + } + + return YES; } @end diff --git a/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m b/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m old mode 100644 new mode 100755 index e8113918..3274a17b --- a/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m +++ b/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m @@ -76,17 +76,73 @@ - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; } +-(void)layoutSubviews { + [super layoutSubviews]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; + [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +} + +#pragma mark - UITextField delegate methods + -(BOOL)textFieldShouldReturn:(UITextField *)textField { if ( ![self focusNextTextField] ) { [textField resignFirstResponder]; } + + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldReturn:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldReturn:textField]; + } + return YES; } --(void)layoutSubviews { - [super layoutSubviews]; - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; - [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +-(void)textFieldDidBeginEditing:(UITextField *)textField { + if ([[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldDidBeginEditing:)]) { + [[[self textFieldDelegates] objectForKey:textField] textFieldDidBeginEditing:textField]; + } +} + +-(void)textFieldDidEndEditing:(UITextField *)textField { + if ([[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldDidEndEditing:)]) { + [[[self textFieldDelegates] objectForKey:textField] textFieldDidEndEditing:textField]; + } +} + +-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldBeginEditing:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldBeginEditing:textField]; + } + + return YES; +} + +-(BOOL)textFieldShouldEndEditing:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldEndEditing:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldEndEditing:textField]; + } + + return YES; +} + +-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textField:textField shouldChangeCharactersInRange:range replacementString:string]; + } + + return YES; +} + +-(BOOL)textFieldShouldClear:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldClear:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldClear:textField]; + } + + return YES; } @end diff --git a/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.h b/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.h old mode 100644 new mode 100755 diff --git a/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m b/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m old mode 100644 new mode 100755 index 7eefa234..1d8863ae --- a/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m +++ b/TPKeyboardAvoiding/TPKeyboardAvoidingTableView.m @@ -22,6 +22,8 @@ - (void)setup { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(TPKeyboardAvoiding_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToActiveTextField) name:UITextViewTextDidBeginEditingNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToActiveTextField) name:UITextFieldTextDidBeginEditingNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(goToNextTextField) name:TPKeyboardAvoidingActionNextTextField object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(goToPrevTextField) name:TPKeyboardAvoidingActionPrevTextField object:nil]; } -(id)initWithFrame:(CGRect)frame { @@ -81,14 +83,26 @@ -(void)setContentSize:(CGSize)contentSize { - (BOOL)focusNextTextField { return [self TPKeyboardAvoiding_focusNextTextField]; - } + +- (BOOL)focusPrevTextField { + return [self TPKeyboardAvoiding_focusPrevTextField]; +} + - (void)scrollToActiveTextField { return [self TPKeyboardAvoiding_scrollToActiveTextField]; } #pragma mark - Responders, events +-(void) goToNextTextField { + [self focusNextTextField]; +} + +-(void) goToPrevTextField { + [self focusPrevTextField]; +} + -(void)willMoveToSuperview:(UIView *)newSuperview { [super willMoveToSuperview:newSuperview]; if ( !newSuperview ) { @@ -101,17 +115,73 @@ - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; } +-(void)layoutSubviews { + [super layoutSubviews]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; + [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +} + +#pragma mark - UITextField delegate methods + -(BOOL)textFieldShouldReturn:(UITextField *)textField { if ( ![self focusNextTextField] ) { [textField resignFirstResponder]; } + + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldReturn:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldReturn:textField]; + } + return YES; } --(void)layoutSubviews { - [super layoutSubviews]; - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) object:self]; - [self performSelector:@selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:) withObject:self afterDelay:0.1]; +-(void)textFieldDidBeginEditing:(UITextField *)textField { + if ([[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldDidBeginEditing:)]) { + [[[self textFieldDelegates] objectForKey:textField] textFieldDidBeginEditing:textField]; + } +} + +-(void)textFieldDidEndEditing:(UITextField *)textField { + if ([[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldDidEndEditing:)]) { + [[[self textFieldDelegates] objectForKey:textField] textFieldDidEndEditing:textField]; + } +} + +-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldBeginEditing:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldBeginEditing:textField]; + } + + return YES; +} + +-(BOOL)textFieldShouldEndEditing:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldEndEditing:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldEndEditing:textField]; + } + + return YES; +} + +-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textField:textField shouldChangeCharactersInRange:range replacementString:string]; + } + + return YES; +} + +-(BOOL)textFieldShouldClear:(UITextField *)textField { + if ([[self textFieldDelegates] objectForKey:textField] + && [[[self textFieldDelegates] objectForKey:textField] respondsToSelector:@selector(textFieldShouldClear:)]) { + return [[[self textFieldDelegates] objectForKey:textField] textFieldShouldClear:textField]; + } + + return YES; } @end diff --git a/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h b/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h old mode 100644 new mode 100755 index 80b9b109..28bbafbe --- a/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h +++ b/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.h @@ -7,9 +7,14 @@ // #import +#import "TPKeyboardAvoidingAction.h" @interface UIScrollView (TPKeyboardAvoidingAdditions) + +@property (strong, nonatomic) NSMapTable>* textFieldDelegates; + - (BOOL)TPKeyboardAvoiding_focusNextTextField; +- (BOOL)TPKeyboardAvoiding_focusPrevTextField; - (void)TPKeyboardAvoiding_scrollToActiveTextField; - (void)TPKeyboardAvoiding_keyboardWillShow:(NSNotification*)notification; @@ -18,5 +23,5 @@ - (void)TPKeyboardAvoiding_updateFromContentSizeChange; - (void)TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView:(UIView*)view; - (UIView*)TPKeyboardAvoiding_findFirstResponderBeneathView:(UIView*)view; --(CGSize)TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames; +- (CGSize)TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames; @end diff --git a/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m b/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m old mode 100644 new mode 100755 index adb9f376..a6f4b342 --- a/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m +++ b/TPKeyboardAvoiding/UIScrollView+TPKeyboardAvoidingAdditions.m @@ -17,6 +17,8 @@ static const int kStateKey; +static void * TextFieldDelegates = &TextFieldDelegates; + #define _UIKeyboardFrameEndUserInfoKey (&UIKeyboardFrameEndUserInfoKey != NULL ? UIKeyboardFrameEndUserInfoKey : @"UIKeyboardBoundsUserInfoKey") #define _UIKeyboardFrameBeginUserInfoKey (&UIKeyboardFrameBeginUserInfoKey != NULL ? UIKeyboardFrameBeginUserInfoKey : @"UIKeyboardBoundsUserInfoKey") @@ -30,6 +32,8 @@ @interface TPKeyboardAvoidingState : NSObject @property (nonatomic, assign) BOOL ignoringNotifications; @property (nonatomic, assign) BOOL keyboardAnimationInProgress; @property (nonatomic, assign) CGFloat animationDuration; +@property (strong, nonatomic) NSMapTable>* textFieldDelegates; + @end @implementation UIScrollView (TPKeyboardAvoidingAdditions) @@ -202,6 +206,27 @@ - (BOOL)TPKeyboardAvoiding_focusNextTextField { return NO; } +- (BOOL)TPKeyboardAvoiding_focusPrevTextField { + UIView *firstResponder = [self TPKeyboardAvoiding_findFirstResponderBeneathView:self]; + if ( !firstResponder ) { + return NO; + } + + UIView *view = [self TPKeyboardAvoiding_findPrevInputViewAfterView:firstResponder beneathView:self]; + + if ( view ) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{ + TPKeyboardAvoidingState *state = self.keyboardAvoidingState; + state.ignoringNotifications = YES; + [view becomeFirstResponder]; + state.ignoringNotifications = NO; + }); + return YES; + } + + return NO; +} + -(void)TPKeyboardAvoiding_scrollToActiveTextField { TPKeyboardAvoidingState *state = self.keyboardAvoidingState; @@ -249,6 +274,12 @@ - (UIView*)TPKeyboardAvoiding_findNextInputViewAfterView:(UIView*)priorView bene return candidate; } +- (UIView*)TPKeyboardAvoiding_findPrevInputViewAfterView:(UIView*)priorView beneathView:(UIView*)view { + UIView * candidate = nil; + [self TPKeyboardAvoiding_findPrevInputViewBeforeView:priorView beneathView:view bestCandidate:&candidate]; + return candidate; +} + - (void)TPKeyboardAvoiding_findNextInputViewAfterView:(UIView*)priorView beneathView:(UIView*)view bestCandidate:(UIView**)bestCandidate { // Search recursively for input view below/to right of priorTextField CGRect priorFrame = [self convertRect:priorView.frame fromView:priorView.superview]; @@ -264,9 +295,9 @@ - (void)TPKeyboardAvoiding_findNextInputViewAfterView:(UIView*)priorView beneath // Find views beneath, or to the right. For those views that match, choose the view closest to the top left if ( childView != priorView - && ((fabs(CGRectGetMinY(frame) - CGRectGetMinY(priorFrame)) < FLT_EPSILON && CGRectGetMinX(frame) > CGRectGetMinX(priorFrame)) - || CGRectGetMinY(frame) > CGRectGetMinY(priorFrame)) - && (!*bestCandidate || heuristic > bestCandidateHeuristic) ) { + && ((fabs(CGRectGetMinY(frame) - CGRectGetMinY(priorFrame)) < FLT_EPSILON && CGRectGetMinX(frame) > CGRectGetMinX(priorFrame)) + || CGRectGetMinY(frame) > CGRectGetMinY(priorFrame)) + && (!*bestCandidate || heuristic > bestCandidateHeuristic) ) { *bestCandidate = childView; bestCandidateHeuristic = heuristic; @@ -277,6 +308,34 @@ - (void)TPKeyboardAvoiding_findNextInputViewAfterView:(UIView*)priorView beneath } } +- (void)TPKeyboardAvoiding_findPrevInputViewBeforeView:(UIView*)priorView beneathView:(UIView*)view bestCandidate:(UIView**)bestCandidate { + // Search recursively for input view below/to right of priorTextField + CGRect priorFrame = [self convertRect:priorView.frame fromView:priorView.superview]; + CGRect candidateFrame = *bestCandidate ? [self convertRect:(*bestCandidate).frame fromView:(*bestCandidate).superview] : CGRectZero; + CGFloat bestCandidateHeuristic = [self TPKeyboardAvoiding_nextInputViewHeuristicForViewFrame:candidateFrame]; + + for ( UIView *childView in view.subviews ) { + if ( [self TPKeyboardAvoiding_viewIsValidKeyViewCandidate:childView] ) { + CGRect frame = [self convertRect:childView.frame fromView:view]; + + // Use a heuristic to evaluate candidates + CGFloat heuristic = [self TPKeyboardAvoiding_nextInputViewHeuristicForViewFrame:frame]; + + // Find views beneath, or to the right. For those views that match, choose the view closest to the top left + if ( childView != priorView + && ((fabs(CGRectGetMinY(frame) - CGRectGetMinY(priorFrame)) > -FLT_EPSILON && CGRectGetMinX(frame) < CGRectGetMinX(priorFrame)) + || CGRectGetMinY(frame) < CGRectGetMinY(priorFrame)) + && (!*bestCandidate || heuristic < bestCandidateHeuristic) ) { + + *bestCandidate = childView; + bestCandidateHeuristic = heuristic; + } + } else { + [self TPKeyboardAvoiding_findPrevInputViewBeforeView:priorView beneathView:childView bestCandidate:bestCandidate]; + } + } +} + - (CGFloat)TPKeyboardAvoiding_nextInputViewHeuristicForViewFrame:(CGRect)frame { return (-frame.origin.y * 1000.0) // Prefer elements closest to top (most important) + (-frame.origin.x); // Prefer elements closest to left @@ -424,6 +483,16 @@ -(CGFloat)TPKeyboardAvoiding_idealOffsetForView:(UIView *)view withViewingAreaHe } - (void)TPKeyboardAvoiding_initializeView:(UIView*)view { + + if (!self.textFieldDelegates) { + self.textFieldDelegates = [NSMapTable weakToWeakObjectsMapTable]; + } + + if ([(UITextField*)view delegate] && [(UITextField*)view delegate] != (id)self) { + [self.textFieldDelegates setObject:[(UITextField*)view delegate] forKey:view]; + [(UITextField*)view setDelegate:nil]; + } + if ( [view isKindOfClass:[UITextField class]] && (((UITextField*)view).returnKeyType == UIReturnKeyDefault || (((UITextField*)view).returnKeyType == UIReturnKeyNext)) && (![(UITextField*)view delegate] || [(UITextField*)view delegate] == (id)self) ) { @@ -438,6 +507,14 @@ - (void)TPKeyboardAvoiding_initializeView:(UIView*)view { } } +- (NSMapTable>*) textFieldDelegates { + return objc_getAssociatedObject(self, TextFieldDelegates); +} + +- (void)setTextFieldDelegates:(NSMapTable> *)textFieldDelegates { + objc_setAssociatedObject(self, TextFieldDelegates, textFieldDelegates, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + @end