Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (8.37 MB, 640 trang )
Discussion
The UIRotationGestureRecognizer, as its name implies, is the perfect candidate among
gesture recognizers to detect rotation gestures and to help you build more intuitive
graphical user interfaces. For instance, when the user encounters an image on the screen
in your application in full-screen mode, it is quite intuitive for her to attempt to correct
the orientation by rotating the image.
The UIRotationGestureRecognizer class implements a property named rotation that
specifies the total amount and direction of rotation requested by the user’s gesture, in
radians. The rotation is determined from the fingers’ initial position (UIGestureRecog
nizerStateBegan) and final position (UIGestureRecognizerStateEnded).
To rotate UI elements that inherit from UIView class, you can pass the rotation property
of the rotation gesture recognizer to the CGAffineTransformMakeRotation function to
make an affine transform, as shown in the example.
The code in this recipe’s Solution passes the current object, in this case a view controller,
to the target of the rotation gesture recognizer. The target selector is specified as han
dleRotations:, a method we have to implement. But before we do that, let’s have a look
at the header file of our view controller:
#import
@interface RootViewController
@protected
UILabel
UIRotationGestureRecognizer
CGFloat
}
: UIViewController {
*helloWorldLabel;
*rotationGestureRecognizer;
rotationAngleInRadians;
@property (retain) UIRotationGestureRecognizer *rotationGestureRecognizer;
@property (retain) IBOutlet UILabel
*helloWorldLabel;
@property (assign) CGFloat
rotationAngleInRadians;
@end
helloWorldLabel is a label that we must create on the view of our view controller.
Then we will write the code that will rotate this label whenever the user attempts to
perform rotation gestures on the view that owns this label, in this case the view of
the view controller.
rotationGestureRecognizer is the instance of the rotation gesture recognizer that we
will later allocate and initialize.
rotationAngleInRadians is the value we will query as the exact rotation angle of
our label. Initially we will set this to zero. Since the rotation angles reported by a
rotation gesture recognizer are reset every time the rotation gesture is started again,
we can keep the value of the rotation gesture recognizer whenever it goes into the
UIGestureRecognizerStateEnded state. The next time the gesture is started, we will
add the previous value to the new value to get an overall rotation angle.
178 | Chapter 5: Implementing Gesture Recognizers
www.it-ebooks.info
Before we go any further, let’s go into the XIB file of our view controller, create
the corresponding Hello World label on its view, and link it to the IBOutlet that we
declared in the header file of our view controller:
1. Open the corresponding XIB file in Interface Builder. You can double-click on the
XIB file or press the Command + down arrow keys on the XIB file in Xcode.
2. In Interface Builder, use Tools→Library to bring up the Library pane.
3. Select the Label component from the Library pane, as shown in Figure 5-1, and
drag and drop it into the view of the view controller whose XIB file we have opened,
as shown in Figure 5-2.
4. Align the label in the center in the Attributes Inspector, which you can open using
Tools→Attributes Inspector in Interface Builder (Figure 5-3).
5. Select the label that you dropped on the view. Now open the Connections Inspector
in Interface Builder using Tools→Connections Inspector. Grab the New Reference
Outlet of the label in the Connections Inspector and drop it into the instance of
the label on the view, as shown in Figure 5-4.
Any component inheriting from UIView could be selected in the Library tab as our target
component. All UIView descendants have the transform property that we are going to
use in this section. For the sake of simplicity, I have chosen a label.
Figure 5-1. Selecting the Label component in the Library pane
5.2 Reacting to Rotation Gestures | 179
www.it-ebooks.info
Figure 5-2. Center-aligned label dropped on the view of a view controller with a navigation bar on top
Figure 5-3. Center-aligning the label using the Layout option in the Attributes Inspector pane in
Interface Builder
The size of the label does not matter much. Even the position of the label isn’t that
important, as we will only attempt to rotate the label around its center, no matter where
on our view the label is positioned. The only important thing to remember is that in
universal applications, the position of a label on a view controller used in different
180 | Chapter 5: Implementing Gesture Recognizers
www.it-ebooks.info
targets (devices) must be calculated dynamically using the size of its parent view. Otherwise, on different devices such as the iPad or the iPhone, it might appear in different
places on the screen.
Using the Layout option, we will center-align the contents of our label. The rotation
transformation that we will apply to this label rotates the label around its center, and
left-aligned or right-aligned labels whose actual frame is bigger than the minimum
frame that is required to hold their contents without truncation will appear to be rotating in an unnatural way and not on the center. If you are curious, just go ahead and
left- or right-align the contents of the label and see what happens.
After connecting the New Referencing Outlet of the label to the File’s Owner item as
shown in Figure 5-4, you will be presented with a dialog asking which outlet you want
to connect the component to, as shown in Figure 5-5.
Choose the helloWorldLabel outlet.
Figure 5-4. Connecting the referencing outlet of the label to its corresponding UILabel object in our
view controller
5.2 Reacting to Rotation Gestures | 181
www.it-ebooks.info
Figure 5-5. Available outlets in our view controller’s header file
Now we will implement our view controller in this way:
#import "RootViewController.h"
@implementation RootViewController
@synthesize rotationGestureRecognizer;
@synthesize helloWorldLabel;
@synthesize rotationAngleInRadians;
- (void) handleRotations:(UIRotationGestureRecognizer *)paramSender{
if (self.helloWorldLabel == nil){
return;
}
/* Take the previous rotation and add the current rotation to it */
self.helloWorldLabel.transform =
CGAffineTransformMakeRotation(self.rotationAngleInRadians +
paramSender.rotation);
/* At the end of the rotation, keep the angle for later use */
if (paramSender.state == UIGestureRecognizerStateEnded){
self.rotationAngleInRadians += paramSender.rotation;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
/* Create the gesture recognizer */
UIRotationGestureRecognizer *rotationRecognizer =
[[UIRotationGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleRotations:)];
self.rotationGestureRecognizer = rotationRecognizer;
[rotationRecognizer release];
/* Add it to our view */
[self.view addGestureRecognizer:self.rotationGestureRecognizer];
}
- (void) viewDidUnload{
[super viewDidUnload];
182 | Chapter 5: Implementing Gesture Recognizers
www.it-ebooks.info
}
self.helloWorldLabel = nil;
self.rotationGestureRecognizer = nil;
- (BOOL)shouldAutorotateToInterfaceOrientation
:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)dealloc {
[rotationGestureRecognizer release];
[helloWorldLabel release];
}
[super dealloc];
@end
The way a rotation gesture recognizer sends us the rotation angles is very interesting.
This gesture recognizer is continuous, which means it starts finding the angles as soon
as the user begins her rotation gesture, and sends updates to the handler method at
frequent intervals until the user finishes the gesture. Each message treats the starting
angle as zero and reports the difference between the messages’ starting point (which is
the angle where the previous message left off) and its ending point. Thus, the complete
effect of the gesture can be discovered only by summing the angles reported by the
different events. Clockwise movement produces a positive angular value, whereas
counterclockwise movement produces a negative value.
If you are using iPhone Simulator instead of a real device, you can still
simulate the rotation gesture by holding down the Option key in the
simulator. You will see two circles appearing on the simulator at the
same distance from the center of the screen, representing two fingers. If
you want to shift these fingers from the center to another location while
holding down the Alt key, press the Shift key and point to somewhere
else on the screen. Where you leave off your pointer will become the
new center for these two fingers.
Now we will simply assign this angle to the rotation angle of our label. But can you
imagine what will happen once the rotation is finished and another one starts? The
second rotation gesture’s angle will replace that of the first rotation in the rotation
value reported to our handler. For this reason, whenever a rotation gesture is finished,
we must keep the current rotation of our label. The value in each rotation gesture’s
angle must be added in turn, and we must assign the result to the label’s rotation
transformation as we saw before.
As we saw earlier, we used the CGAffineTransformMakeRotation function to create an
affine transformation. Functions in the iOS SDK that start with “CG” refer to the Core
5.2 Reacting to Rotation Gestures | 183
www.it-ebooks.info
Graphics framework. For programs that use Core Graphics to compile and link successfully, you must make sure the Core Graphics framework is added to the list of
frameworks. To do this, follow these steps:
1.
2.
3.
4.
Find and select your target output in Xcode, as shown in Figure 5-6.
Choose File→Get Info. Now you will see the Target Information page.
At the top of the Target Information page, choose the General tab.
At the bottom of the page, make sure Core Graphics is added to the Linked Libraries section. If it is not, click the + button and choose Core Graphics from the list.
Finally, click the Add button, as shown in Figure 5-7.
Figure 5-6. Finding and selecting our target in Xcode
You must add the required frameworks for each target individually even
if you have duplicated one target to create other targets. Adding a
framework to the Frameworks folder directly will add the frameworks
to the current target only.
Now that we are sure Core Graphics is added to our target, we can compile and run
our application.
184 | Chapter 5: Implementing Gesture Recognizers
www.it-ebooks.info
Figure 5-7. Adding the Core Graphics framework to our target
See Also
Recipe 5.6
5.3 Detecting Panning and Dragging Gestures
Problem
You want the users of your application to be able to move GUI elements around using
their fingers.
Solution
Use the UIPanGestureRecognizer class:
- (void)viewDidLoad {
[super viewDidLoad];
/* Let's first create a label */
CGRect labelFrame = CGRectMake(0.0f,
0.0f,
150.0f,
100.0f);
/*
/*
/*
/*
X */
Y */
Width */
Height */
UILabel *newLabel = [[UILabel alloc] initWithFrame:labelFrame];
5.3 Detecting Panning and Dragging Gestures | 185
www.it-ebooks.info
self.helloWorldLabel = newLabel;
[newLabel release];
self.helloWorldLabel.text = @"Hello World";
/* Contrasting colors to catch more attention */
self.helloWorldLabel.backgroundColor = [UIColor blackColor];
self.helloWorldLabel.textColor = [UIColor whiteColor];
/* Center align the text to make it look better */
self.helloWorldLabel.textAlignment = UITextAlignmentCenter;
/* Make sure to enable user interaction; otherwise, tap events
won't be caught on this label */
self.helloWorldLabel.userInteractionEnabled = YES;
/* And now make sure this label gets displayed on our view */
[self.view addSubview:self.helloWorldLabel];
/* Create the Pan Gesture Recognizer */
UIPanGestureRecognizer *newPanGestureRecognizer =
[[UIPanGestureRecognizer alloc]
initWithTarget:self
action:@selector(handlePanGestures:)];
self.panGestureRecognizer = newPanGestureRecognizer;
[newPanGestureRecognizer release];
/* At least and at most we need only one finger to activate
the pan gesture recognizer */
self.panGestureRecognizer.minimumNumberOfTouches = 1;
self.panGestureRecognizer.maximumNumberOfTouches = 1;
/* Add it to our view */
[self.helloWorldLabel addGestureRecognizer:self.panGestureRecognizer];
/* Get rid of the navigation bar for now as we don't need it */
self.navigationController.navigationBarHidden = YES;
}
- (void) viewDidUnload{
[super viewDidUnload];
self.panGestureRecognizer = nil;
self.helloWorldLabel = nil;
}
The pan gesture recognizer will call the handlePanGestures: method as its target method. This method is described in this recipe’s Discussion.
Discussion
The UIPanGestureRecognizer, as its name implies, can detect pan gestures. Pan gestures
are continuous movements of fingers on the screen; recall that swipe gestures were
186 | Chapter 5: Implementing Gesture Recognizers
www.it-ebooks.info