No Description

ZYKeyboardUtil.m 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. //
  2. // ZYKeyboardUtil.m
  3. // ZYKeyboardUtil
  4. //
  5. // Created by lzy on 15/12/26.
  6. // Copyright © 2015年 lzy . All rights reserved.
  7. //
  8. #import "ZYKeyboardUtil.h"
  9. #define MARGIN_KEYBOARD_DEFAULT 10
  10. #define textView_no_anim_begin if ([_adaptiveView isKindOfClass:[UITextView class]]) {\
  11. [CATransaction begin];\
  12. [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];\
  13. }
  14. #define textView_no_anim_end if ([_adaptiveView isKindOfClass:[UITextView class]]) {\
  15. [CATransaction commit];\
  16. }
  17. static UIView *FIRST_RESPONDER;
  18. @interface ZYKeyboardUtil()
  19. @property (assign, nonatomic) BOOL keyboardObserveEnabled;
  20. @property (assign, nonatomic) int appearPostIndex;
  21. @property (strong, nonatomic) KeyboardInfo *keyboardInfo;
  22. @property (assign, nonatomic) BOOL haveRegisterObserver;
  23. @property (weak, nonatomic) UIViewController *adaptiveController;
  24. @property (weak, nonatomic) UIView *adaptiveView;
  25. @property (copy, nonatomic) animateWhenKeyboardAppearBlock animateWhenKeyboardAppearBlock;
  26. @property (copy, nonatomic) animateWhenKeyboardAppearAutomaticAnimBlock animateWhenKeyboardAppearAutomaticAnimBlock;
  27. @property (copy, nonatomic) animateWhenKeyboardDisappearBlock animateWhenKeyboardDisappearBlock;
  28. @property (copy, nonatomic) printKeyboardInfoBlock printKeyboardInfoBlock;
  29. @end
  30. @implementation ZYKeyboardUtil
  31. - (void)dealloc {
  32. [[NSNotificationCenter defaultCenter] removeObserver:self];
  33. }
  34. #pragma mark - lazy注册观察者
  35. - (void)registerObserver {
  36. if (_haveRegisterObserver == YES) {
  37. return;
  38. }
  39. self.haveRegisterObserver = YES;
  40. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
  41. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
  42. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
  43. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
  44. }
  45. - (void)adaptiveViewHandleWithController:(UIViewController *)viewController adaptiveView:(UIView *)adaptiveView, ...NS_REQUIRES_NIL_TERMINATION {
  46. NSMutableArray *adaptiveViewList = [NSMutableArray array];
  47. [adaptiveViewList addObject:adaptiveView];
  48. va_list var_list;
  49. va_start(var_list, adaptiveView);
  50. UIView *view;
  51. while ((view = va_arg(var_list, UIView *))) {
  52. [adaptiveViewList addObject:view];
  53. }
  54. va_end(var_list);
  55. for (UIView *adaptiveViews in adaptiveViewList) {
  56. FIRST_RESPONDER = nil;
  57. UIView *firstResponderView = [self recursionTraverseFindFirstResponderIn:adaptiveViews];
  58. if (nil != firstResponderView) {
  59. self.adaptiveView = firstResponderView;
  60. [self fitKeyboardAutomatically:firstResponderView controllerView:viewController.view keyboardRect:_keyboardInfo.frameEnd];
  61. self.adaptiveController = viewController;
  62. break;
  63. }
  64. }
  65. }
  66. //递归视图
  67. - (UIView *)recursionTraverseFindFirstResponderIn:(UIView *)view {
  68. if ([view isFirstResponder]) {
  69. FIRST_RESPONDER = view;
  70. } else {
  71. for (UIView *subView in view.subviews) {
  72. if ([subView isFirstResponder]) {
  73. FIRST_RESPONDER = subView;
  74. return FIRST_RESPONDER;
  75. }
  76. [self recursionTraverseFindFirstResponderIn:subView];
  77. }
  78. }
  79. return FIRST_RESPONDER;
  80. }
  81. - (void)fitKeyboardAutomatically:(UIView *)adaptiveView controllerView:(UIView *)controllerView keyboardRect:(CGRect)keyboardRect {
  82. UIWindow *window = [[UIApplication sharedApplication] keyWindow];
  83. CGRect convertRect = [adaptiveView.superview convertRect:adaptiveView.frame toView:window];
  84. if (CGRectGetMinY(keyboardRect) - MARGIN_KEYBOARD_DEFAULT < CGRectGetMaxY(convertRect)) {
  85. CGFloat signedDiff = CGRectGetMinY(keyboardRect) - CGRectGetMaxY(convertRect) - MARGIN_KEYBOARD_DEFAULT;
  86. //updateOriginY
  87. CGFloat newOriginY = CGRectGetMinY(controllerView.frame) + signedDiff;
  88. controllerView.frame = CGRectMake(controllerView.frame.origin.x, newOriginY, controllerView.frame.size.width, controllerView.frame.size.height);
  89. }
  90. }
  91. - (void)restoreKeyboardAutomatically {
  92. [self textViewHandle];
  93. CGRect tempFrame = self.adaptiveController.view.frame;
  94. if (self.adaptiveController.navigationController == nil || self.adaptiveController.navigationController.navigationBar.hidden == YES) {
  95. tempFrame.origin.y = 0.f;
  96. self.adaptiveController.view.frame = tempFrame;
  97. } else {
  98. tempFrame.origin.y = 64.f;
  99. self.adaptiveController.view.frame = tempFrame;
  100. }
  101. }
  102. - (void)textViewHandle {
  103. //还原时 textView可能会出现offset错乱现象
  104. if ([_adaptiveView isKindOfClass:[UITextView class]]) {
  105. [(UITextView *)_adaptiveView setContentOffset:CGPointMake(0, 0)];
  106. }
  107. }
  108. #pragma mark - 重写KeyboardInfo set方法,调用animationBlock
  109. - (void)setKeyboardInfo:(KeyboardInfo *)keyboardInfo {
  110. //home键使应用进入后台也会有某些通知
  111. if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
  112. return;
  113. }
  114. _keyboardInfo = keyboardInfo;
  115. if(!keyboardInfo.isSameAction || (keyboardInfo.heightIncrement != 0)) {
  116. [UIView animateWithDuration:keyboardInfo.animationDuration animations:^{
  117. switch (keyboardInfo.action) {
  118. case KeyboardActionShow:
  119. if(self.animateWhenKeyboardAppearBlock != nil) {
  120. self.animateWhenKeyboardAppearBlock(++self.appearPostIndex, keyboardInfo.frameEnd, keyboardInfo.frameEnd.size.height, keyboardInfo.heightIncrement);
  121. } else if (self.animateWhenKeyboardAppearAutomaticAnimBlock != nil) {
  122. self.animateWhenKeyboardAppearAutomaticAnimBlock(self);
  123. }
  124. break;
  125. case KeyboardActionHide:
  126. if(self.animateWhenKeyboardDisappearBlock != nil) {
  127. self.animateWhenKeyboardDisappearBlock(keyboardInfo.frameEnd.size.height);
  128. self.appearPostIndex = 0;
  129. } else {
  130. //auto restore
  131. [self restoreKeyboardAutomatically];
  132. }
  133. break;
  134. default:
  135. break;
  136. }
  137. [CATransaction commit];
  138. }completion:^(BOOL finished) {
  139. if(self.printKeyboardInfoBlock != nil && self.keyboardInfo != nil) {
  140. self.printKeyboardInfoBlock(self, keyboardInfo);
  141. }
  142. }];
  143. }
  144. }
  145. #pragma mark - 重写Block set方法,懒加载方式注册观察者
  146. /**
  147. * @brief handle the covering event youself when keyboard Appear, Animation automatically.
  148. *
  149. * use animateWhenKeyboardAppearBlock, animateWhenKeyboardAppearAutomaticAnimBlock will be invalid.
  150. */
  151. - (void)setAnimateWhenKeyboardAppearBlock:(animateWhenKeyboardAppearBlock)animateWhenKeyboardAppearBlock {
  152. _animateWhenKeyboardAppearBlock = animateWhenKeyboardAppearBlock;
  153. [self registerObserver];
  154. }
  155. /**
  156. * @brief handle the covering automatically, you must invoke the method adaptiveViewHandleWithController:adaptiveView: by the param keyboardUtil.
  157. *
  158. * use animateWhenKeyboardAppearAutomaticAnimBlock, animateWhenKeyboardAppearBlock must be nil.
  159. */
  160. - (void)setAnimateWhenKeyboardAppearAutomaticAnimBlock:(animateWhenKeyboardAppearAutomaticAnimBlock)animateWhenKeyboardAppearAutomaticAnimBlock {
  161. _animateWhenKeyboardAppearAutomaticAnimBlock = animateWhenKeyboardAppearAutomaticAnimBlock;
  162. [self registerObserver];
  163. }
  164. /**
  165. * @brief restore the UI youself when keyboard disappear.
  166. *
  167. * if not configure this Block, automatically itself.
  168. */
  169. - (void)setAnimateWhenKeyboardDisappearBlock:(animateWhenKeyboardDisappearBlock)animateWhenKeyboardDisappearBlock {
  170. _animateWhenKeyboardDisappearBlock = animateWhenKeyboardDisappearBlock;
  171. [self registerObserver];
  172. }
  173. - (void)setPrintKeyboardInfoBlock:(printKeyboardInfoBlock)printKeyboardInfoBlock {
  174. _printKeyboardInfoBlock = printKeyboardInfoBlock;
  175. [self registerObserver];
  176. }
  177. #pragma mark 响应selector
  178. - (void)keyboardWillShow:(NSNotification *)notification {
  179. [self handleKeyboard:notification keyboardAction:KeyboardActionShow];
  180. }
  181. //UIKeyboardWillChangeFrameNotification 可解决ios9 对于 第三方键盘 UIKeyboardWillShowNotification漏发的问题
  182. - (void)keyboardWillChangeFrame:(NSNotification *)notification {
  183. if(self.keyboardInfo.action == KeyboardActionShow){
  184. // [self handleKeyboard:notification keyboardAction:KeyboardActionShow];
  185. }
  186. }
  187. - (void)keyboardWillHide:(NSNotification *)notification {
  188. [self handleKeyboard:notification keyboardAction:KeyboardActionHide];
  189. }
  190. - (void)keyboardDidHide:(NSNotification *)notification {
  191. //置空
  192. self.keyboardInfo = nil;
  193. }
  194. #pragma mark 处理键盘事件
  195. - (void)handleKeyboard:(NSNotification *)notification keyboardAction:(KeyboardAction)keyboardAction {
  196. //进入后台触发某些通知,不响应
  197. if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
  198. return;
  199. }
  200. //解析通知
  201. NSDictionary *infoDict = [notification userInfo];
  202. CGRect frameBegin = [[infoDict objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
  203. CGRect frameEnd = [[infoDict objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
  204. CGFloat previousHeight;
  205. if(self.keyboardInfo.frameEnd.size.height > 0) {
  206. previousHeight = self.keyboardInfo.frameEnd.size.height;
  207. }else {
  208. previousHeight = 0;
  209. }
  210. CGFloat heightIncrement = frameEnd.size.height - previousHeight;
  211. BOOL isSameAction;
  212. if(self.keyboardInfo.action == keyboardAction) {
  213. isSameAction = YES;
  214. }else {
  215. isSameAction = NO;
  216. }
  217. KeyboardInfo *info = [[KeyboardInfo alloc] init];
  218. [info fillKeyboardInfoWithDuration:DURATION_ANIMATION frameBegin:frameBegin frameEnd:frameEnd heightIncrement:heightIncrement action:keyboardAction isSameAction:isSameAction];
  219. self.keyboardInfo = info;
  220. }
  221. - (void)fillKeyboardInfoWithKeyboardInfo:(KeyboardInfo *)keyboardInfo duration:(CGFloat)duration frameBegin:(CGRect)frameBegin frameEnd:(CGRect)frameEnd heightIncrement:(CGFloat)heightIncrement action:(KeyboardAction)action isSameAction:(BOOL)isSameAction {
  222. keyboardInfo.animationDuration = duration;
  223. keyboardInfo.frameBegin = frameBegin;
  224. keyboardInfo.frameEnd = frameEnd;
  225. keyboardInfo.heightIncrement = heightIncrement;
  226. keyboardInfo.action = action;
  227. keyboardInfo.isSameAction = isSameAction;
  228. }
  229. @end
  230. #pragma mark - KeyboardInfo(model)
  231. @interface KeyboardInfo()
  232. - (void)fillKeyboardInfoWithDuration:(CGFloat)duration frameBegin:(CGRect)frameBegin frameEnd:(CGRect)frameEnd heightIncrement:(CGFloat)heightIncrement action:(KeyboardAction)action isSameAction:(BOOL)isSameAction;
  233. @end
  234. @implementation KeyboardInfo
  235. - (void)fillKeyboardInfoWithDuration:(CGFloat)duration frameBegin:(CGRect)frameBegin frameEnd:(CGRect)frameEnd heightIncrement:(CGFloat)heightIncrement action:(KeyboardAction)action isSameAction:(BOOL)isSameAction {
  236. self.animationDuration = duration;
  237. self.frameBegin = frameBegin;
  238. self.frameEnd = frameEnd;
  239. self.heightIncrement = heightIncrement;
  240. self.action = action;
  241. self.isSameAction = isSameAction;
  242. }
  243. @end
  244. //ZYKeyboardUtil is available under the MIT license.
  245. //Please visit https://github.com/liuzhiyi1992/ZYKeyboardUtil for details.