悟空记账

IQKeyboardReturnKeyHandler.m 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. //
  2. // IQKeyboardReturnKeyHandler.m
  3. // https://github.com/hackiftekhar/IQKeyboardManager
  4. // Copyright (c) 2013-16 Iftekhar Qurashi.
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. #import "IQKeyboardReturnKeyHandler.h"
  24. #import "IQKeyboardManager.h"
  25. #import "IQUIView+Hierarchy.h"
  26. #import "IQNSArray+Sort.h"
  27. #import <UIKit/UITextField.h>
  28. #import <UIKit/UITextView.h>
  29. #import <UIKit/UIViewController.h>
  30. NSString *const kIQTextField = @"kIQTextField";
  31. NSString *const kIQTextFieldDelegate = @"kIQTextFieldDelegate";
  32. NSString *const kIQTextFieldReturnKeyType = @"kIQTextFieldReturnKeyType";
  33. @interface IQKeyboardReturnKeyHandler ()<UITextFieldDelegate,UITextViewDelegate>
  34. -(void)updateReturnKeyTypeOnTextField:(UIView*)textField;
  35. @end
  36. @implementation IQKeyboardReturnKeyHandler
  37. {
  38. NSMutableSet *textFieldInfoCache;
  39. }
  40. @synthesize lastTextFieldReturnKeyType = _lastTextFieldReturnKeyType;
  41. @synthesize delegate = _delegate;
  42. - (instancetype)init
  43. {
  44. self = [self initWithViewController:nil];
  45. return self;
  46. }
  47. -(instancetype)initWithViewController:(nullable UIViewController*)controller
  48. {
  49. self = [super init];
  50. if (self)
  51. {
  52. textFieldInfoCache = [[NSMutableSet alloc] init];
  53. if (controller.view)
  54. {
  55. [self addResponderFromView:controller.view];
  56. }
  57. }
  58. return self;
  59. }
  60. -(NSDictionary*)textFieldViewCachedInfo:(UIView*)textField
  61. {
  62. for (NSDictionary *infoDict in textFieldInfoCache)
  63. if (infoDict[kIQTextField] == textField) return infoDict;
  64. return nil;
  65. }
  66. #pragma mark - Add/Remove TextFields
  67. -(void)addResponderFromView:(UIView*)view
  68. {
  69. NSArray *textFields = [view deepResponderViews];
  70. for (UIView *textField in textFields) [self addTextFieldView:textField];
  71. }
  72. -(void)removeResponderFromView:(UIView*)view
  73. {
  74. NSArray *textFields = [view deepResponderViews];
  75. for (UIView *textField in textFields) [self removeTextFieldView:textField];
  76. }
  77. -(void)removeTextFieldView:(UIView*)view
  78. {
  79. NSDictionary *dict = [self textFieldViewCachedInfo:view];
  80. if (dict)
  81. {
  82. if ([view isKindOfClass:[UITextField class]] || [view isKindOfClass:[UITextView class]])
  83. {
  84. UITextField *textField = (UITextField*)view;
  85. textField.returnKeyType = (UIReturnKeyType)[dict[kIQTextFieldReturnKeyType] integerValue];
  86. textField.delegate = dict[kIQTextFieldDelegate];
  87. }
  88. [textFieldInfoCache removeObject:dict];
  89. }
  90. }
  91. -(void)addTextFieldView:(UIView*)view
  92. {
  93. NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
  94. dict[kIQTextField] = view;
  95. if ([view isKindOfClass:[UITextField class]] || [view isKindOfClass:[UITextView class]])
  96. {
  97. UITextField *textField = (UITextField*)view;
  98. dict[kIQTextFieldReturnKeyType] = @(textField.returnKeyType);
  99. if (textField.delegate) dict[kIQTextFieldDelegate] = textField.delegate;
  100. [textField setDelegate:self];
  101. }
  102. [textFieldInfoCache addObject:dict];
  103. }
  104. -(void)updateReturnKeyTypeOnTextField:(UIView*)textField
  105. {
  106. UIView *superConsideredView;
  107. //If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347)
  108. for (Class consideredClass in [[IQKeyboardManager sharedManager] toolbarPreviousNextAllowedClasses])
  109. {
  110. superConsideredView = [textField superviewOfClassType:consideredClass];
  111. if (superConsideredView != nil)
  112. break;
  113. }
  114. NSArray *textFields = nil;
  115. //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.
  116. if (superConsideredView) // // (Enhancement ID: #22)
  117. {
  118. textFields = [superConsideredView deepResponderViews];
  119. }
  120. //Otherwise fetching all the siblings
  121. else
  122. {
  123. textFields = [textField responderSiblings];
  124. //Sorting textFields according to behaviour
  125. switch ([[IQKeyboardManager sharedManager] toolbarManageBehaviour])
  126. {
  127. //If needs to sort it by tag
  128. case IQAutoToolbarByTag:
  129. textFields = [textFields sortedArrayByTag];
  130. break;
  131. //If needs to sort it by Position
  132. case IQAutoToolbarByPosition:
  133. textFields = [textFields sortedArrayByPosition];
  134. break;
  135. default:
  136. break;
  137. }
  138. }
  139. //If it's the last textField in responder view, else next
  140. [(UITextField*)textField setReturnKeyType:(([textFields lastObject] == textField) ? self.lastTextFieldReturnKeyType : UIReturnKeyNext)];
  141. }
  142. #pragma mark - Goto next or Resign.
  143. -(BOOL)goToNextResponderOrResign:(UIView*)textField
  144. {
  145. UIView *superConsideredView;
  146. //If find any consider responderView in it's upper hierarchy then will get deepResponderView. (Bug ID: #347)
  147. for (Class consideredClass in [[IQKeyboardManager sharedManager] toolbarPreviousNextAllowedClasses])
  148. {
  149. superConsideredView = [textField superviewOfClassType:consideredClass];
  150. if (superConsideredView != nil)
  151. break;
  152. }
  153. NSArray *textFields = nil;
  154. //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.
  155. if (superConsideredView) // // (Enhancement ID: #22)
  156. {
  157. textFields = [superConsideredView deepResponderViews];
  158. }
  159. //Otherwise fetching all the siblings
  160. else
  161. {
  162. textFields = [textField responderSiblings];
  163. //Sorting textFields according to behaviour
  164. switch ([[IQKeyboardManager sharedManager] toolbarManageBehaviour])
  165. {
  166. //If needs to sort it by tag
  167. case IQAutoToolbarByTag:
  168. textFields = [textFields sortedArrayByTag];
  169. break;
  170. //If needs to sort it by Position
  171. case IQAutoToolbarByPosition:
  172. textFields = [textFields sortedArrayByPosition];
  173. break;
  174. default:
  175. break;
  176. }
  177. }
  178. //Getting index of current textField.
  179. NSUInteger index = [textFields indexOfObject:textField];
  180. //If it is not last textField. then it's next object becomeFirstResponder.
  181. if (index != NSNotFound && index < textFields.count-1)
  182. {
  183. [textFields[index+1] becomeFirstResponder];
  184. return NO;
  185. }
  186. else
  187. {
  188. [textField resignFirstResponder];
  189. return YES;
  190. }
  191. }
  192. #pragma mark - TextField delegate
  193. - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
  194. {
  195. id<UITextFieldDelegate> delegate = self.delegate;
  196. if (delegate == nil)
  197. {
  198. NSDictionary *dict = [self textFieldViewCachedInfo:textField];
  199. delegate = dict[kIQTextFieldDelegate];
  200. }
  201. if ([delegate respondsToSelector:@selector(textFieldShouldBeginEditing:)])
  202. return [delegate textFieldShouldBeginEditing:textField];
  203. else
  204. return YES;
  205. }
  206. - (void)textFieldDidBeginEditing:(UITextField *)textField
  207. {
  208. [self updateReturnKeyTypeOnTextField:textField];
  209. id<UITextFieldDelegate> delegate = self.delegate;
  210. if (delegate == nil)
  211. {
  212. NSDictionary *dict = [self textFieldViewCachedInfo:textField];
  213. delegate = dict[kIQTextFieldDelegate];
  214. }
  215. if ([delegate respondsToSelector:@selector(textFieldDidBeginEditing:)])
  216. [delegate textFieldDidBeginEditing:textField];
  217. }
  218. - (BOOL)textFieldShouldEndEditing:(UITextField *)textField
  219. {
  220. id<UITextFieldDelegate> delegate = self.delegate;
  221. if (delegate == nil)
  222. {
  223. NSDictionary *dict = [self textFieldViewCachedInfo:textField];
  224. delegate = dict[kIQTextFieldDelegate];
  225. }
  226. if ([delegate respondsToSelector:@selector(textFieldShouldEndEditing:)])
  227. return [delegate textFieldShouldEndEditing:textField];
  228. else
  229. return YES;
  230. }
  231. - (void)textFieldDidEndEditing:(UITextField *)textField
  232. {
  233. id<UITextFieldDelegate> delegate = self.delegate;
  234. if (delegate == nil)
  235. {
  236. NSDictionary *dict = [self textFieldViewCachedInfo:textField];
  237. delegate = dict[kIQTextFieldDelegate];
  238. }
  239. if ([delegate respondsToSelector:@selector(textFieldDidEndEditing:)])
  240. [delegate textFieldDidEndEditing:textField];
  241. }
  242. - (void)textFieldDidEndEditing:(UITextField *)textField reason:(UITextFieldDidEndEditingReason)reason NS_AVAILABLE_IOS(10_0);
  243. {
  244. id<UITextFieldDelegate> delegate = self.delegate;
  245. if (delegate == nil)
  246. {
  247. NSDictionary *dict = [self textFieldViewCachedInfo:textField];
  248. delegate = dict[kIQTextFieldDelegate];
  249. }
  250. #ifdef __IPHONE_11_0
  251. if (@available(iOS 10.0, *)) {
  252. #endif
  253. if ([delegate respondsToSelector:@selector(textFieldDidEndEditing:reason:)])
  254. [delegate textFieldDidEndEditing:textField reason:reason];
  255. #ifdef __IPHONE_11_0
  256. }
  257. #endif
  258. }
  259. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
  260. {
  261. id<UITextFieldDelegate> delegate = self.delegate;
  262. if (delegate == nil)
  263. {
  264. NSDictionary *dict = [self textFieldViewCachedInfo:textField];
  265. delegate = dict[kIQTextFieldDelegate];
  266. }
  267. if ([delegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)])
  268. return [delegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
  269. else
  270. return YES;
  271. }
  272. - (BOOL)textFieldShouldClear:(UITextField *)textField
  273. {
  274. id<UITextFieldDelegate> delegate = self.delegate;
  275. if (delegate == nil)
  276. {
  277. NSDictionary *dict = [self textFieldViewCachedInfo:textField];
  278. delegate = dict[kIQTextFieldDelegate];
  279. }
  280. if ([delegate respondsToSelector:@selector(textFieldShouldClear:)])
  281. return [delegate textFieldShouldClear:textField];
  282. else
  283. return YES;
  284. }
  285. -(BOOL)textFieldShouldReturn:(UITextField *)textField
  286. {
  287. id<UITextFieldDelegate> delegate = self.delegate;
  288. if (delegate == nil)
  289. {
  290. NSDictionary *dict = [self textFieldViewCachedInfo:textField];
  291. delegate = dict[kIQTextFieldDelegate];
  292. }
  293. if ([delegate respondsToSelector:@selector(textFieldShouldReturn:)])
  294. {
  295. BOOL shouldReturn = [delegate textFieldShouldReturn:textField];
  296. if (shouldReturn)
  297. {
  298. shouldReturn = [self goToNextResponderOrResign:textField];
  299. }
  300. return shouldReturn;
  301. }
  302. else
  303. {
  304. return [self goToNextResponderOrResign:textField];
  305. }
  306. }
  307. #pragma mark - TextView delegate
  308. - (BOOL)textViewShouldBeginEditing:(UITextView *)textView
  309. {
  310. id<UITextViewDelegate> delegate = self.delegate;
  311. if (delegate == nil)
  312. {
  313. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  314. delegate = dict[kIQTextFieldDelegate];
  315. }
  316. if ([delegate respondsToSelector:@selector(textViewShouldBeginEditing:)])
  317. return [delegate textViewShouldBeginEditing:textView];
  318. else
  319. return YES;
  320. }
  321. - (BOOL)textViewShouldEndEditing:(UITextView *)textView
  322. {
  323. id<UITextViewDelegate> delegate = self.delegate;
  324. if (delegate == nil)
  325. {
  326. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  327. delegate = dict[kIQTextFieldDelegate];
  328. }
  329. if ([delegate respondsToSelector:@selector(textViewShouldEndEditing:)])
  330. return [delegate textViewShouldEndEditing:textView];
  331. else
  332. return YES;
  333. }
  334. - (void)textViewDidBeginEditing:(UITextView *)textView
  335. {
  336. [self updateReturnKeyTypeOnTextField:textView];
  337. id<UITextViewDelegate> delegate = self.delegate;
  338. if (delegate == nil)
  339. {
  340. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  341. delegate = dict[kIQTextFieldDelegate];
  342. }
  343. if ([delegate respondsToSelector:@selector(textViewDidBeginEditing:)])
  344. [delegate textViewDidBeginEditing:textView];
  345. }
  346. - (void)textViewDidEndEditing:(UITextView *)textView
  347. {
  348. id<UITextViewDelegate> delegate = self.delegate;
  349. if (delegate == nil)
  350. {
  351. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  352. delegate = dict[kIQTextFieldDelegate];
  353. }
  354. if ([delegate respondsToSelector:@selector(textViewDidEndEditing:)])
  355. [delegate textViewDidEndEditing:textView];
  356. }
  357. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
  358. {
  359. id<UITextViewDelegate> delegate = self.delegate;
  360. if (delegate == nil)
  361. {
  362. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  363. delegate = dict[kIQTextFieldDelegate];
  364. }
  365. BOOL shouldReturn = YES;
  366. if ([delegate respondsToSelector:@selector(textView:shouldChangeTextInRange:replacementText:)])
  367. shouldReturn = [delegate textView:textView shouldChangeTextInRange:range replacementText:text];
  368. if (shouldReturn && [text isEqualToString:@"\n"])
  369. {
  370. shouldReturn = [self goToNextResponderOrResign:textView];
  371. }
  372. return shouldReturn;
  373. }
  374. - (void)textViewDidChange:(UITextView *)textView
  375. {
  376. id<UITextViewDelegate> delegate = self.delegate;
  377. if (delegate == nil)
  378. {
  379. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  380. delegate = dict[kIQTextFieldDelegate];
  381. }
  382. if ([delegate respondsToSelector:@selector(textViewDidChange:)])
  383. [delegate textViewDidChange:textView];
  384. }
  385. - (void)textViewDidChangeSelection:(UITextView *)textView
  386. {
  387. id<UITextViewDelegate> delegate = self.delegate;
  388. if (delegate == nil)
  389. {
  390. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  391. delegate = dict[kIQTextFieldDelegate];
  392. }
  393. if ([delegate respondsToSelector:@selector(textViewDidChangeSelection:)])
  394. [delegate textViewDidChangeSelection:textView];
  395. }
  396. - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction NS_AVAILABLE_IOS(10_0);
  397. {
  398. id<UITextViewDelegate> delegate = self.delegate;
  399. if (delegate == nil)
  400. {
  401. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  402. delegate = dict[kIQTextFieldDelegate];
  403. }
  404. #ifdef __IPHONE_11_0
  405. if (@available(iOS 10.0, *)) {
  406. #endif
  407. if ([delegate respondsToSelector:@selector(textView:shouldInteractWithURL:inRange:interaction:)])
  408. return [delegate textView:textView shouldInteractWithURL:URL inRange:characterRange interaction:interaction];
  409. #ifdef __IPHONE_11_0
  410. }
  411. #endif
  412. return YES;
  413. }
  414. - (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction NS_AVAILABLE_IOS(10_0);
  415. {
  416. id<UITextViewDelegate> delegate = self.delegate;
  417. if (delegate == nil)
  418. {
  419. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  420. delegate = dict[kIQTextFieldDelegate];
  421. }
  422. #ifdef __IPHONE_11_0
  423. if (@available(iOS 10.0, *)) {
  424. #endif
  425. if ([delegate respondsToSelector:@selector(textView:shouldInteractWithTextAttachment:inRange:interaction:)])
  426. return [delegate textView:textView shouldInteractWithTextAttachment:textAttachment inRange:characterRange interaction:interaction];
  427. #ifdef __IPHONE_11_0
  428. }
  429. #endif
  430. return YES;
  431. }
  432. - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
  433. {
  434. id<UITextViewDelegate> delegate = self.delegate;
  435. if (delegate == nil)
  436. {
  437. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  438. delegate = dict[kIQTextFieldDelegate];
  439. }
  440. #pragma clang diagnostic push
  441. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  442. if ([delegate respondsToSelector:@selector(textView:shouldInteractWithURL:inRange:)])
  443. return [delegate textView:textView shouldInteractWithURL:URL inRange:characterRange];
  444. #pragma clang diagnostic pop
  445. else
  446. return YES;
  447. }
  448. - (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange
  449. {
  450. id<UITextViewDelegate> delegate = self.delegate;
  451. if (delegate == nil)
  452. {
  453. NSDictionary *dict = [self textFieldViewCachedInfo:textView];
  454. delegate = dict[kIQTextFieldDelegate];
  455. }
  456. #pragma clang diagnostic push
  457. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  458. if ([delegate respondsToSelector:@selector(textView:shouldInteractWithTextAttachment:inRange:)])
  459. return [delegate textView:textView shouldInteractWithTextAttachment:textAttachment inRange:characterRange];
  460. #pragma clang diagnostic pop
  461. else
  462. return YES;
  463. }
  464. -(void)dealloc
  465. {
  466. for (NSDictionary *dict in textFieldInfoCache)
  467. {
  468. UIView *view = dict[kIQTextField];
  469. if ([view isKindOfClass:[UITextField class]] || [view isKindOfClass:[UITextView class]])
  470. {
  471. UITextField *textField = (UITextField*)view;
  472. textField.returnKeyType = (UIReturnKeyType)[dict[kIQTextFieldReturnKeyType] integerValue];
  473. textField.delegate = dict[kIQTextFieldDelegate];
  474. }
  475. }
  476. [textFieldInfoCache removeAllObjects];
  477. }
  478. @end