|
17 | 17 |
|
18 | 18 | #define _UIKeyboardFrameEndUserInfoKey (&UIKeyboardFrameEndUserInfoKey != NULL ? UIKeyboardFrameEndUserInfoKey : @"UIKeyboardBoundsUserInfoKey") |
19 | 19 |
|
20 | | -#define fequal(a,b) (fabs((a) - (b)) < DBL_EPSILON) |
21 | | - |
22 | | - |
23 | 20 | @interface TPKeyboardAvoidingState : NSObject |
24 | 21 | @property (nonatomic, assign) UIEdgeInsets priorInset; |
25 | 22 | @property (nonatomic, assign) UIEdgeInsets priorScrollIndicatorInsets; |
@@ -153,9 +150,7 @@ - (BOOL)TPKeyboardAvoiding_focusNextTextField { |
153 | 150 | return NO; |
154 | 151 | } |
155 | 152 |
|
156 | | - CGFloat minY = CGFLOAT_MAX; |
157 | | - UIView *view = nil; |
158 | | - [self TPKeyboardAvoiding_findTextFieldAfterTextField:firstResponder beneathView:self minY:&minY foundView:&view]; |
| 153 | + UIView *view = [self TPKeyboardAvoiding_findNextInputViewAfterView:firstResponder beneathView:self]; |
159 | 154 |
|
160 | 155 | if ( view ) { |
161 | 156 | [view performSelector:@selector(becomeFirstResponder) withObject:nil afterDelay:0.0]; |
@@ -194,38 +189,50 @@ - (UIView*)TPKeyboardAvoiding_findFirstResponderBeneathView:(UIView*)view { |
194 | 189 | return nil; |
195 | 190 | } |
196 | 191 |
|
197 | | -- (void)TPKeyboardAvoiding_findTextFieldAfterTextField:(UIView*)priorTextField beneathView:(UIView*)view minY:(CGFloat*)minY foundView:(UIView* __autoreleasing *)foundView { |
198 | | - // Search recursively for text field or text view below priorTextField |
199 | | - CGFloat priorFieldOffset = CGRectGetMinY([self convertRect:priorTextField.frame fromView:priorTextField.superview]); |
| 192 | +- (UIView*)TPKeyboardAvoiding_findNextInputViewAfterView:(UIView*)priorView beneathView:(UIView*)view { |
| 193 | + UIView * candidate = nil; |
| 194 | + [self TPKeyboardAvoiding_findNextInputViewAfterView:priorView beneathView:view bestCandidate:&candidate]; |
| 195 | + return candidate; |
| 196 | +} |
| 197 | + |
| 198 | +- (void)TPKeyboardAvoiding_findNextInputViewAfterView:(UIView*)priorView beneathView:(UIView*)view bestCandidate:(UIView**)bestCandidate { |
| 199 | + // Search recursively for input view below/to right of priorTextField |
| 200 | + CGRect priorFrame = [self convertRect:priorView.frame fromView:priorView.superview]; |
| 201 | + CGRect candidateFrame = *bestCandidate ? [self convertRect:(*bestCandidate).frame fromView:(*bestCandidate).superview] : CGRectZero; |
| 202 | + CGFloat bestCandidateHeuristic = -sqrt(candidateFrame.origin.x*candidateFrame.origin.x + candidateFrame.origin.y*candidateFrame.origin.y) |
| 203 | + + (fabs(CGRectGetMinY(candidateFrame) - CGRectGetMinY(priorFrame)) < FLT_EPSILON ? 1e6 : 0); |
| 204 | + |
200 | 205 | for ( UIView *childView in view.subviews ) { |
201 | | - if ([self TPKeyboardAvoiding_viewIsNextTextField:childView]) { |
| 206 | + if ( [self TPKeyboardAvoiding_viewIsValidKeyViewCandidate:childView] ) { |
202 | 207 | CGRect frame = [self convertRect:childView.frame fromView:view]; |
203 | | - if ( childView != priorTextField |
204 | | - && CGRectGetMinY(frame) >= priorFieldOffset |
205 | | - && CGRectGetMinY(frame) < *minY && |
206 | | - !(fequal(frame.origin.y, priorTextField.frame.origin.y) |
207 | | - && frame.origin.x < priorTextField.frame.origin.x) ) { |
208 | | - *minY = CGRectGetMinY(frame); |
209 | | - *foundView = childView; |
| 208 | + |
| 209 | + // Use a heuristic to evaluate candidates: prefer elements closest to the top left, and on the same line |
| 210 | + CGFloat heuristic = -sqrt(frame.origin.x*frame.origin.x + frame.origin.y*frame.origin.y) |
| 211 | + + (fabs(CGRectGetMinY(frame) - CGRectGetMinY(priorFrame)) < FLT_EPSILON ? 1e6 : 0); |
| 212 | + |
| 213 | + // Find views beneath, or to the right. For those views that match, choose the view closest to the top left |
| 214 | + if ( childView != priorView |
| 215 | + && ((fabs(CGRectGetMinY(frame) - CGRectGetMinY(priorFrame)) < FLT_EPSILON && CGRectGetMinX(frame) > CGRectGetMinX(priorFrame)) |
| 216 | + || CGRectGetMinY(frame) > CGRectGetMinY(priorFrame)) |
| 217 | + && (!*bestCandidate || heuristic > bestCandidateHeuristic) ) { |
| 218 | + |
| 219 | + *bestCandidate = childView; |
| 220 | + bestCandidateHeuristic = heuristic; |
210 | 221 | } |
211 | 222 | } else { |
212 | | - [self TPKeyboardAvoiding_findTextFieldAfterTextField:priorTextField beneathView:childView minY:minY foundView:foundView]; |
| 223 | + [self TPKeyboardAvoiding_findNextInputViewAfterView:priorView beneathView:childView bestCandidate:bestCandidate]; |
213 | 224 | } |
214 | 225 | } |
215 | 226 | } |
216 | 227 |
|
217 | | -- (BOOL)TPKeyboardAvoiding_viewIsNextTextField:(UIView *)view { |
218 | | - if (view.hidden) return NO; |
| 228 | +- (BOOL)TPKeyboardAvoiding_viewIsValidKeyViewCandidate:(UIView *)view { |
| 229 | + if ( view.hidden || !view.userInteractionEnabled ) return NO; |
219 | 230 |
|
220 | | - if ([view isKindOfClass:[UITextField class]]) { |
221 | | - UITextField *textField = (UITextField *)view; |
222 | | - if (!textField.enabled) return NO; |
223 | | - |
| 231 | + if ( [view isKindOfClass:[UITextField class]] && ((UITextField*)view).enabled ) { |
| 232 | + return YES; |
224 | 233 | } |
225 | 234 |
|
226 | | - if ( ([view isKindOfClass:[UITextField class]] || |
227 | | - [view isKindOfClass:[UITextView class]]) |
228 | | - && view.isUserInteractionEnabled) { |
| 235 | + if ( [view isKindOfClass:[UITextView class]] && ((UITextView*)view).isEditable ) { |
229 | 236 | return YES; |
230 | 237 | } |
231 | 238 |
|
@@ -308,9 +315,7 @@ - (void)TPKeyboardAvoiding_initializeView:(UIView*)view { |
308 | 315 | && ((UITextField*)view).returnKeyType == UIReturnKeyDefault |
309 | 316 | && (![(UITextField*)view delegate] || [(UITextField*)view delegate] == (id<UITextFieldDelegate>)self) ) { |
310 | 317 | [(UITextField*)view setDelegate:(id<UITextFieldDelegate>)self]; |
311 | | - UIView *otherView = nil; |
312 | | - CGFloat minY = CGFLOAT_MAX; |
313 | | - [self TPKeyboardAvoiding_findTextFieldAfterTextField:view beneathView:self minY:&minY foundView:&otherView]; |
| 318 | + UIView *otherView = [self TPKeyboardAvoiding_findNextInputViewAfterView:view beneathView:self]; |
314 | 319 |
|
315 | 320 | if ( otherView ) { |
316 | 321 | ((UITextField*)view).returnKeyType = UIReturnKeyNext; |
|
0 commit comments