Ios-target-action mechanism to create your own UI controls need to know the knowledge

Source: Internet
Author: User
Tags uicontrol

When we develop our applications, we often use a variety of controls, such as buttons ( UIButton ), sliders ( UISlider ), pagination controls (), UIPageControl and so on. These controls are used to interact with the user in response to the user's actions. We look at the inheritance systems of these classes, and we can see that they are all inherited from the UIControl class. UIControlis the base class of the control class, which is an abstract base class, and we cannot instantiate the control directly using a UIControl class, it simply defines some common interfaces for the control subclass and provides some basic implementations to preprocess the messages and send them to the specified target object when the event occurs.

This article will look UIControl at UIControl the basic usage method through a custom subclass. But before we get started, let's take a look at the Target-Action mechanics.

Target-action mechanism

Target-actionis a design pattern, literal translation is "goal-behavior". When we add a click event to a button by code, it is usually handled as follows:

1 [button addTarget:self action:@selector(tapButton:) forControlEvents:UIControlEventTouchUpInside];

That is, when a button's Click event occurs, the message is sent to (this is target the Self object), and the target corresponding event is handled by the object's tapButton: methods. The basic process can be used to describe:

Note: Images from official documents Cocoa application competencies for Ios–target Action

That is, when an event occurs, the event is sent to the control object, which is then triggered by the control object to trigger target the behavior on the object action to finally handle the event. Therefore, the mechanism consists of Target-Action two parts: The target object and the behavior Selector . The target object specifies the object that eventually handles the event, and the behavior Selector is the method that handles the event.

For Target-Action A detailed description of the mechanism, you can refer to Cocoa application competencies for Ios–target Action. We will discuss some Target-action more in-depth things below.

Example: A picture control with a label

Back to our point, we're going to implement a picture control with a label. Typically, we implement a custom control based on the following two reasons:

    • For a particular event, we need to observe or modify target the behavior message that is distributed to the object.

    • Provides custom tracking behavior.

This example will simply combine the two. First look at the effect:

The control is simple, takes a picture as a background, and then displays a label below it.

To create UIControl a subclass first, we need to pass in a string and a UIImage object:

12345 @interface ImageControl : UIControl- (instancetype)initWithFrame:(CGRect)frame title:(NSString *)title image:(UIImage *)image;@end

The basic layout we are not discussing here. Let's take a look at the UIControl methods that provide us with custom tracking behavior.

Tracking Touch Events

If you are trying to provide custom tracking behavior, you can override the following methods:

1234 - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event- (void)cancelTrackingWithEvent:(UIEvent *)event

These four methods correspond to start, move, end, and cancel four states respectively. Does it look familiar? Does this UIResponse look like the four event tracking methods offered? Let's take a look at UIResponse the four ways:

1234 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

As we can see, the parameters of the two sets of methods are basically the same, except for a UIControl single touch and UIResponse possibly multi-touch. In addition, the return value is similar. Because UIControl of itself is the view, so it actually inherited also UIResponse these four methods. If we test it, we will find that both sets of methods are called and do not interfere when touch events occur for the control.

To determine whether the current object is tracking touch operations, UIControl a property is defined tracking . If yes, the value indicates that it is being traced. This is more convenient for us, and we do not need to define a variable to handle it.

In the test, we can find that when our touch points move out of the control area name along the screen, or continue to track the touch operation, the cancelTrackingWithEvent: message is not sent. To determine whether the current touch point is in the control area class, you can use the touchInside property, which is a read-only property. However, the result of the measurement is that within a certain range around the control area, the value is still marked as yes, meaning that the area used to determine touchInside Yes is larger than the control area.

Observe or modify the behavior message that is distributed to the target object

For a given event, it UIControl is called sendAction:to:forEvent: to forward the behavior message to the UIApplication object, and the UIApplication object calls its sendAction:to:fromSender:forEvent: method to distribute the message to the specified target , and if we do not specify target , The event is distributed to the first object on the response chain that wants to process the message. You can override this method if the subclass wants to monitor or modify this behavior.

In our case, we did a little processing, adding the externally added Target-Action inside the control to handle the event, so our code is implemented as follows:

123456789101112131415161718192021222324252627 // ImageControl.m- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {  // 将事件传递到对象本身来处理    [supersendAction:@selector(handleAction:) to:self forEvent:event];}- (void)handleAction:(id)sender {    NSLog(@"handle Action");}// ViewController.m - (void)viewDidLoad {    [superviewDidLoad];    self.view.backgroundColor = [UIColor whiteColor];    ImageControl *control = [[ImageControl alloc] initWithFrame:(CGRect){50.0f, 100.0f, 200.0f, 300.0f} title:@"This is a demo"image:[UIImage imageNamed:@"demo"]];    // ...    [control addTarget:self action:@selector(tapImageControl:) forControlEvents:UIControlEventTouchUpInside];}- (void)tapImageControl:(id)sender {    NSLog(@"sender = %@", sender);}

Since we have rewritten the sendAction:to:forEvent: method, the last way to handle the event Selector is ImageControl the method handleAction: , not the Viewcontroller tapImageControl: method.

In addition, sendAction:to:forEvent: it is actually UIControl called by another method, that is sendActionsForControlEvents: . The function of this method is to send all the behavior messages related to the specified type. We can call this method of the control anywhere (both inside and outside the control) to send the controlEvents message specified by the parameter. In our example, the following tests were made in VIEWCONTROLLER.M:

123456 - (void)viewDidLoad {    // ...    [control addTarget:self action:@selector(tapImageControl:) forControlEvents:UIControlEventTouchUpInside];    [control sendActionsForControlEvents:UIControlEventTouchUpInside];}

You can see that the event is triggered and the log is printed when the control is not clicked UIControlEventTouchUpInside handle Action .

Management of Target-action

Adding and removing actions for a control object Target-Action we are already familiar with the following two methods:

1234 // 添加- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents

If you want to get all the relevant target objects for a control object, you can call the allTargets method, which returns a collection. The collection may contain NSNull objects that represent at least one nil target object.

You can call a method if you want to get a target object and all the action related to the event actionsForTarget:forControlEvent: .

However, these are all UIControl open interfaces. We still want to explore, UIControl How to manage it Target-Action ?

In fact, we can see the result by hitting a breakpoint at a suitable location in the program to observe UIControl the internal structure:

Therefore, Uicontrol internally actually has a mutable array ( _targetactions ) to save target-action , Each element in the array is a uicontroltargetaction object. The uicontroltargetaction class is a private class, and we can find its header file in Ios-runtime-header:

1234567891011121314 @interface   uicontroltargetaction : nsobject {      sel _action;      bool _cancelled;      unsigned int _eventmask;      id _target;   @property   (nonatomic)  BOOL  Cancelled;  -  (void). Cxx_destruct; -  (BOOL) cancelled; -  (void) setcancelled: (BOOL) arg1;   @end

You can see that the UIControlTargetAction object maintains a Target-Action necessary three-factor, that is target , the action corresponding event eventMask .

If you think about it, you'll find an interesting question. Let's take a look at the reference relationship between Viewcontroller (target) and ImageControl instance (control) in the instance, as shown in:

Well, circular references.

In this case, we must find ways to break this circular reference. So what is the best place to do it in these 5 links? Careful thinking, 1, 2, 4 is certainly not, 3 is not very suitable, it is only 5. In the above UIControlTargetAction header file, there is no way to see that _target is weak declared in the way, there is evidence?

We make a breakpoint in the project Symbolic , as shown below:

Run the program, the program will go to [UIControl addTarget:action:forControlEvents:] the assembly code page of the method, here we can find some clues. As shown in the following:

As you can see, for a _target member variable, it is called in the initialization method, that is, the UIControlTargetAction objc_storeWeak member variable is referenced in the way that the externally passed in target object is weak .

In fact UIControl , in the documentation, the addTarget:action:forControlEvents: description of the method also has this sentence:

When you call the This method, the target is not retained.

Also, if we call the method multiple times with the same set of Target-action and event addTarget:action:forControlEvents: , the _targetActions object will not be added repeatedly UIControlTargetAction .

Summary

Control is our common view tool in development, which can express the user's intention very well. We can use the controls provided by Uikit, or you can customize the controls. Of course, UIControl in addition to some of the above methods, there are some properties and methods, as well as some constants, you can refer to the document.

Ios-target-action mechanism to create your own UI controls need to know the knowledge

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.