How to “transfer” first responder from one UIView

2019-02-15 10:03发布

问题:

I have a UIView subclass (CustomView for purposes of this question) that has its own handling of all touches events (Began, Moved, Ended, Cancelled). I also have a UIButton that is a sibling of CustomView that overlaps it.

For example, my view hierarchy looks like this:

  • UIView (Controller's view)
    • CustomView with frame (0, 0, 300, 300)
    • UIButton with frame (0, 0, 100, 50)

I would like the CustomView to capture touch events once the user has dragged out of the UIButton. Through debug logging, it appears that the UIControlEventTouchDragExit event is the one to intercept (though it doesn't fire until the touch is around 100 pixels away from the button, but that is not important to this question).

Ideally, when the drag exit event is fired, the button would stop receiving touch move events, the CustomView would get a touchesBegan event (even if I need to fake one myself somehow), and all future touch events (touchesMoved, etc) would be sent to the CustomView.

I have tried the following, and it doesn't have any effect:

-(void)btnTouchDragExit:(id)sender
{
    UIButton * btn = sender;
    [btn resignFirstResponder];
    [customView becomeFirstResponder];
}

I think the underlying UITouch object (which is consistent across touch events) is not being retargeted to point at the CustomView. How can I achieve the desired effect?

回答1:

Just use hitTest:withEvent: to figure out what view "should" be receiving the event, then forward on the event to the appropriate view. You might have to do a little bit of dancing around to intercept events before they go to the button, but this is what hitTest:withEvent: was made for.



回答2:

you have to override canBecomeFirstResponder to return YES otherwise becomeFirstResponder does nothing. This is already done on UIControls, but not on UIViews