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. UIControl
is 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-action
is 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 {
// 将事件传递到对象本身来处理
[
super
sendAction:@selector(handleAction:) to:self forEvent:event];
}
- (void)handleAction:(id)sender {
NSLog(@
"handle Action"
);
}
// ViewController.m - (void)viewDidLoad {
[
super
viewDidLoad];
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