UITouch 与 手势识别器

交互相关的属性

  1. userInteractionEnabled 默认是YES ,如果设置为NO则不响应用户事件,并且把当前控件从事件队列中删除。也就是说设置了userInterfaceEnabled属性的视图会打断响应者链导致该view的subview都无法响应事件。
  2. multipleTouchEnabled 默认是NO,如果设置为YES则支持多点触碰。
  3. exclusiveTouch 默认是NO,如果设置为YES则当前UIView会独占整个Touch事件。具体来说就是如果UIView设置了exclusiveTouch属性为YES则当这个UIView成为第一响应者时,在手指离开屏幕前其他view不会响应任何touch事件。

UITouch的属性

window:触碰产生时所处的窗口,由于窗口可能发生变化,当前所在的窗口不一定是最开始的窗口。

view:触碰产生时所处的视图。由于视图可能发生变化,当前视图也不一定是最初的视图。

tapCount:短时间内轻击(tap)屏幕的次数,可根据tapCount判断单击、双击或更多的轻击。

timestamp:时间戳记录了触碰事件产生或变化时的时间。单位是秒。

phase:触碰事件在屏幕上有一个周期,即触碰开始、触碰点移动、触碰结束,中途取消。通过phase可以查看当前触碰事件在一个周期中所处的状态

交互相关的方法

根据不同的触碰状态,程序会调用相应的处理函数,这些函数包括:

//开始
-(void) touchesBegan:(NSSet *)touches withEvents:(UIEvent *)event;
//移动
-(void) touchesMoved:(NSSet *)touches withEvents:(UIEvent *)event;
//结束
-(void) touchesEnded:(NSSet *)touches withEvents:(UIEvent *)event;
//接到电话时触发
-(void) touchesCancelled:(NSSet *)touches withEvents:(UIEvent *)event;

NSSet类:类似于NSArray,用于存储对象,NSSet、NSMutableSet类声明编程接口对象,无序的集合,在内存中存储方式是不连续的

远程事件响应必须执行的两个方法

//重写下面的方法(默认返回值为NO),返回 YES
- (BOOL)canBecomeFirstResponder
//让它成为第一响应者
- (BOOL)becomeFirstResponder

UIButton 继承自 UIControl, UIControl 继承自 UIView
ImageView 和 lablel 的默认状态是不允许用户进行交互


用xcode打开API的快捷键:command + shift + 0

手势识别器 UIGestureRecognizer

  1. 点击手势:UITapGestureRecognizer
  2. 滑动手势:UISwipeGestureRecognizer
  3. 旋转手势:UIRotationGestureRecognizer
  4. 捏合手势:UIPinchGestureRecognizer
  5. 长按手势:UILongPressGestureRecognizer
  6. 平移手势:UIPanGestureRecognizer
  7. 屏幕边缘平移手势:UIScreenEdgePanGestureRecognizer

UIGestureRecognizer
(是个抽象的基类,不能用它直接实例化对象,主要使用它的子类)

//初始化方法
- (instancetype _Nonnull)initWithTarget:(id _Nullable)target action:(SEL _Nullable)action
//添加监听事件
- (void)addTarget:(id _Nonnull)target action:(SEL _Nonnull)action
//移除监听事件
- (void)removeTarget:(id _Nullable)target action:(SEL _Nullable)action
//在视图中的位置
- (CGPoint)locationInView:(UIView * _Nullable)view
//对应手指在视图中的位置
- (CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(UIView * _Nullable)view
//手指个数
- (NSUInteger)numberOfTouches
//状态
@property(nonatomic, readonly) UIGestureRecognizerState state
//状态的枚举
typedef enum {
   UIGestureRecognizerStatePossible,
   UIGestureRecognizerStateBegan,
   UIGestureRecognizerStateChanged,
   UIGestureRecognizerStateEnded,
   UIGestureRecognizerStateCancelled,
   UIGestureRecognizerStateFailed,
   UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
} UIGestureRecognizerState;
//视图
@property(nonatomic, readonly, nullable) UIView *view
//是否允许添加手势(默认是YES)
@property(nonatomic, getter=isEnabled) BOOL enabled

下面三个方法表示我们使用手势识别器封装了,还要不要使用触摸或者延时触摸
//取消手势
@property(nonatomic) BOOL cancelsTouchesInView
//延迟开始
@property(nonatomic) BOOL delaysTouchesBegan
//延迟结束
@property(nonatomic) BOOL delaysTouchesEnded

//如果otherGestureRecognizer手势失败则调用另一个手势(调用这个方法的手势)
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer * _Nonnull)otherGestureRecognizer
//代理(代理方法见下)
@property(nonatomic, weak, nullable) id< UIGestureRecognizerDelegate > delegate

UITapGestureRecognizer 点击

//点击次数
@property(nonatomic) NSUInteger numberOfTapsRequired
//触发事件的手指数量
@property(nonatomic) NSUInteger numberOfTouchesRequired

UISwipeGestureRecognizer 滑动(轻扫,快速移动,用来监测滑动方向的)

@property(nonatomic) NSUInteger numberOfTouchesRequired
//允许滑动的方向
@property(nonatomic) UISwipeGestureRecognizerDirection direction
//方向枚举
typedef enum {
   UISwipeGestureRecognizerDirectionRight = 1 << 0,
   UISwipeGestureRecognizerDirectionLeft  = 1 << 1,
   UISwipeGestureRecognizerDirectionUp    = 1 << 2,
   UISwipeGestureRecognizerDirectionDown  = 1 << 3
} UISwipeGestureRecognizerDirection;

UIRotationGestureRecognizer 旋转

//旋转弧度,默认是绝对值(叠加值),需要的是增量值时,需要每次rotation置0
@property(nonatomic) CGFloat rotation
//速度(每秒多少弧度,radius为1)
@property(nonatomic, readonly) CGFloat velocity

UIPinchGestureRecognizer 捏合

//scale(比例)如果需要获得增量值
@property(nonatomic) CGFloat scale
//每秒多少比例
@property(nonatomic, readonly) CGFloat velocity

UILongPressGestureRecognizer 长按

//设置当前长按最小的时长
@property(nonatomic) CFTimeInterval minimumPressDuration
@property(nonatomic) NSUInteger numberOfTouchesRequired
@property(nonatomic) NSUInteger numberOfTapsRequired
//设置允许的移动范围,在范围内手指不离开屏幕算长按,超过就算平移
@property(nonatomic) CGFloat allowableMovement

UIPanGestureRecognizer 平移(慢速移动,是用于监测偏移的量的)

@property(nonatomic) NSUInteger maximumNumberOfTouches//上不封顶
@property(nonatomic) NSUInteger minimumNumberOfTouches//默认是1
//返回的是个绝对值
- (CGPoint)translationInView:(UIView *)view
//用来重置 Translation 的值
- (void)setTranslation:(CGPoint)translation inView:(UIView *)view
- (CGPoint)velocityInView:(UIView *)view//速度

UIScreenEdgePanGestureRecognizer 屏幕边缘平移(继承自平移,它拥有平移的全部属性和方法)

//方向
@property(readwrite, nonatomic, assign) UIRectEdge edges  
//枚举
typedef enum : NSUInteger  {
   UIRectEdgeNone    = 0,//(0000 0000)这种方式是为了更好的或运算
   UIRectEdgeTop     = 1 << 0,//(0000 0001)
   UIRectEdgeLeft    = 1 << 1,//(0000 0010)
   UIRectEdgeBottom  = 1 << 2,//(0000 0100)
   UIRectEdgeRight   = 1 << 3,//(0000 1000)
   UIRectEdgeAll  = UIRectEdgeTop  | UIRectEdgeLeft  | UIRectEdgeBottom  | UIRectEdgeRight 
} UIRectEdge;

代理方法

//是否允许手势开始(默认YES- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer * _Nonnull)gestureRecognizer
//是否允许手势识别器检测触摸手势(默认YES)
- (BOOL)gestureRecognizer:(UIGestureRecognizer * _Nonnull)gestureRecognizer shouldReceiveTouch:(UITouch * _Nonnull)touch
//是否允许两个手势同时有效
- (BOOL)gestureRecognizer:(UIGestureRecognizer * _Nonnull)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer * _Nonnull)otherGestureRecognizer
//下面这两个方法是用来设置谁有效,谁没效
- (BOOL)gestureRecognizer:(UIGestureRecognizer * _Nonnull)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer * _Nonnull)otherGestureRecognizer
- (BOOL)gestureRecognizer:(UIGestureRecognizer * _Nonnull)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer * _Nonnull)otherGestureRecognizer

补充

与:&
或:|
非:~
异或:不同位1,相同为0
同或:相同为1,不同位0
左移位:<<
右移位:>> (正数右移,前边空出来的位全部补0,负数全部补1)

代码举例(以下两个手势有需要注意的细节)

手势识别是具有互斥的原则的,比如单击和双击,如果它识别出一种手势,其后的手势将不被识别,所以对于关联手势,要做特殊处理以帮助程序甄别,应该把当前手势归结到哪一类手势里面。比如,单击和双击并存时,如果不做处理,它就只能发送出单击的消息。为了能够识别出双击手势,就需要做一个特殊处理逻辑,即先判断手势是否是双击,在双击失效的情况下作为单击手势处理。使用如下方法:
[singleRecognizer requireGestureRecognizerToFail:doubleRecognizer];

单击和双击

//单击
 UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(singleAction:)];
 singleTap.numberOfTapsRequired = 1;
 [self.view addGestureRecognizer:singleTap];
//双击
 UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(doubleAction:)];
 doubleTap.numberOfTapsRequired = 2;
 [self.view addGestureRecognizer:doubleTap];
//建立单击手势识别器和双击手势识别器的依赖关系
 [singleTap requireGestureRecognizerToFail:doubleTap];

长按手势

    UILongPressGestureRecognizer *longPressGestion = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressAction:)];
    longPressGestion.minimumPressDuration = 2;
    [self.view addGestureRecognizer:longPressGestion];

-(void)longPressAction:(UILongPressGestureRecognizer *)gesture{
    //需要判断一下是否是开始点击状态,否则会调用两次这个方法
    if (gesture.state != UIGestureRecognizerStateBegan) {
        return;
    }
    NSLog(@"长按了");
}

关于滑动手势的状态,只有 ended状态 才能捕获到

菜单

UIMenuItem(菜单)

//初始化菜单
- (instancetype _Nonnull)initWithTitle:(NSString * _Nonnull)title action:(SEL _Nonnull)action
@property(nonatomic, copy, nonnull) NSString *title
@property(nonatomic, nonnull) SEL action

UIMenuController(菜单控制器,全局单例对象)

//创建对象
+ (UIMenuController * _Nonnull)sharedMenuController
//存放菜单的数组
@property(nonatomic, copy, nullable) NSArray  *menuItems
//设置在哪个位置显示菜单
- (void)setTargetRect:(CGRect)targetRect inView:(UIView * _Nonnull)targetView
//显示菜单
- (void)setMenuVisible:(BOOL)menuVisible animated:(BOOL)animated

菜单代码举例

- (IBAction)showResetenu:(UILongPressGestureRecognizer *)sender {
    if (sender.state != UIGestureRecognizerStateBegan) {
        return;
    }
    //创建UIMenuItem对象
    UIMenuItem *resetitem = [[UIMenuItem alloc]initWithTitle:@"Reset" action:@selector(resetView)];
    //创建UIMenuController对象
    UIMenuController * menuController = [UIMenuController sharedMenuController];
    //都有哪些Item,放到数组中
    menuController.menuItems = @[resetitem];
    //item放到哪个位置
    CGPoint location = [sender locationInView:self.view];
    [menuController setTargetRect:CGRectMake(location.x, location.y, 0, 0) inView:self.view];
    //设置成为第一响应者(必须有第一响应,才能显示出菜单)
    [self becomeFirstResponder];
    //显示item
    [menuController setMenuVisible:YES   animated:YES];
    //标记将要复位的视图
    _view2Reset = sender.view;
}
//允许成为第一响应
-(BOOL)canBecomeFirstResponder{
    return YES;
}
//复位方法
-(void)resetView{
    [UIView animateWithDuration:.3 animations:^{
    _view2Reset.transform = CGAffineTransformIdentity;
    }];
}

你可能感兴趣的:(iOS-OC)