123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- //
- // IQKeyboardReturnKeyHandler.m
- // https://github.com/hackiftekhar/IQKeyboardManager
- // Copyright (c) 2013-16 Iftekhar Qurashi.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "IQKeyboardReturnKeyHandler.h"
- #import "IQKeyboardManager.h"
- #import "IQUIView+Hierarchy.h"
- #import "IQNSArray+Sort.h"
- #import <UIKit/UITextField.h>
- #import <UIKit/UITextView.h>
- #import <UIKit/UIViewController.h>
- NSString *const kIQTextField = @"kIQTextField";
- NSString *const kIQTextFieldDelegate = @"kIQTextFieldDelegate";
- NSString *const kIQTextFieldReturnKeyType = @"kIQTextFieldReturnKeyType";
- @interface IQKeyboardReturnKeyHandler ()<UITextFieldDelegate,UITextViewDelegate>
- -(void)updateReturnKeyTypeOnTextField:(UIView*)textField;
- @end
- @implementation IQKeyboardReturnKeyHandler
- {
- NSMutableSet *textFieldInfoCache;
- }
- @synthesize lastTextFieldReturnKeyType = _lastTextFieldReturnKeyType;
- @synthesize delegate = _delegate;
- - (instancetype)init
- {
- self = [self initWithViewController:nil];
- return self;
- }
- -(instancetype)initWithViewController:(nullable UIViewController*)controller
- {
- self = [super init];
-
- if (self)
- {
- textFieldInfoCache = [[NSMutableSet alloc] init];
-
- if (controller.view)
- {
- [self addResponderFromView:controller.view];
- }
- }
-
- return self;
- }
- -(NSDictionary*)textFieldViewCachedInfo:(UIView*)textField
- {
- for (NSDictionary *infoDict in textFieldInfoCache)
- if (infoDict[kIQTextField] == textField) return infoDict;
-
- return nil;
- }
- #pragma mark - Add/Remove TextFields
- -(void)addResponderFromView:(UIView*)view
- {
- NSArray *textFields = [view deepResponderViews];
-
- for (UIView *textField in textFields) [self addTextFieldView:textField];
- }
- -(void)removeResponderFromView:(UIView*)view
- {
- NSArray *textFields = [view deepResponderViews];
-
- for (UIView *textField in textFields) [self removeTextFieldView:textField];
- }
- -(void)removeTextFieldView:(UIView*)view
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:view];
-
- if (dict)
- {
- if ([view isKindOfClass:[UITextField class]] || [view isKindOfClass:[UITextView class]])
- {
- UITextField *textField = (UITextField*)view;
- textField.returnKeyType = (UIReturnKeyType)[dict[kIQTextFieldReturnKeyType] integerValue];
- textField.delegate = dict[kIQTextFieldDelegate];
- }
- [textFieldInfoCache removeObject:dict];
- }
- }
- -(void)addTextFieldView:(UIView*)view
- {
- NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
-
- dict[kIQTextField] = view;
-
- if ([view isKindOfClass:[UITextField class]] || [view isKindOfClass:[UITextView class]])
- {
- UITextField *textField = (UITextField*)view;
- dict[kIQTextFieldReturnKeyType] = @(textField.returnKeyType);
- if (textField.delegate) dict[kIQTextFieldDelegate] = textField.delegate;
- [textField setDelegate:self];
- }
- [textFieldInfoCache addObject:dict];
- }
- -(void)updateReturnKeyTypeOnTextField:(UIView*)textField
- {
- UIView *superConsideredView;
-
- //If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347)
- for (Class consideredClass in [[IQKeyboardManager sharedManager] toolbarPreviousNextAllowedClasses])
- {
- superConsideredView = [textField superviewOfClassType:consideredClass];
-
- if (superConsideredView != nil)
- break;
- }
- NSArray *textFields = nil;
- //If there is a tableView in view's hierarchy, then fetching all it's subview that responds. No sorting for tableView, it's by subView position.
- if (superConsideredView) // // (Enhancement ID: #22)
- {
- textFields = [superConsideredView deepResponderViews];
- }
- //Otherwise fetching all the siblings
- else
- {
- textFields = [textField responderSiblings];
-
- //Sorting textFields according to behaviour
- switch ([[IQKeyboardManager sharedManager] toolbarManageBehaviour])
- {
- //If needs to sort it by tag
- case IQAutoToolbarByTag:
- textFields = [textFields sortedArrayByTag];
- break;
-
- //If needs to sort it by Position
- case IQAutoToolbarByPosition:
- textFields = [textFields sortedArrayByPosition];
- break;
-
- default:
- break;
- }
- }
-
- //If it's the last textField in responder view, else next
- [(UITextField*)textField setReturnKeyType:(([textFields lastObject] == textField) ? self.lastTextFieldReturnKeyType : UIReturnKeyNext)];
- }
- #pragma mark - Goto next or Resign.
- -(BOOL)goToNextResponderOrResign:(UIView*)textField
- {
- UIView *superConsideredView;
-
- //If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347)
- for (Class consideredClass in [[IQKeyboardManager sharedManager] toolbarPreviousNextAllowedClasses])
- {
- superConsideredView = [textField superviewOfClassType:consideredClass];
-
- if (superConsideredView != nil)
- break;
- }
-
- NSArray *textFields = nil;
-
- //If there is a tableView in view's hierarchy, then fetching all it's subview that responds. No sorting for tableView, it's by subView position.
- if (superConsideredView) // // (Enhancement ID: #22)
- {
- textFields = [superConsideredView deepResponderViews];
- }
- //Otherwise fetching all the siblings
- else
- {
- textFields = [textField responderSiblings];
-
- //Sorting textFields according to behaviour
- switch ([[IQKeyboardManager sharedManager] toolbarManageBehaviour])
- {
- //If needs to sort it by tag
- case IQAutoToolbarByTag:
- textFields = [textFields sortedArrayByTag];
- break;
-
- //If needs to sort it by Position
- case IQAutoToolbarByPosition:
- textFields = [textFields sortedArrayByPosition];
- break;
-
- default:
- break;
- }
- }
-
- //Getting index of current textField.
- NSUInteger index = [textFields indexOfObject:textField];
-
- //If it is not last textField. then it's next object becomeFirstResponder.
- if (index != NSNotFound && index < textFields.count-1)
- {
- [textFields[index+1] becomeFirstResponder];
- return NO;
- }
- else
- {
- [textField resignFirstResponder];
- return YES;
- }
- }
- #pragma mark - TextField delegate
- - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
- {
- id<UITextFieldDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textField];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textFieldShouldBeginEditing:)])
- return [delegate textFieldShouldBeginEditing:textField];
- else
- return YES;
- }
- - (void)textFieldDidBeginEditing:(UITextField *)textField
- {
- [self updateReturnKeyTypeOnTextField:textField];
- id<UITextFieldDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textField];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textFieldDidBeginEditing:)])
- [delegate textFieldDidBeginEditing:textField];
- }
- - (BOOL)textFieldShouldEndEditing:(UITextField *)textField
- {
- id<UITextFieldDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textField];
- delegate = dict[kIQTextFieldDelegate];
- }
- if ([delegate respondsToSelector:@selector(textFieldShouldEndEditing:)])
- return [delegate textFieldShouldEndEditing:textField];
- else
- return YES;
- }
- - (void)textFieldDidEndEditing:(UITextField *)textField
- {
- id<UITextFieldDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textField];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textFieldDidEndEditing:)])
- [delegate textFieldDidEndEditing:textField];
- }
- - (void)textFieldDidEndEditing:(UITextField *)textField reason:(UITextFieldDidEndEditingReason)reason NS_AVAILABLE_IOS(10_0);
- {
- id<UITextFieldDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textField];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- #ifdef __IPHONE_11_0
- if (@available(iOS 10.0, *)) {
- #endif
- if ([delegate respondsToSelector:@selector(textFieldDidEndEditing:reason:)])
- [delegate textFieldDidEndEditing:textField reason:reason];
- #ifdef __IPHONE_11_0
- }
- #endif
- }
- - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
- {
- id<UITextFieldDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textField];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)])
- return [delegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
- else
- return YES;
- }
- - (BOOL)textFieldShouldClear:(UITextField *)textField
- {
- id<UITextFieldDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textField];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textFieldShouldClear:)])
- return [delegate textFieldShouldClear:textField];
- else
- return YES;
- }
- -(BOOL)textFieldShouldReturn:(UITextField *)textField
- {
- id<UITextFieldDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textField];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textFieldShouldReturn:)])
- {
- BOOL shouldReturn = [delegate textFieldShouldReturn:textField];
- if (shouldReturn)
- {
- shouldReturn = [self goToNextResponderOrResign:textField];
- }
-
- return shouldReturn;
- }
- else
- {
- return [self goToNextResponderOrResign:textField];
- }
- }
- #pragma mark - TextView delegate
- - (BOOL)textViewShouldBeginEditing:(UITextView *)textView
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textViewShouldBeginEditing:)])
- return [delegate textViewShouldBeginEditing:textView];
- else
- return YES;
- }
- - (BOOL)textViewShouldEndEditing:(UITextView *)textView
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textViewShouldEndEditing:)])
- return [delegate textViewShouldEndEditing:textView];
- else
- return YES;
- }
- - (void)textViewDidBeginEditing:(UITextView *)textView
- {
- [self updateReturnKeyTypeOnTextField:textView];
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textViewDidBeginEditing:)])
- [delegate textViewDidBeginEditing:textView];
- }
- - (void)textViewDidEndEditing:(UITextView *)textView
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textViewDidEndEditing:)])
- [delegate textViewDidEndEditing:textView];
- }
- - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- BOOL shouldReturn = YES;
-
- if ([delegate respondsToSelector:@selector(textView:shouldChangeTextInRange:replacementText:)])
- shouldReturn = [delegate textView:textView shouldChangeTextInRange:range replacementText:text];
-
- if (shouldReturn && [text isEqualToString:@"\n"])
- {
- shouldReturn = [self goToNextResponderOrResign:textView];
- }
-
- return shouldReturn;
- }
- - (void)textViewDidChange:(UITextView *)textView
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textViewDidChange:)])
- [delegate textViewDidChange:textView];
- }
- - (void)textViewDidChangeSelection:(UITextView *)textView
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- if ([delegate respondsToSelector:@selector(textViewDidChangeSelection:)])
- [delegate textViewDidChangeSelection:textView];
- }
- - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction NS_AVAILABLE_IOS(10_0);
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- #ifdef __IPHONE_11_0
- if (@available(iOS 10.0, *)) {
- #endif
- if ([delegate respondsToSelector:@selector(textView:shouldInteractWithURL:inRange:interaction:)])
- return [delegate textView:textView shouldInteractWithURL:URL inRange:characterRange interaction:interaction];
- #ifdef __IPHONE_11_0
- }
- #endif
- return YES;
- }
- - (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction NS_AVAILABLE_IOS(10_0);
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- #ifdef __IPHONE_11_0
- if (@available(iOS 10.0, *)) {
- #endif
- if ([delegate respondsToSelector:@selector(textView:shouldInteractWithTextAttachment:inRange:interaction:)])
- return [delegate textView:textView shouldInteractWithTextAttachment:textAttachment inRange:characterRange interaction:interaction];
- #ifdef __IPHONE_11_0
- }
- #endif
- return YES;
- }
- - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- if ([delegate respondsToSelector:@selector(textView:shouldInteractWithURL:inRange:)])
- return [delegate textView:textView shouldInteractWithURL:URL inRange:characterRange];
- #pragma clang diagnostic pop
- else
- return YES;
- }
- - (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange
- {
- id<UITextViewDelegate> delegate = self.delegate;
-
- if (delegate == nil)
- {
- NSDictionary *dict = [self textFieldViewCachedInfo:textView];
- delegate = dict[kIQTextFieldDelegate];
- }
-
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- if ([delegate respondsToSelector:@selector(textView:shouldInteractWithTextAttachment:inRange:)])
- return [delegate textView:textView shouldInteractWithTextAttachment:textAttachment inRange:characterRange];
- #pragma clang diagnostic pop
- else
- return YES;
- }
- -(void)dealloc
- {
- for (NSDictionary *dict in textFieldInfoCache)
- {
- UIView *view = dict[kIQTextField];
-
- if ([view isKindOfClass:[UITextField class]] || [view isKindOfClass:[UITextView class]])
- {
- UITextField *textField = (UITextField*)view;
- textField.returnKeyType = (UIReturnKeyType)[dict[kIQTextFieldReturnKeyType] integerValue];
- textField.delegate = dict[kIQTextFieldDelegate];
- }
- }
- [textFieldInfoCache removeAllObjects];
- }
- @end
|