In the normal usage scenario, we have handled more of the intra-rectangular touch events, such as UIButton, Uicontrol. In general, the shapes of these controls, as well as the touch areas, are rectangular or rounded rectangles. But in some special scenarios we sometimes have to deal with a more demanding requirement, such as requiring the program to process only the click events of an unconventional area, such as a circle or a five-point shape, which takes a bit of effort. In this paper, we use the circle as an example to describe the processing method of this kind of scene.
Let's take a look at the following diagram (Figure 1), and our goal is to implement the following custom Tabbar. A custom Tabbar with a convex circle in the middle has once been popular, and today we come to the rough to achieve it.
In the accompanying drawings, red stands for Tabbar with three blue buttons. In the three buttons we focus on button a because half of the area is out of the active area of the Tabbar.
For Button A, we have the following two questions to solve:
1, how to accurately filter out a non-blue region of the external rectangle click event?
2, how to make the upper part of a can also respond to touch events?
In fact, the solution of the two problems is basically consistent. All controls in iOS exist as rectangles, and in Figure 2, although the blue part looks like a circle, the Click event is triggered by default when you click the non-circular area within the bounding rectangle. Therefore, we need to use some means to "intercept" the Touch event. To "intercept" the event, you must understand the iOS event distribution mechanism, that is, when you click on the device screen, how iOS decides to finally respond to your touch by that view! Here's a short section on iOS event distribution:
==================================
When your finger touches the screen, the following things happen: The touch event is encapsulated as a uievent event, taking the active app in the active app queue of the current iOS operating system, passing the event to it--->event to uiapplication--- > to UIWindow Root View controller (ROOTVC)---> Calls all subviews hittest:event: Method of Rootvc.view. Which view's Hittest:event method returns a non-nil value, the touch event is given to the view handle. Detailed mechanism and examples of event distribution can be found in the article on the Technical Gothic god
==================================
Let's go back to the question we're discussing. Through the above introduction we can know that want to "intercept" touch events, you should be in the Tabbar hittest:event method of processing (coordinate judgment, etc.). The following is the specific demo source:
1 #import <UIKit/UIKit.h>23@interface panelview:uiview4 5@end
1 #import "panelView.h"2 3 @implementationPanelview4 5- (ID) initWithFrame: (CGRect) Frame6 {7Self =[Super Initwithframe:frame];8 if(self) {9 [self initsubviews];Ten } One returnSelf ; A } - -- (void) Initsubviews the { -UIButton *roundbtn =[UIButton Buttonwithtype:uibuttontypecustom]; -Roundbtn.frame = CGRectMake (Self.frame.size.width/2- -, - -, -, -); -Roundbtn.backgroundcolor =[Uicolor Bluecolor]; +RoundBtn.layer.cornerRadius = -; -Roundbtn.tag =10086; + [roundbtn addtarget:self Action: @selector (onbtnpressed:) A Forcontrolevents:uicontroleventtouchupinside]; at [self addsubview:roundbtn]; - -UIButton *leftbtn =[UIButton Buttonwithtype:uibuttontypecustom]; -Leftbtn.frame = CGRectMake (0, the, -, -); -Leftbtn.backgroundcolor =[Uicolor Bluecolor]; -Leftbtn.tag =10087; in [leftbtn addtarget:self Action: @selector (onbtnpressed:) - Forcontrolevents:uicontroleventtouchupinside]; to [self addsubview:leftbtn]; + -UIButton *rightbtn =[UIButton Buttonwithtype:uibuttontypecustom]; theRightbtn.frame = CGRectMake (Self.frame.size.width- -, the, -, -); *Rightbtn.backgroundcolor =[Uicolor Bluecolor]; $Rightbtn.tag =10088;Panax Notoginseng [rightbtn addtarget:self Action: @selector (onbtnpressed:) - Forcontrolevents:uicontroleventtouchupinside]; the [self addsubview:rightbtn]; + } A the-(UIView *) HitTest: (cgpoint) point withevent: (Uievent *)Event + { -UIView *hitview =Nil; $ //NSLog (@ "point:%@", Nsstringfromcgpoint (point)); $UIButton *roundbtn = (UIButton *) [Self Viewwithtag:10086]; -UIButton *leftbtn = (UIButton *) [Self Viewwithtag:10087]; -UIButton *rightbtn = (UIButton *) [Self Viewwithtag:10088]; theBOOL pointinround = [Self touchPointInsideCircle:roundBtn.center radius: -Targetpoint:point]; - if(pointinround) {WuyiHitview =roundbtn; the}Else if(Cgrectcontainspoint (Leftbtn.frame, point)) { -Hitview =leftbtn; Wu}Else if(Cgrectcontainspoint (Rightbtn.frame, point)) { -Hitview =rightbtn; About}Else { $Hitview =Self ; - } - returnHitview; - } A +-(BOOL) touchpointinsidecircle: (cgpoint) Center radius: (cgfloat) Radius targetpoint: (cgpoint) Point the { -CGFloat dist = sqrtf ((point.x-center.x) * (point.x-center.x) + $(POINT.Y-CENTER.Y) * (Point.y-center.y)); the return(Dist <=radius); the } the the -- (void) Onbtnpressed: (ID) Sender in { theUIButton *btn = (UIButton *) sender; theNSLog (@"btn tag:%d", Btn.tag); About } the the @end
PANELVIEW.M (control initialization, not expanded)
The most important thing in the HitTest method is to judge the area where Button A is located, in fact, only use the distance formula of two points to circle the blue part of the circle, the method is as follows:
1 - (BOOL) touchpointinsidecircle: (cgpoint) Center radius: (cgfloat) Radius targetpoint: (cgpoint) point 2{3 cgfloat dist = sqrtf ((point.x-center.x) * (point.x-center.x) +4 (POINT.Y-CENTER.Y) * (Point.y- center.y)); 5 return (Dist <= radius); 6 }
It is easier to determine whether the point is within the button b/c, and the system provides a packaged API:
1 BOOL Cgrectcontainspoint (CGRect rect, Cgpoint point)
In the end, the decision about the event "intercept" is as follows:
1-(UIView *) HitTest: (cgpoint) point withevent: (Uievent *)Event2 {3UIView *hitview =Nil;4 //NSLog (@ "point:%@", Nsstringfromcgpoint (point));5UIButton *roundbtn = (UIButton *) [Self Viewwithtag:10086];6UIButton *leftbtn = (UIButton *) [Self Viewwithtag:10087];7UIButton *rightbtn = (UIButton *) [Self Viewwithtag:10088];8BOOL pointinround = [Self touchPointInsideCircle:roundBtn.center radius: -Targetpoint:point];9 if(pointinround) {TenHitview =roundbtn; One}Else if(Cgrectcontainspoint (Leftbtn.frame, point)) { AHitview =leftbtn; -}Else if(Cgrectcontainspoint (Rightbtn.frame, point)) { -Hitview =rightbtn; the}Else { -Hitview =Self ; - } - returnHitview; +}
In addition, in the HitTest can also play other tricks, such as the time should be a response to the button a mandatory forwarding to other buttons, which only need to be modified in the HitTest return value!
This article uses the demo full source point here test.zip download.
Fig. 1 Fig. 2
HitTest methods for "original" iOS and touch event handling in irregular areas