ios开发 之 UIResponder详解

        在iOS中UIResponder类是专门用来响应用户的操作处理各种事件的,包括触摸事件(Touch Events)、运动事件(Motion Events)、远程控制事件(Remote Control Events)。我们知道UIApplication、UIView、UIViewController这几个类是直接继承自UIResponder,所以这些类都可以响应事件。当然我们自定义的继承自UIView的View以及自定义的继承自UIViewController的控制器都可以响应事件。本文将详细介绍UIResponder类。


原文地址:http://blog.csdn.net/zeng_zhiming/article/details/71747881


一、使用详解

1、通过响应者链查找视图的视图控制器

[objc]  view plain  copy
  1. /** 
  2.  *  查找视图的视图控制器 
  3.  * 
  4.  *  @param view 视图 
  5.  * 
  6.  *  @return 返回视图的控制器 
  7.  */  
  8. - (UIViewController *)getControllerFromView:(UIView *)view {  
  9.     // 遍历响应者链。返回第一个找到视图控制器  
  10.     UIResponder *responder = view;  
  11.     while ((responder = [responder nextResponder])){  
  12.         if ([responder isKindOfClass: [UIViewController class]]){  
  13.             return (UIViewController *)responder;  
  14.         }  
  15.     }  
  16.     // 如果没有找到则返回nil  
  17.     return nil;  
  18. }  

       通过响应链查找视图控制器,nextResponder获取下一个响应者,响应者顺序为:



2、设置与取消第一响应者

[objc]  view plain  copy
  1. //  
  2. //  ZMFirstResponderView.m  
  3. //  ZMResponderDemo  
  4. //  
  5. //  Created by ZengZhiming on 2017/5/12.  
  6. //  Copyright © 2017年 菜鸟基地. All rights reserved.  
  7. //  
  8.   
  9. #import "ZMFirstResponderView.h"  
  10.   
  11. @implementation ZMFirstResponderView  
  12.   
  13. /** 演示设置为第一响应者 */  
  14. - (void)setBecomeFirstResponder {  
  15.     // 判断对象是否已经是第一响应者  
  16.     if ([self isFirstResponder]) {  
  17.         return;  
  18.     }  
  19.     // 判断对象是否允许成为第一响应者  
  20.     if ([self canBecomeFirstResponder]) {  
  21.         // 设置成为第一响应者  
  22.         [self becomeFirstResponder];  
  23.     }  
  24. }  
  25.   
  26. /** 演示放弃第一响应者 */  
  27. - (void)setResignFirstResponder {  
  28.     // 判断对象是否不是第一响应者  
  29.     if (![self isFirstResponder]) {  
  30.         return;  
  31.     }  
  32.     // 判断对象是否允许放弃第一响应者  
  33.     if ([self canResignFirstResponder]) {  
  34.         // 设置放弃第一响应者  
  35.         [self resignFirstResponder];  
  36.     }  
  37. }  
  38.   
  39. /** 重写方法,允许对象成为第一响应者 */  
  40. - (BOOL)canBecomeFirstResponder {  
  41.     return YES;  
  42. }  
  43.   
  44. @end  
      UIView默认不允许设置为第一响应者,因此设置UIView为第一响应者需要重写canBecomeFirstResponder方法并返回YES。 设置为第一响应者后,对象则可以接受远程控制事件进行处理(如耳机线控)。 UITextField、UITextView成为第一响应者后会弹出输入键盘,取消第一响应者则会隐藏输入键盘。


3、触摸相关方法,一般用于响应屏幕触摸

[objc]  view plain  copy
  1. /** 手指按下时响应 */  
  2. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {  
  3.     [super touchesBegan:touches withEvent:event];  
  4.     NSLog(@"--->手指按下时响应");  
  5. }  
  6.   
  7. /** 手指移动时响应 */  
  8. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {  
  9.     [super touchesMoved:touches withEvent:event];  
  10.     NSLog(@"--->手指移动时响应");  
  11. }  
  12.   
  13. /** 手指抬起时响应 */  
  14. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {  
  15.     [super touchesEnded:touches withEvent:event];  
  16.     NSLog(@"--->手指抬起时响应");  
  17. }  
  18.   
  19. /** 触摸取消(意外中断, 如:电话, Home键退出等) */  
  20. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {  
  21.     [super touchesCancelled:touches withEvent:event];  
  22.     NSLog(@"--->取消触摸响应");  
  23. }  

4、加速相关方法,一般用于摇一摇、运动事件监听等

[objc]  view plain  copy
  1. /** 开始加速 */  
  2. - (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {  
  3.     [super motionBegan:motion withEvent:event];  
  4.     NSLog(@"--->开始加速");  
  5. }  
  6.   
  7. /** 结束加速 */  
  8. - (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {  
  9.     [super motionEnded:motion withEvent:event];  
  10.     NSLog(@"--->结束加速");  
  11. }  
  12.   
  13. /** 加速取消(意外中断, 如:电话, Home键退出等) */  
  14. - (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {  
  15.     [super motionCancelled:motion withEvent:event];  
  16.     NSLog(@"--->加速取消");  
  17. }  

5、远程控制方法,一般用于耳机线控

[objc]  view plain  copy
  1. //  
  2. //  ZMAudioView.m  
  3. //  ZMResponderDemo  
  4. //  
  5. //  Created by ZengZhiming on 2017/5/12.  
  6. //  Copyright © 2017年 菜鸟基地. All rights reserved.  
  7. //  
  8.   
  9. #import "ZMAudioView.h"  
  10. #import <AVFoundation/AVFoundation.h>  
  11.   
  12. @implementation ZMAudioView  
  13.   
  14. - (instancetype)initWithFrame:(CGRect)frame  
  15. {  
  16.     self = [super initWithFrame:frame];  
  17.     if (self) {  
  18.         // 启动接受远程事件  
  19.         [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];  
  20.         // 设置成为第一响应者  
  21.         [self becomeFirstResponder];  
  22.         // 播放一段静音文件,使APP获取音频的控制权  
  23.         NSURL *audioURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"mute_60s" ofType:@"mp3"]];  
  24.         AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil];  
  25.         [audioPlayer play];  
  26.     }  
  27.     return self;  
  28. }  
  29.   
  30. /** 允许对象成为第一响应者 */  
  31. - (BOOL)canBecomeFirstResponder {  
  32.     return YES;  
  33. }  
  34.   
  35. /** 远程控制事件响应 */  
  36. - (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {  
  37.     NSLog(@"--->耳机线控响应");  
  38. }  
  39.   
  40. - (void)dealloc {  
  41.     // 停止接受远程事件  
  42.     [[UIApplication sharedApplication] endReceivingRemoteControlEvents];  
  43.     // 放弃第一响应者  
  44.     [self resignFirstResponder];  
  45. }  
  46.   
  47. @end  

耳机线控要注意三点要素:
(1)启动接受远程事件:[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
(2)设置成为第一响应者(UIViewController,AppDelegate中不需要设置)

[objc]  view plain  copy
  1. // 设置成为第一响应者  
  2. [self becomeFirstResponder];  
  3.   
  4. /** 允许对象成为第一响应者 */  
  5. - (BOOL)canBecomeFirstResponder {  
  6.     return YES;  
  7. }  

(3)获取音频的控制权

[objc]  view plain  copy
  1. // 播放一段静音文件,使APP获取音频的控制权  
  2. NSURL *audioURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"mute_60s" ofType:@"mp3"]];  
  3. AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil];  
  4. [audioPlayer play];  


6、在UILabel中实现长按菜单(复制、粘贴等)

[objc]  view plain  copy
  1. //  
  2. //  ZMMenuLabel.m  
  3. //  ZMResponderDemo  
  4. //  
  5. //  Created by ZengZhiming on 2017/5/15.  
  6. //  Copyright © 2017年 菜鸟基地. All rights reserved.  
  7. //  
  8.   
  9. #import "ZMMenuLabel.h"  
  10.   
  11. @implementation ZMMenuLabel  
  12.   
  13. - (instancetype)initWithFrame:(CGRect)frame  
  14. {  
  15.     self = [super initWithFrame:frame];  
  16.     if (self) {  
  17.         // 启用用户交互  
  18.         self.userInteractionEnabled = YES;  
  19.         // 添加长按手势  
  20.         UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMenu:)];  
  21.         longPressGesture.minimumPressDuration = 0.2;  
  22.         [self addGestureRecognizer:longPressGesture];  
  23.     }  
  24.     return self;  
  25. }  
  26.   
  27. /** 允许对象成为第一响应者 */  
  28. - (BOOL)canBecomeFirstResponder {  
  29.     return YES;  
  30. }  
  31.   
  32. /** 长按响应 */  
  33. - (void)longPressMenu:(UILongPressGestureRecognizer *)sender {  
  34.     if (sender.state == UIGestureRecognizerStateBegan) {  
  35.         // 设置成为第一响应者  
  36.         [self becomeFirstResponder];  
  37.         // 显示菜单  
  38.         UIMenuController *menuCtrl = [UIMenuController sharedMenuController];  
  39.         [menuCtrl setTargetRect:self.frame inView:self.superview];  
  40.         [menuCtrl setMenuVisible:YES animated:YES];  
  41.     }  
  42. }  
  43.   
  44. /** 返回需要显示的菜单按钮 */  
  45. - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {  
  46.     // 只显示复制、粘贴按钮  
  47.     if (action == @selector(copy:) || action == @selector(paste:)) {  
  48.         return YES;  
  49.     }  
  50.     return NO;  
  51. }  
  52.   
  53. /** 实现复制方法 */  
  54. - (void)copy:(id)sender {  
  55.     UIPasteboard *paste = [UIPasteboard generalPasteboard];  
  56.     paste.string = self.text;  
  57. }  
  58.   
  59. /** 实现粘贴方法 */  
  60. - (void)paste:(id)sender {  
  61.     UIPasteboard *paste = [UIPasteboard generalPasteboard];  
  62.     self.text = paste.string;  
  63. }  
  64.   
  65. @end  
为UILabel添加长按菜单需要注意几点:

(1)启用用户交互:self.userInteractionEnabled = YES;

(2)在显示菜单之前设置对象成为第一响应者(UIViewController,AppDelegate中不需要设置)

[objc]  view plain  copy
  1. /** 允许对象成为第一响应者 */  
  2. - (BOOL)canBecomeFirstResponder {  
  3.     return YES;  
  4. }  
  5.   
  6. // 设置成为第一响应者  
  7. [self becomeFirstResponder];  

(3)返回菜单需要显示的按钮,并重写实现对应方法

[objc]  view plain  copy
  1. /** 返回需要显示的菜单按钮 */  
  2. - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {  
  3.     // 只显示复制、粘贴按钮  
  4.     if (action == @selector(copy:) || action == @selector(paste:)) {  
  5.         return YES;  
  6.     }  
  7.     return NO;  
  8. }  
  9.   
  10. /** 实现复制方法 */  
  11. - (void)copy:(id)sender {  
  12.     UIPasteboard *paste = [UIPasteboard generalPasteboard];  
  13.     paste.string = self.text;  
  14. }  
  15.   
  16. /** 实现粘贴方法 */  
  17. - (void)paste:(id)sender {  
  18.     UIPasteboard *paste = [UIPasteboard generalPasteboard];  
  19.     self.text = paste.string;  
  20. }  
(4)注册长按手势,显示菜单
[objc]  view plain  copy
  1. // 添加长按手势  
  2. UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMenu:)];  
  3. longPressGesture.minimumPressDuration = 0.2;  
  4. [self addGestureRecognizer:longPressGesture];  
  5.   
  6. /** 长按响应 */  
  7. - (void)longPressMenu:(UILongPressGestureRecognizer *)sender {  
  8.     if (sender.state == UIGestureRecognizerStateBegan) {  
  9.         // 设置成为第一响应者  
  10.         [self becomeFirstResponder];  
  11.         // 显示菜单  
  12.         UIMenuController *menuCtrl = [UIMenuController sharedMenuController];  
  13.         [menuCtrl setTargetRect:self.frame inView:self.superview];  
  14.         [menuCtrl setMenuVisible:YES animated:YES];  
  15.     }  
  16. }  

7、使用NSUndoManager实现画板撤销/重做功能

[objc]  view plain  copy
  1. /** ==============ZMDrawingBoardView.h文件=================== */  
  2.   
  3. #import <UIKit/UIKit.h>  
  4.   
  5. /** 画板View */  
  6. @interface ZMDrawingBoardView : UIView  
  7.   
  8. @end  
  9.   
  10.   
  11. /** 划线Model */  
  12. @interface ZMLineModel : NSObject  
  13.   
  14. @property (nonatomic) CGPoint begin;  
  15. @property (nonatomic) CGPoint end;  
  16.   
  17. @end  
  18.   
  19.   
  20. /** ==============ZMDrawingBoardView.m文件=================== */  
  21.   
  22. #import "ZMDrawingBoardView.h"  
  23.   
  24. /** 画板View */  
  25. @interface ZMDrawingBoardView ()  
  26.   
  27. @property (nonatomicstrongZMLineModel *currentLine;  
  28. @property (nonatomicstrong) NSMutableArray<ZMLineModel *> *toucheArray;  
  29.   
  30. @end  
  31.   
  32. @implementation ZMDrawingBoardView  
  33.   
  34.   
  35. - (instancetype)initWithFrame:(CGRect)frame  
  36. {  
  37.     self = [super initWithFrame:frame];  
  38.     if (self) {  
  39.         [self initSubView];  
  40.         self.backgroundColor = [UIColor whiteColor];  
  41.         self.toucheArray = [NSMutableArray array];  
  42.     }  
  43.     return self;  
  44. }  
  45.   
  46. /** 绘制画板 */  
  47. - (void)drawRect:(CGRect)rect {  
  48.     // 获得上下文  
  49.     CGContextRef context = UIGraphicsGetCurrentContext();  
  50.     // 设置样式  
  51.     CGContextSetLineCap(context, kCGLineCapSquare);  
  52.     // 设置宽度  
  53.     CGContextSetLineWidth(context, 5.0);  
  54.     // 设置颜色  
  55.     CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);  
  56.       
  57.     for (ZMLineModel *line in self.toucheArray) {  
  58.         // 开始绘制  
  59.         CGContextBeginPath(context);  
  60.         // 移动画笔到起点  
  61.         CGContextMoveToPoint(context, line.begin.x, line.begin.y);  
  62.         // 添加下一点  
  63.         CGContextAddLineToPoint(context, line.end.x, line.end.y);  
  64.         // 绘制完成  
  65.         CGContextStrokePath(context);  
  66.     }  
  67. }  
  68.   
  69. /** 划线开始 */  
  70. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event  
  71. {  
  72.     // 标记开始撤销分组  
  73.     [self.undoManager beginUndoGrouping];  
  74.       
  75.     for (UITouch *touch in touches) {  
  76.         // 记录起始点  
  77.         CGPoint locTouch = [touch locationInView:self];  
  78.         _currentLine = [[ZMLineModel alloc] init];  
  79.         _currentLine.begin = locTouch;  
  80.         _currentLine.end = locTouch;  
  81.     }  
  82.       
  83. }  
  84.   
  85. /** 划线移动 */  
  86. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event  
  87. {  
  88.     for (UITouch *touch in touches) {  
  89.         // 添加线条  
  90.         CGPoint locTouch = [touch locationInView:self];  
  91.         _currentLine.end = locTouch;  
  92.         [self addLine:_currentLine];  
  93.         // 当前线条  
  94.         _currentLine = [[ZMLineModel alloc] init];  
  95.         _currentLine.begin = locTouch;  
  96.         _currentLine.end = locTouch;  
  97.     }  
  98. }  
  99.   
  100. /** 划线结束 */  
  101. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event  
  102. {  
  103.     // 结束标记撤销分组  
  104.     [self.undoManager endUndoGrouping];  
  105. }  
  106.   
  107. /** 添加划线 */  
  108. - (void)addLine:(ZMLineModel *)line  
  109. {  
  110.     // 添加划线并重绘画板  
  111.     [self.toucheArray addObject:line];  
  112.     [self setNeedsDisplay];  
  113.     // 注册撤销方法  
  114.     [[self.undoManager prepareWithInvocationTarget:self] removeLine:line];  
  115. }  
  116.   
  117. /** 移除划线 */  
  118. - (void)removeLine:(ZMLineModel *)line  
  119. {  
  120.     if ([self.toucheArray containsObject:line]) {  
  121.         // 移除划线并重绘画板  
  122.         [self.toucheArray removeObject:line];  
  123.         [self setNeedsDisplay];  
  124.         // 注册撤销方法  
  125.         [[self.undoManager prepareWithInvocationTarget:self] addLine:line];  
  126.     }  
  127. }  
  128.   
  129. /** 撤销按钮点击响应 */  
  130. - (void)undoButtonAction:(id)sender {  
  131.     if ([self.undoManager canUndo]) {  
  132.         [self.undoManager undo];  
  133.     }  
  134. }  
  135.   
  136. /** 重做按钮点击响应 */  
  137. - (void)redoButtonAction:(id)sender {  
  138.     if ([self.undoManager canRedo]) {  
  139.         [self.undoManager redo];  
  140.     }  
  141. }  
  142.   
  143. /** 初始化子控件 */  
  144. - (void)initSubView {  
  145.     // 撤销按钮  
  146.     UIButton *undoButton = [UIButton buttonWithType:UIButtonTypeSystem];  
  147.     undoButton.frame = CGRectMake(0647050);  
  148.     [undoButton setTitle:@"undo撤销" forState:UIControlStateNormal];  
  149.     [undoButton sizeToFit];  
  150.     [undoButton addTarget:self action:@selector(undoButtonAction:) forControlEvents:UIControlEventTouchUpInside];  
  151.     [self addSubview:undoButton];  
  152.     // 重做按钮  
  153.     UIButton *redoButton = [UIButton buttonWithType:UIButtonTypeSystem];  
  154.     redoButton.frame = CGRectMake(CGRectGetWidth(self.frame)-70647050);  
  155.     [redoButton setTitle:@"redo重做" forState:UIControlStateNormal];  
  156.     [redoButton sizeToFit];  
  157.     [redoButton addTarget:self action:@selector(redoButtonAction:) forControlEvents:UIControlEventTouchUpInside];  
  158.     [self addSubview:redoButton];  
  159. }  
  160.   
  161. @end  
实现撤销/重做注意以下几点:

(1)在调用方法时需要添加注册一个对应的撤销方法

[objc]  view plain  copy
  1. // 注册撤销方法  
  2. [[self.undoManager prepareWithInvocationTarget:self] removeLine:line];  
(2)撤销/ 重做只需要调用undoManager中的相应方法即可

[objc]  view plain  copy
  1. /** 撤销按钮点击响应 */  
  2. - (void)undoButtonAction:(id)sender {  
  3.     if ([self.undoManager canUndo]) {  
  4.         [self.undoManager undo];  
  5.     }  
  6. }  
  7.   
  8. /** 重做按钮点击响应 */  
  9. - (void)redoButtonAction:(id)sender {  
  10.     if ([self.undoManager canRedo]) {  
  11.         [self.undoManager redo];  
  12.     }  
  13. }  
(3)如果需要多个动作一起撤销则需要标记分组

[objc]  view plain  copy
  1. // 标记开始撤销分组  
  2. [self.undoManager beginUndoGrouping];  
  3. // 结束标记撤销分组  
  4. [self.undoManager endUndoGrouping];  

8、自定义快捷键

[objc]  view plain  copy
  1. //  
  2. //  ZMKeyCommandView.m  
  3. //  ZMResponderDemo  
  4. //  
  5. //  Created by ZengZhiming on 2017/5/17.  
  6. //  Copyright © 2017年 菜鸟基地. All rights reserved.  
  7. //  
  8.   
  9. #import "ZMKeyCommandView.h"  
  10.   
  11. @implementation ZMKeyCommandView  
  12.   
  13. - (instancetype)initWithFrame:(CGRect)frame  
  14. {  
  15.     self = [super initWithFrame:frame];  
  16.     if (self) {  
  17.         // 设置成为第一响应者  
  18.         [self becomeFirstResponder];  
  19.     }  
  20.     return self;  
  21. }  
  22.   
  23. /** 允许对象成为第一响应者 */  
  24. - (BOOL)canBecomeFirstResponder {  
  25.     return YES;  
  26. }  
  27.   
  28. /** 返回快捷命令数组 */  
  29. -(NSArray<UIKeyCommand *> *)keyCommands {  
  30.     return @[  
  31.              [UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:UIKeyModifierShift action:@selector(pressedShiftAndEscapeKey:) discoverabilityTitle:@"自定义[Shift+Esc]快捷键"],  
  32.              [UIKeyCommand keyCommandWithInput:@"a" modifierFlags:UIKeyModifierShift action:@selector(pressedShiftAndAKey:) discoverabilityTitle:@"自定义[Shift+A]快捷键"]  
  33.              ];  
  34. }  
  35.   
  36. /** Shift+Esc快捷命令响应 */  
  37. -(void)pressedShiftAndEscapeKey:(UIKeyCommand *)keyCommand {  
  38.     UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:keyCommand.discoverabilityTitle message:[NSString stringWithFormat:@"按下快捷辅键:[%@]", keyCommand.input] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];  
  39.     [alertView show];  
  40. }  
  41.   
  42. /** Shift+A快捷命令响应 */  
  43. -(void)pressedShiftAndAKey:(UIKeyCommand *)keyCommand {  
  44.     UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:keyCommand.discoverabilityTitle message:[NSString stringWithFormat:@"按下快捷辅键:[%@]", keyCommand.input] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];  
  45.     [alertView show];  
  46. }  
  47.   
  48. @end  
自定义快捷键需要注意两点:

(1)设置对象成为第一响应者(UIViewController,AppDelegate中不需要设置)

[objc]  view plain  copy
  1. // 设置成为第一响应者  
  2. [self becomeFirstResponder];  
  3.   
  4. /** 允许对象成为第一响应者 */  
  5. - (BOOL)canBecomeFirstResponder {  
  6.     return YES;  
  7. }  
(2)重写 keyCommands 返回快捷命令组合
[objc]  view plain  copy
  1. /** 返回快捷命令数组 */  
  2. -(NSArray<UIKeyCommand *> *)keyCommands {  
  3.     return @[  
  4.              [UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:UIKeyModifierShift action:@selector(pressedShiftAndEscapeKey:) discoverabilityTitle:@"自定义[Shift+Esc]快捷键"],  
  5.              [UIKeyCommand keyCommandWithInput:@"a" modifierFlags:UIKeyModifierShift action:@selector(pressedShiftAndAKey:) discoverabilityTitle:@"自定义[Shift+A]快捷键"]  
  6.              ];  
  7. }  


9、自定义UITextField输入键盘 

[objc]  view plain  copy
  1. //  
  2. //  ZMCustomInputView.m  
  3. //  ZMResponderDemo  
  4. //  
  5. //  Created by ZengZhiming on 2017/5/18.  
  6. //  Copyright © 2017年 菜鸟基地. All rights reserved.  
  7. //  
  8.   
  9. #import "ZMCustomInputView.h"  
  10.   
  11. #define MAIN_SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width   //!< 屏幕的Width  
  12.   
  13. @interface ZMCustomInputView ()  
  14.   
  15. @property (nonatomicstrongUITextField *textField;  
  16. @property (nonatomicstrongUIView *customInputView;  
  17. @property (nonatomicstrongUIToolbar *customAccessoryView;  
  18.   
  19. @end  
  20.   
  21. @implementation ZMCustomInputView  
  22.   
  23. - (instancetype)initWithFrame:(CGRect)frame  
  24. {  
  25.     self = [super initWithFrame:frame];  
  26.     if (self) {  
  27.         // 添加TextField  
  28.         [self addSubview:self.textField];  
  29.     }  
  30.     return self;  
  31. }  
  32.   
  33. /** 懒加载textField */  
  34. - (UITextField *)textField {  
  35.     if (!_textField) {  
  36.         // 初始化textField  
  37.         _textField = [[UITextField alloc]initWithFrame:CGRectMake(50100, MAIN_SCREEN_WIDTH - 10030)];  
  38.         _textField.borderStyle = UITextBorderStyleRoundedRect;  
  39.         _textField.placeholder = @"测试";  
  40.         // 设置自定义键盘View  
  41.         _textField.inputView = self.customInputView;  
  42.         _textField.inputAccessoryView = self.customAccessoryView;  
  43.     }  
  44.     return _textField;  
  45. }  
  46.   
  47. /** 懒加载customInputView */  
  48. - (UIView *)customInputView {  
  49.     if (!_customInputView) {  
  50.         _customInputView = [[UIView alloc]initWithFrame:CGRectMake(00, MAIN_SCREEN_WIDTH, 220)];  
  51.         _customInputView.backgroundColor = [UIColor lightGrayColor];  
  52.         UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0100, MAIN_SCREEN_WIDTH, 40)];  
  53.         label.textAlignment = NSTextAlignmentCenter;  
  54.         label.text = @"自定义inputView";  
  55.         [_customInputView addSubview:label];  
  56.     }  
  57.     return _customInputView;  
  58. }  
  59.   
  60. /** 懒加载customAccessoryView */  
  61. - (UIToolbar *)customAccessoryView {  
  62.     if (!_customAccessoryView) {  
  63.         _customAccessoryView = [[UIToolbar alloc]initWithFrame:CGRectMake(00, MAIN_SCREEN_WIDTH, 40)];  
  64.         _customAccessoryView.barTintColor = [UIColor orangeColor];  
  65.         UIBarButtonItem *space = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];  
  66.         UIBarButtonItem *done = [[UIBarButtonItem alloc]initWithTitle:@"完成" style:UIBarButtonItemStyleDone target:self action:@selector(done)];  
  67.         [_customAccessoryView setItems:@[space, space, done]];  
  68.     }  
  69.     return _customAccessoryView;  
  70. }  
  71.   
  72. /** 响应完成按钮 */  
  73. - (void)done {  
  74.     [self.textField resignFirstResponder];  
  75. }  
  76.   
  77.   
  78. @end  

二、UIResponder.h详解

[objc]  view plain  copy
  1. //  
  2. //  UIResponder.h  
  3. //  ZMHeaderFile  
  4. //  
  5. //  Created by ZengZhiming on 2017/4/18.  
  6. //  Copyright © 2017年 菜鸟基地. All rights reserved.  
  7. //  
  8. //  详解 UIResponder.h  
  9. //  Version iOS 10.3  
  10. //  
  11.   
  12. #import <Foundation/Foundation.h>  
  13. #import <UIKit/UIKitDefines.h>  
  14. #import <UIKit/UIEvent.h>  
  15.   
  16. NS_ASSUME_NONNULL_BEGIN  
  17.   
  18. @class UIPress;  
  19. @class UIPressesEvent;  
  20.   
  21. #pragma mark - UIResponderStandardEditActions协议定义  
  22.   
  23. @protocol UIResponderStandardEditActions <NSObject>  
  24. @optional  
  25. /** 剪切事件 */  
  26. - (void)cut:(nullable id)sender NS_AVAILABLE_IOS(3_0);  
  27. /** 复制事件 */  
  28. - (void)copy:(nullable id)sender NS_AVAILABLE_IOS(3_0);  
  29. /** 粘贴事件 */  
  30. - (void)paste:(nullable id)sender NS_AVAILABLE_IOS(3_0);  
  31. /** 选择事件 */  
  32. - (void)select:(nullable id)sender NS_AVAILABLE_IOS(3_0);  
  33. /** 全选事件 */  
  34. - (void)selectAll:(nullable id)sender NS_AVAILABLE_IOS(3_0);  
  35. /** 删除事件 */  
  36. - (void)delete:(nullable id)sender NS_AVAILABLE_IOS(3_2);  
  37. /** 从左到右写入字符串(居左) */  
  38. - (void)makeTextWritingDirectionLeftToRight:(nullable id)sender NS_AVAILABLE_IOS(5_0);  
  39. /** 从右到左写入字符串(居右) */  
  40. - (void)makeTextWritingDirectionRightToLeft:(nullable id)sender NS_AVAILABLE_IOS(5_0);  
  41. /** 切换字体为黑体(粗体) */  
  42. - (void)toggleBoldface:(nullable id)sender NS_AVAILABLE_IOS(6_0);  
  43. /** 切换字体为斜体 */  
  44. - (void)toggleItalics:(nullable id)sender NS_AVAILABLE_IOS(6_0);  
  45. /** 给文字添加下划线 */  
  46. - (void)toggleUnderline:(nullable id)sender NS_AVAILABLE_IOS(6_0);  
  47.   
  48. /** 增加字体大小 */  
  49. - (void)increaseSize:(nullable id)sender NS_AVAILABLE_IOS(7_0);  
  50. /** 减小字体大小 */  
  51. - (void)decreaseSize:(nullable id)sender NS_AVAILABLE_IOS(7_0);  
  52.   
  53. @end  
  54.   
  55. #pragma mark - UIResponder类定义  
  56.   
  57. NS_CLASS_AVAILABLE_IOS(2_0@interface UIResponder : NSObject <UIResponderStandardEditActions>  
  58.   
  59. #pragma mark - 响应者相关方法  
  60.   
  61. /** 获取下一个响应者 */  
  62. #if UIKIT_DEFINE_AS_PROPERTIES  
  63. @property(nonatomicreadonly, nullable) UIResponder *nextResponder;  
  64. #else  
  65. - (nullable UIResponder *)nextResponder;  
  66. #endif  
  67.   
  68. /** 是否允许成为第一响应者。默认返回NO */  
  69. #if UIKIT_DEFINE_AS_PROPERTIES  
  70. @property(nonatomicreadonlyBOOL canBecomeFirstResponder;  
  71. #else  
  72. - (BOOL)canBecomeFirstResponder;  
  73. #endif  
  74. /** 设置成为第一响应者 */  
  75. - (BOOL)becomeFirstResponder;  
  76.   
  77. /** 是否允许放弃第一响应者。默认返回YES */  
  78. #if UIKIT_DEFINE_AS_PROPERTIES  
  79. @property(nonatomicreadonlyBOOL canResignFirstResponder;  
  80. #else  
  81. - (BOOL)canResignFirstResponder;  
  82. #endif  
  83. /** 设置放弃第一响应者 */  
  84. - (BOOL)resignFirstResponder;  
  85.   
  86. /** 判断对象是否是第一响应者 */  
  87. #if UIKIT_DEFINE_AS_PROPERTIES  
  88. @property(nonatomicreadonlyBOOL isFirstResponder;  
  89. #else  
  90. - (BOOL)isFirstResponder;  
  91. #endif  
  92.   
  93. #pragma mark - 触摸相关方法,一般用于响应屏幕触摸  
  94. /** 手指按下时响应 */  
  95. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;  
  96. /** 手指移动时响应 */  
  97. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;  
  98. /** 手指抬起时响应 */  
  99. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;  
  100. /** 取消(意外中断, 如:电话, 系统警告窗等) */  
  101. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;  
  102. /** 3DTouch响应(iOS9.1后使用) */  
  103. - (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches NS_AVAILABLE_IOS(9_1);  
  104.   
  105. #pragma mark - 深按相关方法,一般用于遥控器按键响应  
  106. /** 手指按压开始时响应 */  
  107. - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);  
  108. /** 手指按压位置移动时响应 */  
  109. - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);  
  110. /** 手指抬起接受按压时响应 */  
  111. - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);  
  112. /** 按压取消(意外中断, 如:电话, 系统警告窗等) */  
  113. - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);  
  114.   
  115. #pragma mark - 加速相关方法,一般用于摇一摇、运动事件监听等  
  116. /** 开始加速 */  
  117. - (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);  
  118. /** 结束加速 */  
  119. - (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);  
  120. /** 加速取消(意外中断, 如:电话, 系统警告窗等) */  
  121. - (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);  
  122.   
  123. /** 远程控制事件 */  
  124. - (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);  
  125.   
  126. /** 返回UIMenuController需要显示的控件(如:复制,粘贴等) */  
  127. - (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(3_0);  
  128.   
  129. /** 返回响应的操作目标对象 */  
  130. - (nullable id)targetForAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(7_0);  
  131.   
  132. /** 获取响应链就近共享撤消管理 */  
  133. @property(nullable, nonatomic,readonlyNSUndoManager *undoManager NS_AVAILABLE_IOS(3_0);  
  134.   
  135. @end  
  136.   
  137. /** 快捷主键枚举 */  
  138. typedef NS_OPTIONS(NSInteger, UIKeyModifierFlags) {  
  139.     UIKeyModifierAlphaShift     = 1 << 16,  //!< Alpha+Shift键.  
  140.     UIKeyModifierShift          = 1 << 17,  //!< Shift键.  
  141.     UIKeyModifierControl        = 1 << 18,  //!< Control键.  
  142.     UIKeyModifierAlternate      = 1 << 19,  //!< Alt键.  
  143.     UIKeyModifierCommand        = 1 << 20,  //!< Command键.  
  144.     UIKeyModifierNumericPad     = 1 << 21,  //!< Num键.  
  145. } NS_ENUM_AVAILABLE_IOS(7_0);  
  146.   
  147. #pragma mark - 快捷键对象  
  148.   
  149. NS_CLASS_AVAILABLE_IOS(7_0@interface UIKeyCommand : NSObject <NSCopying, NSSecureCoding>  
  150.   
  151. /** 初始化对象 */  
  152. - (instancetype)init NS_DESIGNATED_INITIALIZER;  
  153. /** 初始化对象 */  
  154. - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;  
  155.   
  156. /** 获取快捷辅键(如快捷命令【Command+A】中的 A 键) */  
  157. @property (nonatomic,readonlyNSString *input;  
  158. /** 获取快捷主键(如快捷命令【Command+A】中的 Command 键) */  
  159. @property (nonatomic,readonly) UIKeyModifierFlags modifierFlags;  
  160. /** 显示给用户的快捷键标题 */  
  161. @property (nullable,nonatomic,copyNSString *discoverabilityTitle NS_AVAILABLE_IOS(9_0);  
  162.   
  163. /** 创建一个快捷键命令 */  
  164. + (UIKeyCommand *)keyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action;  
  165.   
  166. /** 创建一个快捷键命令 */  
  167. + (UIKeyCommand *)keyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action discoverabilityTitle:(NSString *)discoverabilityTitle NS_AVAILABLE_IOS(9_0);  
  168.   
  169. @end  
  170.   
  171. #pragma mark - 响应快捷命令  
  172.   
  173. @interface UIResponder (UIResponderKeyCommands)  
  174. /** 返回快捷键命令数组 */  
  175. @property (nullable,nonatomic,readonly) NSArray<UIKeyCommand *> *keyCommands NS_AVAILABLE_IOS(7_0);  
  176. @end  
  177.   
  178. @class UIInputViewController;  
  179. @class UITextInputMode;  
  180. @class UITextInputAssistantItem;  
  181.   
  182. #pragma mark - 输入视图  
  183.   
  184. @interface UIResponder (UIResponderInputViewAdditions)  
  185.   
  186. /** 键盘输入视图(系统默认的,可以自定义) */  
  187. @property (nullable, nonatomicreadonlystrong) __kindof UIView *inputView NS_AVAILABLE_IOS(3_2);  
  188. /** 弹出键盘时附带的视图 */  
  189. @property (nullable, nonatomicreadonlystrong) __kindof UIView *inputAccessoryView NS_AVAILABLE_IOS(3_2);  
  190.   
  191. /** 输入助手配置键盘的快捷方式栏时使用 */  
  192. @property (nonnull, nonatomicreadonlystrongUITextInputAssistantItem *inputAssistantItem NS_AVAILABLE_IOS(9_0) __TVOS_PROHIBITED __WATCHOS_PROHIBITED;  
  193.   
  194. /** 键盘输入视图控制器 */  
  195. @property (nullable, nonatomicreadonlystrongUIInputViewController *inputViewController NS_AVAILABLE_IOS(8_0);  
  196. /** 弹出键盘时附带的视图的视图控制器 */  
  197. @property (nullable, nonatomicreadonlystrongUIInputViewController *inputAccessoryViewController NS_AVAILABLE_IOS(8_0);  
  198.   
  199. /** 文本输入模式 */  
  200. @property (nullable, nonatomicreadonlystrongUITextInputMode *textInputMode NS_AVAILABLE_IOS(7_0);  
  201.   
  202. /** 文本输入模式标识 */  
  203. @property (nullable, nonatomicreadonlystrongNSString *textInputContextIdentifier NS_AVAILABLE_IOS(7_0);  
  204. /** 根据设置的标识清除指定的文本输入模式 */  
  205. + (void)clearTextInputContextIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(7_0);  
  206.   
  207. /** 重新刷新键盘输入视图 */  
  208. - (void)reloadInputViews NS_AVAILABLE_IOS(3_2);  
  209.   
  210. @end  
  211.   
  212. /** 特殊快捷辅键定义 */  
  213. UIKIT_EXTERN NSString *const UIKeyInputUpArrow         NS_AVAILABLE_IOS(7_0); //!< 上按键.  
  214. UIKIT_EXTERN NSString *const UIKeyInputDownArrow       NS_AVAILABLE_IOS(7_0); //!< 下按键.  
  215. UIKIT_EXTERN NSString *const UIKeyInputLeftArrow       NS_AVAILABLE_IOS(7_0); //!< 左按键.  
  216. UIKIT_EXTERN NSString *const UIKeyInputRightArrow      NS_AVAILABLE_IOS(7_0); //!< 右按键  
  217. UIKIT_EXTERN NSString *const UIKeyInputEscape          NS_AVAILABLE_IOS(7_0); //!< Esc按键.  
  218.   
  219. #pragma mark - 响应者活动  
  220.   
  221. @interface UIResponder (ActivityContinuation)  
  222. /** 用户活动 */  
  223. @property (nullable, nonatomicstrongNSUserActivity *userActivity NS_AVAILABLE_IOS(8_0);  
  224. /** 更新用户活动 */  
  225. - (void)updateUserActivityState:(NSUserActivity *)activity NS_AVAILABLE_IOS(8_0);  
  226. /** 恢复用户活动 */  
  227. - (void)restoreUserActivityState:(NSUserActivity *)activity NS_AVAILABLE_IOS(8_0);  
  228. @end  
  229.   
  230. NS_ASSUME_NONNULL_END  


Demo地址:https://github.com/zeng-zhiming/ZMResponderDemo


  原文地址: http://blog.csdn.net/zeng_zhiming/article/details/71747881
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值