iOS学习——(转)UIResponder详解

2018-02-23 21:18:09来源:cnblogs.com作者:mukekeheart人点击

分享

本文转载自:ios开发 之 UIResponder详解 

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

一 UIResponder.h文件注释版

  首先,我们对UIResponder.h文件进行了研究和解释,并对各模块进行了注释添加,方便我们在阅读和学习时候的理解。在学习了UIView、NSObject的.h文件之后,我们发现这些基类的.h文件的组织架构基本类似,最初是定义该类中需要用到的一些枚举类型数据,然后对相应的协议进行定义,接着就是对本类进行定义,一些基本的属性和方法的定义,最后就是对本类做各种功能性的分类。

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

 二 UIResponder的使用

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

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

 

/** *  查找视图的视图控制器 * *  @param view 视图 * *  @return 返回视图的控制器 */- (UIViewController *)getControllerFromView:(UIView *)view {    // 遍历响应者链。返回第一个找到视图控制器    UIResponder *responder = view;    while ((responder = [responder nextResponder])){        if ([responder isKindOfClass: [UIViewController class]]){            return (UIViewController *)responder;        }    }    // 如果没有找到则返回nil    return nil;}

2.2 设置与取消第一响应者

  UIView默认不允许设置为第一响应者,因此设置UIView为第一响应者需要重写canBecomeFirstResponder方法并返回YES。 设置为第一响应者后,对象则可以接受远程控制事件进行处理(如耳机线控)。UITextField、UITextView成为第一响应者后会弹出输入键盘,取消第一响应者则会隐藏输入键盘。 

 1 //  ZMFirstResponderView.m 2  3 #import "ZMFirstResponderView.h" 4  5 @implementation ZMFirstResponderView 6  7 /** 演示设置为第一响应者 */ 8 - (void)setBecomeFirstResponder { 9     // 判断对象是否已经是第一响应者10     if ([self isFirstResponder]) {11         return;12     }13     // 判断对象是否允许成为第一响应者14     if ([self canBecomeFirstResponder]) {15         // 设置成为第一响应者16         [self becomeFirstResponder];17     }18 }19 20 /** 演示放弃第一响应者 */21 - (void)setResignFirstResponder {22     // 判断对象是否不是第一响应者23     if (![self isFirstResponder]) {24         return;25     }26     // 判断对象是否允许放弃第一响应者27     if ([self canResignFirstResponder]) {28         // 设置放弃第一响应者29         [self resignFirstResponder];30     }31 }32 33 /** 重写方法,允许对象成为第一响应者 */34 - (BOOL)canBecomeFirstResponder {35     return YES;36 }37 38 @end

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

/** 手指按下时响应 */- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {    [super touchesBegan:touches withEvent:event];    NSLog(@"--->手指按下时响应");}/** 手指移动时响应 */- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {    [super touchesMoved:touches withEvent:event];    NSLog(@"--->手指移动时响应");}/** 手指抬起时响应 */- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {    [super touchesEnded:touches withEvent:event];    NSLog(@"--->手指抬起时响应");}/** 触摸取消(意外中断, 如:电话, Home键退出等) */- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {    [super touchesCancelled:touches withEvent:event];    NSLog(@"--->取消触摸响应");}

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

/** 开始加速 */- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {    [super motionBegan:motion withEvent:event];    NSLog(@"--->开始加速");}/** 结束加速 */- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {    [super motionEnded:motion withEvent:event];    NSLog(@"--->结束加速");}/** 加速取消(意外中断, 如:电话, Home键退出等) */- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {    [super motionCancelled:motion withEvent:event];    NSLog(@"--->加速取消");}

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

耳机线控要注意三点要素:

  1. 启动接受远程事件:[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
  2. 设置成为第一响应者(UIViewController,AppDelegate中不需要设置)
  3. 获取音频的控制权
//  ZMAudioView.m#import "ZMAudioView.h"#import <AVFoundation/AVFoundation.h>@implementation ZMAudioView- (instancetype)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        // 1 启动接受远程事件        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];        //2  设置成为第一响应者        [self becomeFirstResponder];        // 3 播放一段静音文件,使APP获取音频的控制权        NSURL *audioURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"mute_60s" ofType:@"mp3"]];        AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil];        [audioPlayer play];    }    return self;}/** 允许对象成为第一响应者 */- (BOOL)canBecomeFirstResponder {    return YES;}/** 远程控制事件响应 */- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {    NSLog(@"--->耳机线控响应");}- (void)dealloc {    // 停止接受远程事件    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];    // 放弃第一响应者    [self resignFirstResponder];}@end

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

为UILabel添加长按菜单需要注意几点:

  1. 启用用户交互:self.userInteractionEnabled = YES;
  2. 在显示菜单之前设置对象成为第一响应者(UIViewController,AppDelegate中不需要设置)
  3. 返回菜单需要显示的按钮,并重写实现对应方法
  4. 注册长按手势,显示菜单
//  ZMMenuLabel.m#import "ZMMenuLabel.h"@implementation ZMMenuLabel- (instancetype)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        // 启用用户交互        self.userInteractionEnabled = YES;        // 添加长按手势        UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMenu:)];        longPressGesture.minimumPressDuration = 0.2;        [self addGestureRecognizer:longPressGesture];    }    return self;}/** 允许对象成为第一响应者 */- (BOOL)canBecomeFirstResponder {    return YES;}/** 长按响应 */- (void)longPressMenu:(UILongPressGestureRecognizer *)sender {    if (sender.state == UIGestureRecognizerStateBegan) {        // 设置成为第一响应者        [self becomeFirstResponder];        // 显示菜单        UIMenuController *menuCtrl = [UIMenuController sharedMenuController];        [menuCtrl setTargetRect:self.frame inView:self.superview];        [menuCtrl setMenuVisible:YES animated:YES];    }}/** 返回需要显示的菜单按钮 */- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {    // 只显示复制、粘贴按钮    if (action == @selector(copy:) || action == @selector(paste:)) {        return YES;    }    return NO;}/** 实现复制方法 */- (void)copy:(id)sender {    UIPasteboard *paste = [UIPasteboard generalPasteboard];    paste.string = self.text;}/** 实现粘贴方法 */- (void)paste:(id)sender {    UIPasteboard *paste = [UIPasteboard generalPasteboard];    self.text = paste.string;}@end

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

实现撤销/重做注意以下几点:

  1. 在调用方法时需要添加注册一个对应的撤销方法
  2. 撤销/ 重做只需要调用undoManager中的相应方法即可
  3. 如果需要多个动作一起撤销则需要标记分组
  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 (nonatomic, strong) ZMLineModel *currentLine; 28 @property (nonatomic, strong) 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 *)event102 {103     // 结束标记撤销分组104     [self.undoManager endUndoGrouping];105 }106 107 /** 添加划线 */108 - (void)addLine:(ZMLineModel *)line109 {110     // 添加划线并重绘画板111     [self.toucheArray addObject:line];112     [self setNeedsDisplay];113     // 注册撤销方法114     [[self.undoManager prepareWithInvocationTarget:self] removeLine:line];115 }116 117 /** 移除划线 */118 - (void)removeLine:(ZMLineModel *)line119 {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(0, 64, 70, 50);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)-70, 64, 70, 50);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

2.8 自定义快捷键

自定义快捷键需要注意两点:

  1. 设置对象成为第一响应者(UIViewController,AppDelegate中不需要设置)
  2. 重写keyCommands返回快捷命令组合
//  ZMKeyCommandView.m#import "ZMKeyCommandView.h"@implementation ZMKeyCommandView- (instancetype)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        // 设置成为第一响应者        [self becomeFirstResponder];    }    return self;}/** 允许对象成为第一响应者 */- (BOOL)canBecomeFirstResponder {    return YES;}/** 返回快捷命令数组 */-(NSArray<UIKeyCommand *> *)keyCommands {    return @[             [UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:UIKeyModifierShift action:@selector(pressedShiftAndEscapeKey:) discoverabilityTitle:@"自定义[Shift+Esc]快捷键"],             [UIKeyCommand keyCommandWithInput:@"a" modifierFlags:UIKeyModifierShift action:@selector(pressedShiftAndAKey:) discoverabilityTitle:@"自定义[Shift+A]快捷键"]             ];}/** Shift+Esc快捷命令响应 */-(void)pressedShiftAndEscapeKey:(UIKeyCommand *)keyCommand {    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:keyCommand.discoverabilityTitle message:[NSString stringWithFormat:@"按下快捷辅键:[%@]", keyCommand.input] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];    [alertView show];}/** Shift+A快捷命令响应 */-(void)pressedShiftAndAKey:(UIKeyCommand *)keyCommand {    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:keyCommand.discoverabilityTitle message:[NSString stringWithFormat:@"按下快捷辅键:[%@]", keyCommand.input] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];    [alertView show];}@end

2.9 自定义UITextField输入键盘

 1 //  ZMCustomInputView.m 2  3 #import "ZMCustomInputView.h" 4  5 #define MAIN_SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width   //!< 屏幕的Width 6  7 @interface ZMCustomInputView () 8  9 @property (nonatomic, strong) UITextField *textField;10 @property (nonatomic, strong) UIView *customInputView;11 @property (nonatomic, strong) UIToolbar *customAccessoryView;12 13 @end14 15 @implementation ZMCustomInputView16 17 - (instancetype)initWithFrame:(CGRect)frame18 {19     self = [super initWithFrame:frame];20     if (self) {21         // 添加TextField22         [self addSubview:self.textField];23     }24     return self;25 }26 27 /** 懒加载textField */28 - (UITextField *)textField {29     if (!_textField) {30         // 初始化textField31         _textField = [[UITextField alloc]initWithFrame:CGRectMake(50, 100, MAIN_SCREEN_WIDTH - 100, 30)];32         _textField.borderStyle = UITextBorderStyleRoundedRect;33         _textField.placeholder = @"测试";34         // 设置自定义键盘View35         _textField.inputView = self.customInputView;36         _textField.inputAccessoryView = self.customAccessoryView;37     }38     return _textField;39 }40 41 /** 懒加载customInputView */42 - (UIView *)customInputView {43     if (!_customInputView) {44         _customInputView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, MAIN_SCREEN_WIDTH, 220)];45         _customInputView.backgroundColor = [UIColor lightGrayColor];46         UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0, 100, MAIN_SCREEN_WIDTH, 40)];47         label.textAlignment = NSTextAlignmentCenter;48         label.text = @"自定义inputView";49         [_customInputView addSubview:label];50     }51     return _customInputView;52 }53 54 /** 懒加载customAccessoryView */55 - (UIToolbar *)customAccessoryView {56     if (!_customAccessoryView) {57         _customAccessoryView = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, MAIN_SCREEN_WIDTH, 40)];58         _customAccessoryView.barTintColor = [UIColor orangeColor];59         UIBarButtonItem *space = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];60         UIBarButtonItem *done = [[UIBarButtonItem alloc]initWithTitle:@"完成" style:UIBarButtonItemStyleDone target:self action:@selector(done)];61         [_customAccessoryView setItems:@[space, space, done]];62     }63     return _customAccessoryView;64 }65 66 /** 响应完成按钮 */67 - (void)done {68     [self.textField resignFirstResponder];69 }70 71 72 @end

  

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台