iOS学习——(转)UIResponder详解

本文转载自: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文件的组织架构基本类似,最初是定义该类中需要用到的一些枚举类型数据,然后对相应的协议进行定义,接着就是对本类进行定义,一些基本的属性和方法的定义,最后就是对本类做各种功能性的分类。

 //
// UIResponder.h #import <Foundation/Foundation.h>
#import <UIKit/UIKitDefines.h>
#import <UIKit/UIEvent.h> NS_ASSUME_NONNULL_BEGIN @class UIPress;
@class UIPressesEvent; #pragma mark - UIResponderStandardEditActions协议定义 @protocol UIResponderStandardEditActions <NSObject>
@optional
/** 剪切事件 */
- (void)cut:(nullable id)sender NS_AVAILABLE_IOS(3_0);
/** 复制事件 */
- (void)copy:(nullable id)sender NS_AVAILABLE_IOS(3_0);
/** 粘贴事件 */
- (void)paste:(nullable id)sender NS_AVAILABLE_IOS(3_0);
/** 选择事件 */
- (void)select:(nullable id)sender NS_AVAILABLE_IOS(3_0);
/** 全选事件 */
- (void)selectAll:(nullable id)sender NS_AVAILABLE_IOS(3_0);
/** 删除事件 */
- (void)delete:(nullable id)sender NS_AVAILABLE_IOS(3_2);
/** 从左到右写入字符串(居左) */
- (void)makeTextWritingDirectionLeftToRight:(nullable id)sender NS_AVAILABLE_IOS(5_0);
/** 从右到左写入字符串(居右) */
- (void)makeTextWritingDirectionRightToLeft:(nullable id)sender NS_AVAILABLE_IOS(5_0);
/** 切换字体为黑体(粗体) */
- (void)toggleBoldface:(nullable id)sender NS_AVAILABLE_IOS(6_0);
/** 切换字体为斜体 */
- (void)toggleItalics:(nullable id)sender NS_AVAILABLE_IOS(6_0);
/** 给文字添加下划线 */
- (void)toggleUnderline:(nullable id)sender NS_AVAILABLE_IOS(6_0); /** 增加字体大小 */
- (void)increaseSize:(nullable id)sender NS_AVAILABLE_IOS(7_0);
/** 减小字体大小 */
- (void)decreaseSize:(nullable id)sender NS_AVAILABLE_IOS(7_0); @end #pragma mark - UIResponder类定义 NS_CLASS_AVAILABLE_IOS(2_0) @interface UIResponder : NSObject <UIResponderStandardEditActions> #pragma mark - 响应者相关方法 /** 获取下一个响应者 */
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly, nullable) UIResponder *nextResponder;
#else
- (nullable UIResponder *)nextResponder;
#endif /** 是否允许成为第一响应者。默认返回NO */
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly) BOOL canBecomeFirstResponder;
#else
- (BOOL)canBecomeFirstResponder;
#endif
/** 设置成为第一响应者 */
- (BOOL)becomeFirstResponder; /** 是否允许放弃第一响应者。默认返回YES */
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly) BOOL canResignFirstResponder;
#else
- (BOOL)canResignFirstResponder;
#endif
/** 设置放弃第一响应者 */
- (BOOL)resignFirstResponder; /** 判断对象是否是第一响应者 */
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly) BOOL isFirstResponder;
#else
- (BOOL)isFirstResponder;
#endif #pragma mark - 触摸相关方法,一般用于响应屏幕触摸
/** 手指按下时响应 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
/** 手指移动时响应 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
/** 手指抬起时响应 */
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
/** 取消(意外中断, 如:电话, 系统警告窗等) */
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
/** 3DTouch响应(iOS9.1后使用) */
- (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches NS_AVAILABLE_IOS(9_1); #pragma mark - 深按相关方法,一般用于遥控器按键响应
/** 手指按压开始时响应 */
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
/** 手指按压位置移动时响应 */
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
/** 手指抬起接受按压时响应 */
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
/** 按压取消(意外中断, 如:电话, 系统警告窗等) */
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0); #pragma mark - 加速相关方法,一般用于摇一摇、运动事件监听等
/** 开始加速 */
- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
/** 结束加速 */
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
/** 加速取消(意外中断, 如:电话, 系统警告窗等) */
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0); /** 远程控制事件 */
- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0); /** 返回UIMenuController需要显示的控件(如:复制,粘贴等) */
- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(3_0); /** 返回响应的操作目标对象 */
- (nullable id)targetForAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(7_0); /** 获取响应链就近共享撤消管理 */
@property(nullable, nonatomic,readonly) NSUndoManager *undoManager NS_AVAILABLE_IOS(3_0); @end /** 快捷主键枚举 */
typedef NS_OPTIONS(NSInteger, UIKeyModifierFlags) {
UIKeyModifierAlphaShift = << , //!< Alpha+Shift键.
UIKeyModifierShift = << , //!< Shift键.
UIKeyModifierControl = << , //!< Control键.
UIKeyModifierAlternate = << , //!< Alt键.
UIKeyModifierCommand = << , //!< Command键.
UIKeyModifierNumericPad = << , //!< Num键.
} NS_ENUM_AVAILABLE_IOS(7_0); #pragma mark - 快捷键对象 NS_CLASS_AVAILABLE_IOS(7_0) @interface UIKeyCommand : NSObject <NSCopying, NSSecureCoding> /** 初始化对象 */
- (instancetype)init NS_DESIGNATED_INITIALIZER;
/** 初始化对象 */
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; /** 获取快捷辅键(如快捷命令【Command+A】中的 A 键) */
@property (nonatomic,readonly) NSString *input;
/** 获取快捷主键(如快捷命令【Command+A】中的 Command 键) */
@property (nonatomic,readonly) UIKeyModifierFlags modifierFlags;
/** 显示给用户的快捷键标题 */
@property (nullable,nonatomic,copy) NSString *discoverabilityTitle NS_AVAILABLE_IOS(9_0); /** 创建一个快捷键命令 */
+ (UIKeyCommand *)keyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action; /** 创建一个快捷键命令 */
+ (UIKeyCommand *)keyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action discoverabilityTitle:(NSString *)discoverabilityTitle NS_AVAILABLE_IOS(9_0); @end #pragma mark - 响应快捷命令 @interface UIResponder (UIResponderKeyCommands)
/** 返回快捷键命令数组 */
@property (nullable,nonatomic,readonly) NSArray<UIKeyCommand *> *keyCommands NS_AVAILABLE_IOS(7_0);
@end @class UIInputViewController;
@class UITextInputMode;
@class UITextInputAssistantItem; #pragma mark - 输入视图 @interface UIResponder (UIResponderInputViewAdditions) /** 键盘输入视图(系统默认的,可以自定义) */
@property (nullable, nonatomic, readonly, strong) __kindof UIView *inputView NS_AVAILABLE_IOS(3_2);
/** 弹出键盘时附带的视图 */
@property (nullable, nonatomic, readonly, strong) __kindof UIView *inputAccessoryView NS_AVAILABLE_IOS(3_2); /** 输入助手配置键盘的快捷方式栏时使用 */
@property (nonnull, nonatomic, readonly, strong) UITextInputAssistantItem *inputAssistantItem NS_AVAILABLE_IOS(9_0) __TVOS_PROHIBITED __WATCHOS_PROHIBITED; /** 键盘输入视图控制器 */
@property (nullable, nonatomic, readonly, strong) UIInputViewController *inputViewController NS_AVAILABLE_IOS(8_0);
/** 弹出键盘时附带的视图的视图控制器 */
@property (nullable, nonatomic, readonly, strong) UIInputViewController *inputAccessoryViewController NS_AVAILABLE_IOS(8_0); /** 文本输入模式 */
@property (nullable, nonatomic, readonly, strong) UITextInputMode *textInputMode NS_AVAILABLE_IOS(7_0); /** 文本输入模式标识 */
@property (nullable, nonatomic, readonly, strong) NSString *textInputContextIdentifier NS_AVAILABLE_IOS(7_0);
/** 根据设置的标识清除指定的文本输入模式 */
+ (void)clearTextInputContextIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(7_0); /** 重新刷新键盘输入视图 */
- (void)reloadInputViews NS_AVAILABLE_IOS(3_2); @end /** 特殊快捷辅键定义 */
UIKIT_EXTERN NSString *const UIKeyInputUpArrow NS_AVAILABLE_IOS(7_0); //!< 上按键.
UIKIT_EXTERN NSString *const UIKeyInputDownArrow NS_AVAILABLE_IOS(7_0); //!< 下按键.
UIKIT_EXTERN NSString *const UIKeyInputLeftArrow NS_AVAILABLE_IOS(7_0); //!< 左按键.
UIKIT_EXTERN NSString *const UIKeyInputRightArrow NS_AVAILABLE_IOS(7_0); //!< 右按键
UIKIT_EXTERN NSString *const UIKeyInputEscape NS_AVAILABLE_IOS(7_0); //!< Esc按键. #pragma mark - 响应者活动 @interface UIResponder (ActivityContinuation)
/** 用户活动 */
@property (nullable, nonatomic, strong) NSUserActivity *userActivity NS_AVAILABLE_IOS(8_0);
/** 更新用户活动 */
- (void)updateUserActivityState:(NSUserActivity *)activity NS_AVAILABLE_IOS(8_0);
/** 恢复用户活动 */
- (void)restoreUserActivityState:(NSUserActivity *)activity NS_AVAILABLE_IOS(8_0);
@end NS_ASSUME_NONNULL_END

二 UIResponder的使用

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

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

iOS学习——(转)UIResponder详解

/**
* 查找视图的视图控制器
*
* @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成为第一响应者后会弹出输入键盘,取消第一响应者则会隐藏输入键盘。

 //  ZMFirstResponderView.m

 #import "ZMFirstResponderView.h"

 @implementation ZMFirstResponderView

 /** 演示设置为第一响应者 */
- (void)setBecomeFirstResponder {
// 判断对象是否已经是第一响应者
if ([self isFirstResponder]) {
return;
}
// 判断对象是否允许成为第一响应者
if ([self canBecomeFirstResponder]) {
// 设置成为第一响应者
[self becomeFirstResponder];
}
} /** 演示放弃第一响应者 */
- (void)setResignFirstResponder {
// 判断对象是否不是第一响应者
if (![self isFirstResponder]) {
return;
}
// 判断对象是否允许放弃第一响应者
if ([self canResignFirstResponder]) {
// 设置放弃第一响应者
[self resignFirstResponder];
}
} /** 重写方法,允许对象成为第一响应者 */
- (BOOL)canBecomeFirstResponder {
return YES;
} @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. 如果需要多个动作一起撤销则需要标记分组
 /** ==============ZMDrawingBoardView.h文件=================== */

 #import <UIKit/UIKit.h>

 /** 画板View */
@interface ZMDrawingBoardView : UIView @end /** 划线Model */
@interface ZMLineModel : NSObject @property (nonatomic) CGPoint begin;
@property (nonatomic) CGPoint end; @end /** ==============ZMDrawingBoardView.m文件=================== */ #import "ZMDrawingBoardView.h" /** 画板View */
@interface ZMDrawingBoardView () @property (nonatomic, strong) ZMLineModel *currentLine;
@property (nonatomic, strong) NSMutableArray<ZMLineModel *> *toucheArray; @end @implementation ZMDrawingBoardView - (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initSubView];
self.backgroundColor = [UIColor whiteColor];
self.toucheArray = [NSMutableArray array];
}
return self;
} /** 绘制画板 */
- (void)drawRect:(CGRect)rect {
// 获得上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置样式
CGContextSetLineCap(context, kCGLineCapSquare);
// 设置宽度
CGContextSetLineWidth(context, 5.0);
// 设置颜色
CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]); for (ZMLineModel *line in self.toucheArray) {
// 开始绘制
CGContextBeginPath(context);
// 移动画笔到起点
CGContextMoveToPoint(context, line.begin.x, line.begin.y);
// 添加下一点
CGContextAddLineToPoint(context, line.end.x, line.end.y);
// 绘制完成
CGContextStrokePath(context);
}
} /** 划线开始 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 标记开始撤销分组
[self.undoManager beginUndoGrouping]; for (UITouch *touch in touches) {
// 记录起始点
CGPoint locTouch = [touch locationInView:self];
_currentLine = [[ZMLineModel alloc] init];
_currentLine.begin = locTouch;
_currentLine.end = locTouch;
} } /** 划线移动 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
// 添加线条
CGPoint locTouch = [touch locationInView:self];
_currentLine.end = locTouch;
[self addLine:_currentLine];
// 当前线条
_currentLine = [[ZMLineModel alloc] init];
_currentLine.begin = locTouch;
_currentLine.end = locTouch;
}
} /** 划线结束 */
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// 结束标记撤销分组
[self.undoManager endUndoGrouping];
} /** 添加划线 */
- (void)addLine:(ZMLineModel *)line
{
// 添加划线并重绘画板
[self.toucheArray addObject:line];
[self setNeedsDisplay];
// 注册撤销方法
[[self.undoManager prepareWithInvocationTarget:self] removeLine:line];
} /** 移除划线 */
- (void)removeLine:(ZMLineModel *)line
{
if ([self.toucheArray containsObject:line]) {
// 移除划线并重绘画板
[self.toucheArray removeObject:line];
[self setNeedsDisplay];
// 注册撤销方法
[[self.undoManager prepareWithInvocationTarget:self] addLine:line];
}
} /** 撤销按钮点击响应 */
- (void)undoButtonAction:(id)sender {
if ([self.undoManager canUndo]) {
[self.undoManager undo];
}
} /** 重做按钮点击响应 */
- (void)redoButtonAction:(id)sender {
if ([self.undoManager canRedo]) {
[self.undoManager redo];
}
} /** 初始化子控件 */
- (void)initSubView {
// 撤销按钮
UIButton *undoButton = [UIButton buttonWithType:UIButtonTypeSystem];
undoButton.frame = CGRectMake(, , , );
[undoButton setTitle:@"undo撤销" forState:UIControlStateNormal];
[undoButton sizeToFit];
[undoButton addTarget:self action:@selector(undoButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:undoButton];
// 重做按钮
UIButton *redoButton = [UIButton buttonWithType:UIButtonTypeSystem];
redoButton.frame = CGRectMake(CGRectGetWidth(self.frame)-, , , );
[redoButton setTitle:@"redo重做" forState:UIControlStateNormal];
[redoButton sizeToFit];
[redoButton addTarget:self action:@selector(redoButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:redoButton];
} @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输入键盘

 //  ZMCustomInputView.m

 #import "ZMCustomInputView.h"

 #define MAIN_SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width   //!< 屏幕的Width

 @interface ZMCustomInputView ()

 @property (nonatomic, strong) UITextField *textField;
@property (nonatomic, strong) UIView *customInputView;
@property (nonatomic, strong) UIToolbar *customAccessoryView; @end @implementation ZMCustomInputView - (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 添加TextField
[self addSubview:self.textField];
}
return self;
} /** 懒加载textField */
- (UITextField *)textField {
if (!_textField) {
// 初始化textField
_textField = [[UITextField alloc]initWithFrame:CGRectMake(, , MAIN_SCREEN_WIDTH - , )];
_textField.borderStyle = UITextBorderStyleRoundedRect;
_textField.placeholder = @"测试";
// 设置自定义键盘View
_textField.inputView = self.customInputView;
_textField.inputAccessoryView = self.customAccessoryView;
}
return _textField;
} /** 懒加载customInputView */
- (UIView *)customInputView {
if (!_customInputView) {
_customInputView = [[UIView alloc]initWithFrame:CGRectMake(, , MAIN_SCREEN_WIDTH, )];
_customInputView.backgroundColor = [UIColor lightGrayColor];
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(, , MAIN_SCREEN_WIDTH, )];
label.textAlignment = NSTextAlignmentCenter;
label.text = @"自定义inputView";
[_customInputView addSubview:label];
}
return _customInputView;
} /** 懒加载customAccessoryView */
- (UIToolbar *)customAccessoryView {
if (!_customAccessoryView) {
_customAccessoryView = [[UIToolbar alloc]initWithFrame:CGRectMake(, , MAIN_SCREEN_WIDTH, )];
_customAccessoryView.barTintColor = [UIColor orangeColor];
UIBarButtonItem *space = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *done = [[UIBarButtonItem alloc]initWithTitle:@"完成" style:UIBarButtonItemStyleDone target:self action:@selector(done)];
[_customAccessoryView setItems:@[space, space, done]];
}
return _customAccessoryView;
} /** 响应完成按钮 */
- (void)done {
[self.textField resignFirstResponder];
} @end
上一篇:3.2 js六大数据类型


下一篇:AVFoundation的使用