Data Sources
A data source is like a delegate, except that its methods supply the data for another object to display. The only Cocoa classes with data sources are UITableView and UIPickerView. A table view displays data in rows; a picker view displays selectable choices using a rotating drum metaphor. In each case, the data source must formally conform to a protocol with required methods(UITableViewDataSource and UIPickerViewDataSource, respectively).
Actions
An action is a message emitted by an instance of a UIControl subclass (a control) to notify you of a significant user event taking place in that control.
What is the UIEvent, and what is it for? Well, a touch event is generated whenever the user does something with a finger (sets it down on the screen, moves it, raises it from the screen). UIEvents are the lowest-level objects charged with communication of touch events to your app. A UIEvent is basically a timestamp (a double) along with a collection (NSSet) of touch events (UITouch). The action mechanism deliberately shields you from the complexities of touch events, but by electing to receive the UIEvent, you can still deal with those complexities if you want to.
In this example, I take advantage of the UIEvent’s timestamp to do one thing if the user releases a UIButton after holding down a finger for a short time, but a different thing if the user releases the UIButton after holding it down for a longer time. Assume that the UIButton’s dispatch table is configured so that its Touch Down control event calls my buttonDown:event: method, and its Touch Up Inside control event calls my buttonUp:event: method:
- (void) buttonDown: (id) sender event: (UIEvent*) e {
self.downtime = [e timestamp]; // downtime is a property and ivar
}
- (void) buttonUp: (id) sender event: (UIEvent*) e {
if ([e timestamp] - self.downtime < 0.3) {
// respond to short tap
} else {
// respond to longer hold and release
}
}
The Responder Chain
A responder is an object that knows how to receive UIEvents directly (see the previous section). It knows this because it is an instance of UIResponder or a UIResponder subclass. If you examine the Cocoa class hierarchy, you’ll find that just about any class that has anything to do with display on the screen is a responder. A UIView is a responder. A UIWindow is a responder. A UIViewController is a responder. Even a UIApplication is a responder.
If you look in the documentation for the UIResponder class, you’ll find that it implements four low-level methods for receiving touch-related UIEvents:touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:and touchesCancelled:
withEvent:. These are called to notify a responder of a touch event. No matter how your code ultimately hears about a user-related touch event — indeed, even if your code never hears about a touch event (because Cocoa reacted in some automatic way to the touch, without your code’s intervention) — the touch was initially communicated to a responder through one of these methods.
The mechanism for this communication starts by deciding which responder the user touched. The UIView methods hitTest:withEvent: and pointInside:withEvent: are called until the correct view (the hit-test view) is located. Then UIApplication’s sendEvent: method is called, which calls UIWindow’s sendEvent:, which calls the correct method of the hit-test view (a responder).
The responders in your app participate in a responder chain, which essentially links them up through the view hierarchy. A UIView can sit inside another UIView, its superview, and so on until we reach the app’s UIWindow (a UIView that has no superview). The responder chain, from bottom to top, looks like this:
1. The UIView that we start with (here, the hit-test view).
2. The UIViewController that controls that UIView, if there is one.
3. The UIView’s superview, and then its UIViewController if there is one. Repeat this step, moving up the superview hierarchy one superview at a time, until we reach…
4. The UIWindow.
5. The UIApplication.