Saturday, 26 April 2014

Run Loop [Coming Events]

iOS apps sit perfectly still, waiting for something to happen. This is a very important feature of app design, because it keeps your app efficient; the code in your app only runs when theres something important to do.

This, seemingly innocuous, arrangement is critical to keeping your users happy. Running computer code requires electricity, and electricity in mobile devices is a precious commodity. Keeping your code from running atunnecessary times allows iOS to conserve power. It does this by turning off,
or minimizing, the amount of power the CPU and other hardware accessories use when they are not needed. This power management happens hundreds of times a second, but its crucial to the battery life of mobile devices, andusers love mobile devices with long battery life.

The code in your app is at the receiving end of two mechanisms: a run loop and an event queue.
The run loop is what executes code in your app when something happens, and stops your app from running when theres nothing to do. The event queue is a data structure containing the list of events waiting to be processed.As long as there are events in the queue, the run loop sends them—one
at a time—to your app. As soon as all of the events have been processed, and the event queue is empty, your app stops executing code.

Conceptually, your apps run loop looks like this:

while   ( true ) {
UIEvent  *event = [iOS  waitForNextEvent]; [yourApp  processEvent:event];
}

The magic is in the -waitForNextEvent message (which doesnt exist, I made it up). If theres an event waiting to be processed, that event is removed from the queue and returned. The run loop passes it to your app for processing.If theres no event, the function simply doesnt return; your app is suspended until theres something to do. Now lets look at what those events are and where they come from.


Event Queue

Events waiting to be processed are added to a FIFO (first in, first out) buffer called the event queue.
There are different kinds of events, and events come from different sources, as shown in Figure 4-1.


Figure 4-1. The Event Queue


Lets follow one event through your app. When you touch your finger to the surface of an iOS device, heres what happens:

1.       Hardware in the screen detects the location of the touch.

2.       This information is used to create a touch event object, which records the position of the touch, what time it occurred, and other information.

3.       The touch event object is placed in the event queue of your app.

4.       The run loop pulls the touch event object from the queue and passes it to your application object.

5.       Your application object uses the geometry of the active views in your app to determine which view your finger “touched.”

6.       An event message, containing the touch event, is sent to that view object.

7.       The view object decides what the touch event means and what it will do.
It might highlight a button or send an action message.

When you touched the “shorten URL” button in the Shorty app from Chapter 3, thats how the physical act of touching the screen turned into the -shortenURL: message your view controller received.

Different event types take different paths. The next few sections will describe the different delivery methods, along with the types of events that each deliver. 

Event Delivery

Event delivery is how an event gets from the event queue to an object in your app. Different types of events take different paths, befitting their purpose. The actual delivery mechanism is a combination of logic in the Cocoa Touch framework, your application object, and various methods defined by your app objects.

Broadly speaking, there are three delivery methods:

n     Direct delivery

n     Hit testing

n     First responder

The next few sections will describe each of these three methods, and the events that get delivered that way.


Direct Delivery

Direct delivery is the simplest form of event delivery. A number of event types target specific objects. These events know which object(s) will receive them, so theres not much to know about how these events are delivered,beyond the fact that they’re dispatched by the run loop.

For example, an Objective-C message can be placed in the event queue. When that event is pulled from the queue, the message is sent to its target object. Thats how the web view told your Shorty app when the web page hadloaded. When the network communications code (running in its own thread) determined the page had finished loading, it pushed a -webViewDidFinishLoad: message onto the main threads event queue. As your main threadpulled events from its event queue, one of those events sent that message to your web view delegate object, telling it that the page had loaded.

Other events that are sent to specific objects, or groups of objects, are notifications, timer events, anuser interface updates. All of these events know, either directly or indirectly, which objects they will
bsent to. As an app developer, all you need to know is that when those events work their way to thend of the event queue, the run loop will send an Objective-C message to one or morobjects.


Hit Testing

Hit testing delivers events based on the geometry of your user interface, and it applies only to touch events. When a touch event occurs, the UIWindow and UIView objects work together to determine which view objectcorresponds to the location of the touch. Messages are then sent to that view  object, which interprets those events however it chooses; it may flip a switch, scroll a shopping list, or blow up a spaceship. Lets take a quick look at how hit testing works.

When a touch event is pulled from the event queue, it contains the absolute hardware coordinates where the touch occurred, as shown on the left in Figure 4-2. This example will use a stylized representation of the Shorty appfrom the previous chapter.



Figure 4-2. Hit testing a touch event

Your UIApplication object uses the event coordinates to determine the UIWindow object thatresponsible for that portion of the screen. That UIWindow object receives a -sendEvent: message containing the touch eventobject to process.

The UIWindow object then performs hit testing. Starting at the top of its view hierarchy, it sends a -hitTest:withEvent: message to its top-level view object, as shown in the second panel of Figure 4-2.

The top-level view first determines if the event is within its bounds. It is, so it starts to look for any subviews that contain the touch coordinate. The top-level view contains three subviews: the navigation toolbar at the top, the webview in the middle, and the toolbar at the bottom. The touch is within the bounds of the toolbar, so it passes on the -hitTest:withEvent: message to the toolbar.

The toolbar repeats the process, looking for a subview that contains the location, as shown in the third frame of
Figure 4-2. The toolbar object discovers that touch occurs inside the leftmost bar button item bounds. The barbutton item is returned as the “hit” object, which causes UIWindow to begin sending it low-level touch event messages.

Being a “button,”  the bar button item object examines the events to determine if the user tapped the button (as opposed to swiping it or some other irrelevant gesture). If they did, the button sends its action message, in thiscase -shortenURL:, to the object its connected to.

The First Responder

The first responder is a view, view controller, or window object in your active interface (a visible window). Think of it as the designated receiver for events that arent determined by hit testing. I’ll talk about how an object becomesthe first responder later in this chapter. For now, just know that every active interface has a first responder.

Events that get delivered to the first responder are:

n     Shake motion events

n     Remote control events

n     Key events

The shake motion event tells your app that the user is shaking their device (moving it rapidly back and forth). This information comes from the accelerometer hardware.

So-called remote control events are generated when the user presses any of the multi-media controls, which include:

n     Play

n     Pause

n     Stop

n     Skip to Next Track

n     Skip to Previous Track

n     Fast Forward

n     Fast Backward

These are called remote” events because they could originate from external accessories, such as the play/pause button on the cord of many headphones. In reality, they most often come from the play/pause buttons you seeon the screen.

Key events come from tapping on the virtual keyboard, or from a hardware keyboard connected via Bluetooth.

To review, direct delivery sends event objects or Objective-C messages directly to their target object(s). Touch events use hit testing to determine which view object will receive them, and all other events are sent to the firstresponder. Now its time to handle those events.

Event Handling

You’ve arrived at the second half of your event processing journey: event handling. In simple terms, an object handles or responds to an event if it contains code to interpret that event and decide what it wants to do about it.
I’ll get the direct delivery events out of the way first. An object receiving a direct delivery event must have a method to process that event, message, or notification. This is not optional. If the object doesnt implement themessage sent it, your application will malfunction and could crash. Thats all you need to know about directly delivered events.
Other event types are much more forgiving. Much like optional delegate methods, if your object implements the methods for handling an event, it will receive those events. If it isnt interested in handling that type of event, yousimply omit those methods from its implementation and iOS will go looking for another object that wants to handle them.

To handle touch events, for example, you add the following methods to your class’ implementation:

-  (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

If hit testing determines that your object should receive touch events, it will receive a
-touchesBegan:withEvent: message when the hardware detects a physical touch in your
view, a -touchesMoved:withEvent: whenever the position changes (a dragging gesture), and a-touchesEnded:withEvent: message when contact with the screen is removed. As the user taps and drags their fingers across the screen, your object may receive many of these messages, often in rapid succession.

If you omit all of these methods from your class, your object will not handle any touch events.

All of the methods for handling events are inherited from the UIResponder class, and each type of event has one or more methods that you must implement if you want to handle that 
event. The UIResponder class documentation has a complete list of event handling methods.

So what happens if the hit test or first responder object ignores the event? Thats a good question, and the answer is found in the responder chain.

The Responder Chain

The responder chain is a string of objects that represent the “focus” of your user interface. What I mean by “focus” is those objects controlling the currently visible interface, and those view objects that are most relevant to whatthe user is doing. Does that sound all vague and confusing? A picture and an explanation of how iOS creates and uses the responder chain will make things clear.

The responder chain starts with the initial responder (see Figure 4-3). When delivering motion, key, and remote events, the first responder is the initial responder object. For touch events, the initial responder is the view objectdetermined by hit testing.


Figure 4-3. First responder chain

iOS begins by trying to deliver that event to the initial responder. Trying” is the key word here.
If the object has methods that handle that event, it does so. If not, iOS moves onto the next object in the chain until it either finds an object that wants to process the event or it gives up and throws the event away.

Figure 4-3 shows the conceptual organization of view objects in an app with two screens. The second screen is currently being shown to the user. It consists of a view controller object, a number of subviews, some nestedinside of other subviews, and even a sub-view controller. In this example,
a sub-sub-view has been designated the initial responder, which would be appropriate after a hit test determined that the user touched that view.

iOS will try to deliver the touch event to the initial responder (the sub-sub-view). If that object doesnt handle touch events, iOS sees if that view has a view controller object (it doesnt) and tries to send the event to its controller. Ifneither the view nor its controller handle touch events, iOS finds the view that contains that view (its superview), and repeats the entire process until it runs out of views and view controllers to try.

After all of the view and view controller objects have been given a chance to handle the event, delivery moves on to the window object for that screen, and finally to the single application object.

What makes the responder chain so elegant is its dynamic nature and ordered processing of events.
The responder chain is created automatically, so your object doesnt have to do anything to be a part of the responder chain, except to make sure that either it, or a subsidiary view, is the initial responder. Your object willreceive event messages while that portion of your interface is active, and wont receive events when its not.

The other aspect is the specific-to-general nature of responder chain event handling. The chain always starts at the view thats most relevant to the user: the button they touched, an active text input field, or a row in a list. Thatobject always receives the events first. If the event has specific meaning to those views, its processed accordingly. At the same time, your view controller or UIApplication object could also respond to those events, but if one ofthe subviews handles it first, those objects wont receive it.

If the user moves to another screen, as shown in Figure 4-4, and presses the “pause” button on their headphones, a new responder chain is established. This chain starts at the first responder, which in this case is a view controller. The chain doesnt include any view objects at all, because the top-level view controller object is the first responder.


Figure 4-4. Second responder chain

If the view controller handles the “pause event, then it does so. The view controllers in other interfacenever see the event. By implementing different “pause event handling code in the various controllersyour apps response to a“pause event will be different, depending on which screen is active.

Your application object could also handle the “pause” event. If none of the view controllers handled the “pause” event, then all “pause” events would trickle down to the application object. This would be the arrangement you’d use if you wanted all “pause” events to be handled the same way, regardless of what screen the user was looking at.

Finally, you can mix these solutions. A “pause” event handler in the application could handle the event in a generic way, and then specific view controllers could intercept the event if pressing the “pause” button has specialmeaning in that screen.

High- vs. Low-Level Events

Programmers are perpetually labeling things as high level or low level. Objects in your app form a kind of pyramid. A few complex objects at the top are constructed from more primitive objects in the middle, which arethemselves constructed from even more primitive objects. The complex objects
on top are called the “high-level” objects (UIApplicationUIWebView). The simple objects at the bottom are called the “low-level” objects (NSNumber, NSString). Similarly, programmers will talk about high- and low-levelframeworks, interfaces, communications paths, and so on.

Events, too, come in different levels. Low-level events are the nitty-gritty, moment-by-moment, details that are happening right now. The touch events are examples of low-level events. Another example is the instantaneous forcevector values that you can request from the accelerometer and gyroscope hardware.

At the other end of the scale are high-level events, like the shake motion event. Another example is the UIGestureRecognizer objects that interpret complex touch event patterns and turn those into a single high-level event, like “pinched” or “swiped.”

When you design your app, you must decide what level of events you want to process. In the next app, you’re going to use the shake motion event to trigger actions in your app.

To do that, you could request and handle the low-level accelerometer events. You would have to create variables to track the force vectors for each of the three movement axes (X, Y, and Z). When you detected that the devicewas accelerating in a particular direction, you would record that direction and start a timer. If the direction of travel reversed, within a reasonable angle of trajectory and within a short period of time, and then reversed two orthree more times, you could conclude that the user was shaking the device.

Or, you could let iOS do all of those calculations for you and simply handle the shake motion events generated by the Cocoa Touch framework. When the user starts to shake their device, your first responder receives a -motionBegan:withEvent: message. When the user stops shaking it, your object receives a -motionEnded:withEvent: message. Its that simple.

That doesnt mean you’ll never need low-level events. If you were writing a game app where your user directed a star-nosed mole through the soil of a magical garden by tilting the device from side-to-side, then interpreting thelow-level accelerometer events would be the correct solution. You’ll use the low-level accelerometer events in Chapter 16.

Decide what information you need from events, and then handle the highest-level events that give you that information. Now you’re ready to start designing your app.

Eight Ball

The app you’ll create mimics the famous Magic Eight Ball toy from the 1950s (http://en.wikipedia.org/wiki/Magic_Eight_Ball). The app works by displaying an eerily prescient message whenever you shake your iOS device. Start bysketching out a quick design for your app.

Design

The design for this app is the simplest so far: a screen containing a message is displayed in
the center of a “ball,” as shown in Figure 4-5. When you shake the device, the current message disappears. When you stop shaking it, a new message appears.


Figure 4-5. EightBall app design

No comments:

Post a Comment