自定义 UIControl 之控件状态

1.系统是如何判断控件状态的

在UIControl中有这个方法
- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents;
用它来发送控件状态。
什么时候发送对应状态,我们可以自己来进行来判断。

这要用到以下几个UIResponder中的方法

- (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event; 
- (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event; 
- (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event; 
- (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event; 
  • 在touchesBegan时,要发送UIControlEventTouchDown事件。
  • 在touchesMoved中,根据前一刻point和当前时刻的point,来判断发送UIControlEventTouchDragInside,UIControlEventTouchDragOutside,UIControlEventTouchDragEnter或UIControlEventTouchDragExit事件。
  • 在touchesEnded的时,应该发送UIControlEventTouchUpInside或UIControlEventTouchUpOutside事件。
  • 在touchesCancelled时,要发送UIControlEventTouchCancel。

控件highlighted只要直接赋值YES 或 NO 就可以了。

  • 在touchesBegan时 highlighted为YES
  • 在touchesEnded和touchesCancelled时 highlighted应该为NO。
  • 在touchesMoved中,要判断触碰的位置来设置highlighted的值。

2.如何判断触碰的位置

在UITouch中有以下两个方法。

- (CGPoint)locationInView:(nullable UIView*)view;//获得当前触摸点的point 
- (CGPoint)previousLocationInView:(nullable UIView *)view;//获得先前触摸点的point 

结合CoreGraphics中的 bool CGRectContainsPoint(CGRect rect, CGPoint point)方法来判断触碰点与控件的关系

以touchesEnded和touchesMoved为例。

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    self.highlighted = NO; 
    CGPoint point = [touches.anyObject locationInView:self]; 

   if (CGRectContainsPoint(self.bounds, point)) { //判断point是否在self.bounds内。 
        [self sendActionsForControlEvents:UIControlEventTouchUpInside]; 
    } else { 
        [self sendActionsForControlEvents:UIControlEventTouchUpOutside]; 
    } 
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = touches.anyObject;
    BOOL currentInside = CGRectContainsPoint(self.bounds, [touch locationInView:self]);
    BOOL preInside = CGRectContainsPoint(self.bounds, [touch previousLocationInView:self]);
    
    if (currentInside) {
        self.highlighted = YES;
        if (preInside) {
            [self sendActionsForControlEvents:UIControlEventTouchDragInside];
        } else {
            [self sendActionsForControlEvents:UIControlEventTouchDragEnter];
        }
    } else {
        self.highlighted = NO;
        if (preInside) {
            [self sendActionsForControlEvents:UIControlEventTouchDragExit];
        } else {
            [self sendActionsForControlEvents:UIControlEventTouchDragOutside];
        }
    }
}

以上这些,其实可以让系统来做,只要调用即可。
[super touchesXXX:touches withEvent:event]

例如:
以下代码就等于上面的一大段的touchesMoved方法了。

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
}

3.系统的方法如此简便,那为什么我们还要重写touches系列的方法

因为系统的touchesMoved在手指移动时,大概是离开控件70pt左右内,控件仍然会处于高亮的状态,这时touchesEnded则会触发UIControlEventTouchUpInside事件。
现实中,我们会遇到许多奇奇怪怪的需求,系统原本的方法不足以满足需求,这就需要我们自己来重写方法了。
去研究,探索系统的方法是如何实现的,最终学会创新,这是件非常有趣的事。

你可能感兴趣的:(自定义 UIControl 之控件状态)