iOS Event distribution

Source: Internet
Author: User

The previous period of time the project has a demand, to click on the splash screen when doing some processing, just received this demand feel very simple ah, in the original view Add a button or gesture recognition what, the implementation of the back when the discovery or a bit of a pit. Whether I add a button on the splash screen or a gesture doesn't respond to the touch event, I've also thought of a number of possibilities, such as whether the message was delivered to another view, and I finally found out that I had to remove the button from the parent view. The specific reason is that the splash screen display is completed when I put the button also removed, while the display splash screen when the project also did a lot of initialization work, very occupied the main thread, causing UIApplication sendevent is blocked, when the main thread idle down, I remove the button from the parent view, and I never receive the touch event.

The whole distribution process of the event is not very well understood, to find the problem, so write this article on the distribution of the event to do a detailed explanation, but also on this basis to do some expansion.

1.TouchEvents Distribution Process

Many of the app's pages jump, the view switch is triggered by touching events, such as button clicks, view gestures and so on, then we click on the screen, the system is how to determine which view we clicked? The answer is HitTest, the following is to introduce the whole process.

A) When we click on the device's screen, Uikit generates an event object uievent, which is then distributed to the current app.

b) After the current app receives the event, UIApplication will go to the event queue to take the latest event, and then sendevent to the object capable of handling the event, but who can handle the event, this depends on the hittest to confirm.

c) HitTest will detect if the point of the click is on this view, and if it is, it will traverse the view's subviews until it finds the smallest view that can handle the event, and if it does not find the view that can be processed, it returns itself. When Hit-test view is determined, if the current application does not ignore touch events (uiapplication:isignoringinteractionevents), then application will distribute the event ( Sendevent:->keywindow:sendevent:).

As shown in the following example:

  

Steps:

1) When we click on View 6 in the figure because the touch point is within view 1, check view 1 for Subview View 2 and view 3

2) Touch point is not within View 2, touch point within view 3, so check view 3 Subview view 5 and view 6

3) The touch point is not within view 5, the touch point is within view 6, and view 6 is not subview, so view 6 is the smallest unit in view 1 that contains this point, so view 6 becomes the Hit-testview of this touch event

Description

1) The default hit-testing order is in reverse order of subviews in UIView

2) If the view has overlapping portions in the same level subview, the top subview is checked, if the top Subview returns nil, then the subview at the bottom is checked

3) Hit-test is also more intelligent, the detection process has such a point, that is, if the click does not occur in a view, then the event can not occur in the view of the subview, so the detection process found that the event is not in Viewb, Also directly will not detect within the VIEWF. That is, if your subview set Clipstobounds=no, the actual display area may be beyond the Superview frame, you click on the outside of the section, will not deal with your event, is so capricious!

4) View provides two ways to match hittest:

-(UIView *) HitTest: (cgpoint) point withevent: (Uievent *) event; Recursively calls-pointinside:withevent:. Point was in the receiver ' s coordinate system

-(BOOL) Pointinside: (cgpoint) point withevent: (Uievent *) event; Default returns YES if point was in bounds

5) When a view receives a hittest message, it calls its own pointinside:withevent: method, which if Pointinside returns Yes, indicates that the touch event occurred inside myself, It will traverse all of its subview to find the smallest unit (without any child view) of UIView, if current view.userinteractionenabled = No,enabled=no (Uicontrol), or alpha< =0.01, hidden and other situations, HitTest will not call their own pointinside, directly return nil, and then the system went back to traverse the sibling node, as shown in the following code:

-(UIView *) HitTest: (cgpoint) point withevent: (Uievent *)Event {    if(Self.alpha &lt;=0.01|| !self.userinteractionenabled | |Self.hidden) {returnNil; } BOOL inside= [Self Pointinside:point withevent:Event]; UIView*hitview =Nil; if(inside) {Nsenumerator*enumerator =[Self.subviews Reverseobjectenumerator];  for(UIView *subviewinchenumerator) {Hitview= [Subview hittest:point withevent:Event]; if(Hitview) { Break; }        }        if(!Hitview) {Hitview=Self ; }        returnHitview; } Else {        returnNil; }}

6) Hit-test is the first step in event distribution, and hit-test can occur even if your app ignores events. After the Hit-testview is identified, the next event distribution is started.

2. Application

We can use Hit-test to do something.

1) For example, we clicked on the Viewa, we want to let VIEWB response, this time, we just need to rewrite the view's HitTest method, return VIEWB can be, although may not be used, but occasionally will be used. The approximate code is as follows:

@interface Stpview:uiview @end @implementation Stpview-(Instancetype) initWithFrame: (CGRect) frame {self = [super]    Itwithframe:frame];        if (self) {UIButton *button = [UIButton buttonwithtype:uibuttontypecustom];        Button.frame = CGRectMake (0, 0, cgrectgetwidth (frame), cgrectgetheight (frame)/2);        Button.tag = 10001;        Button.backgroundcolor = [Uicolor Graycolor];        [Button settitle:@ "Button1" forstate:uicontrolstatenormal];        [Self Addsubview:button];                [Button addtarget:self action: @selector (_buttonactionfired:) Forcontrolevents:uicontroleventtouchdown];        UIButton *button2 = [UIButton buttonwithtype:uibuttontypecustom];        Button2.frame = CGRectMake (0, Cgrectgetheight (frame)/2, Cgrectgetwidth (frame), cgrectgetheight (frame)/2);        Button2.tag = 10002;        Button2.backgroundcolor = [Uicolor Darkgraycolor];        [Button2 settitle:@ "Button2" forstate:uicontrolstatenormal];    [Self addsubview:button2];    [Button2 addtarget:self Action: @selector (_buttonactionfired:) Forcontrolevents:uicontroleventtouchdown]; } return self;} -(void) _buttonactionfired: (UIButton *) button {NSLog (@ "=====button titled%@ actionfired", [button Titleforstate:uico Ntrolstatenormal]);} -(UIView *) HitTest: (cgpoint) point withevent: (Uievent *) Event {UIView *hitview = [Super Hittest:point withevent:event]    ;    if (Hitview = = [Self viewwithtag:10001]) {return [self viewwithtag:10002]; } return Hitview;} @end

2) here to provide you with a category, from Stkit, the purpose of this category is to easily write the HitTest method, because the HitTest method is override, not delegate, so using the default implementation is more troublesome. Category below

/** * @abstract Hittestblock * * @param the remaining parameters refer to UIView hittest:withevent: * @param returnsuper Returns the value of super. If *returnsuper=yes, the delegate returns Super Hittest:withevent: Otherwise, the return value of block (even nil) *  * @discussion Remember that Do not call the self hittest:withpoint in this block, otherwise it will cause a recursive call. This method is a substitute for hittest:withevent. */typedef UIView * (^sthittestviewblock) (Cgpoint Point, Uievent *event, BOOL *returnsuper); typedef BOOL (^ Stpointinsideblock) (Cgpoint Point, Uievent *event, BOOL *returnsuper); @interface UIView (sthittest)//Althought this was strong, but I deal it with Copy@property (nonatomic, Strong) Sthittestvi Ewblock Hittestblock; @property (nonatomic, strong) Stpointinsideblock Pointinsideblock; @end

@implementation UIView (sthittest) const static NSString *sthittestviewblockkey = @ "Sthittestviewblockkey"; const static NSString *stpointinsideblockkey = @ "Stpointinsideblockkey";                                   + (void) load {method_exchangeimplementations (Class_getinstancemethod (Self, @selector (hittest:withevent:)),    Class_getinstancemethod (Self, @selector (st_hittest:withevent:)));                                   Method_exchangeimplementations (Class_getinstancemethod (Self, @selector (pointinside:withevent:)), Class_getinstancemethod (Self, @selector (st_pointinside:withevent:)));} -(UIView *) St_hittest: (cgpoint) point withevent: (Uievent *) Event {nsmutablestring *spaces = [nsmutablestring stringwit    HCAPACITY:20];    UIView *superview = Self.superview;        while (Superview) {[Spaces appendstring:@ "----"];    Superview = Superview.superview;    } NSLog (@ "%@%@:[hittest:withevent:]", spaces, Nsstringfromclass (Self.class));    UIView *deliveredview = nil; If there is hiTtestblock is implemented, the call to block if (self.hittestblock) {BOOL returnsuper = NO;        Deliveredview = Self.hittestblock (point, Event, &returnsuper);        if (returnsuper) {Deliveredview = [self st_hittest:point withevent:event];    }} else {deliveredview = [self st_hittest:point withevent:event]; }//NSLog (@ "%@%@:[hittest:withevent:] result:%@", spaces, Nsstringfromclass (Self.class), Nsstringfromclass (    Deliveredview.class)); return Deliveredview;} -(BOOL) St_pointinside: (cgpoint) point withevent: (Uievent *) Event {nsmutablestring *spaces = [nsmutablestring stringwit    HCAPACITY:20];    UIView *superview = Self.superview;        while (Superview) {[Spaces appendstring:@ "----"];    Superview = Superview.superview;    } NSLog (@ "%@%@:[pointinside:withevent:]", spaces, Nsstringfromclass (Self.class));    BOOL pointinside = NO;        if (self.pointinsideblock) {BOOL returnsuper = NO; Pointinside = Self.pointinsideblock (Point,event, &returnsuper);        if (returnsuper) {pointinside = [self st_pointinside:point withevent:event];    }} else {pointinside = [self st_pointinside:point withevent:event]; } return pointinside;} -(void) Sethittestblock: (Sthittestviewblock) Hittestblock {objc_setassociatedobject (self, (__bridge const void *) ( Sthittestviewblockkey), Hittestblock, objc_association_copy);} -(Sthittestviewblock) Hittestblock {return objc_getassociatedobject (self, (__bridge const void *) (Sthittestviewblockk ey));} -(void) Setpointinsideblock: (Stpointinsideblock) Pointinsideblock {objc_setassociatedobject (self, (__bridge const void *) (Stpointinsideblockkey), Pointinsideblock, objc_association_copy);} -(Stpointinsideblock) Pointinsideblock {return objc_getassociatedobject (self, (__bridge const void *) (STPOINTINSIDEBL Ockkey));} @end

The code is simple, using iOS's runtime capabilities, to insert a method before HitTest executes.

3) iOS7 native Navigationcontroller can be implemented from the leftmost drag Popviewcontroller (about 13pt), regardless of the currently visible Viewcontroller there are no other sliding gestures or events, which is why? How it is implemented. The following is a brief introduction:

If we touch the coordinates of the point Point.x < 13, we'll let Hit-test return to Navigationcontroller.view, give him all the event portals, or return to super, what to do with what to do, so that we can meet our conditions, Even if the current VC has scrollview, but because the click on a specific area, ScrollView not get the event, so the system will concentrate on handling navigationcontroller drag gestures, rather than scrollview events, When no specific area is clicked, the Navigationcontroller gesture does not trigger, and the system will concentrate on the ScrollView events.

Although iOS8 new uiscreenedgepangesturerecognizer gestures, but the simple use of this gesture does not solve the current VC above the ScrollView problem.

When we have identified the Hittestview, our event distribution formally began, if the hittestview can be dealt with directly, on processing, can not deal with, then to the Responder Chain/gesturerecognizer.

Attach some tests to find the logs printed during the Hittestview process, you can observe:

Stpwindow:[hittest:withevent:]----uiview:[hittest:withevent:]--------stpview:[hittest:withevent:]-------- Uicollectionview:[hittest:withevent:]------------uiimageview:[hittest:withevent:]------------uiimageview:[ Hittest:withevent:]------------stdefaultrefreshcontrol:[hittest:withevent:]------------stpfeedcell:[hittest: Withevent:]------------stpfeedcell:[hittest:withevent:]----------------uiview:[hittest:withevent:]------------ --------Uiimageview:[hittest:withevent:]------------------------uiimageview:[hittest:withevent:]-------------- ----------uiview:[hittest:withevent:]------------------------stimageview:[hittest:withevent:]

Where--represents the hierarchy of view

Reference: Http://suenblog.duapp.com/blog/100031/iOS event distribution Mechanism (i)%20hit-testing

iOS Event distribution

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.