শুক্রবার, ২০ এপ্রিল, ২০১২

iOS Gesture Recognizer: Tap, Pinch Zoom, Rotate, Swipe, Pan, Long Press

The UIGestureRecognizer help us detecting and responding to the various UI gestures of iOS devices. UIGestureRecognizer is an abstract class, which provides the following subclasses:
  1. UITapGestureRecognizer
  2. UIPinchGestureRecognizer
  3. UIRotationGestureRecognizer
  4. UISwipeGestureRecognizer
  5. UIPanGestureRecognizer
  6. UILongPressGestureRecognizer

Gesture Recognizer Examples:

We have defined a recognizer for managing taps. We have specified that there are two taps required, with just one touch (finger). The @selector specifies the method that will be called when the gesture is recognized.

// -----------------------------
// One finger, two taps
// -----------------------------
// Create gesture recognizer
UITapGestureRecognizer *oneFingerTwoTaps = 
  [[[UITapGestureRecognizer alloc] initWithTarget:self action: 
@selector(oneFingerTwoTaps)] autorelease];
 
// Set required taps and number of touches
[oneFingerTwoTaps setNumberOfTapsRequired:2];
[oneFingerTwoTaps setNumberOfTouchesRequired:1];
 
// Add the gesture to the view
[[self view] addGestureRecognizer:oneFingerTwoTaps];
Below is a recognizer for two fingers, two taps:
 
// -----------------------------
// Two fingers, two taps
// -----------------------------
UITapGestureRecognizer *twoFingersTwoTaps = 
  [[[UITapGestureRecognizer alloc] initWithTarget:self action: 
@selector(twoFingersTwoTaps)] autorelease];
[twoFingersTwoTaps setNumberOfTapsRequired:2];
[twoFingersTwoTaps setNumberOfTouchesRequired:2];
[[self view] addGestureRecognizer:twoFingersTwoTaps];
The following gestures recognize swipes up and down:
 
// -----------------------------
// One finger, swipe up
// -----------------------------
UISwipeGestureRecognizer *oneFingerSwipeUp = 
  [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:
 @selector(oneFingerSwipeUp:)] autorelease];
[oneFingerSwipeUp setDirection:UISwipeGestureRecognizerDirectionUp];
[[self view] addGestureRecognizer:oneFingerSwipeUp];
 
// -----------------------------
// One finger, swipe down
// -----------------------------
UISwipeGestureRecognizer *oneFingerSwipeDown = 
  [[[UISwipeGestureRecognizer alloc] initWithTarget:self action: 
@selector(oneFingerSwipeDown:)] autorelease];
[oneFingerSwipeDown setDirection:UISwipeGestureRecognizerDirectionDown];
[[self view] addGestureRecognizer:oneFingerSwipeDown];
Two recognize two fingers rotating on a view:
 
// -----------------------------
// Two finger rotate  
// -----------------------------
UIRotationGestureRecognizer *twoFingersRotate = 
  [[[UIRotationGestureRecognizer alloc] initWithTarget:self action: 
@selector(twoFingersRotate:)] autorelease];
[[self view] addGestureRecognizer:twoFingersRotate];
And finally, two finger pinch:
 
// -----------------------------
// Two finger pinch
// -----------------------------
UIPinchGestureRecognizer *twoFingerPinch = 
  [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:
 @selector(twoFingerPinch:)] autorelease];
[[self view] addGestureRecognizer:twoFingerPinch];

// -----------------------------
// Two finger pan
// ----------------------------- 
panGesture = [[[UIPanGestureRecognizer alloc] initWithTarget:self action: 
@selector(panGestureMoveAround:)] autorelease];
    [panGesture setMaximumNumberOfTouches:2];
    [panGesture setDelegate:self];
 
// -----------------------------
// Long Press Gesture
// ----------------------------- 
UILongPressGestureRecognizer *longPressRecognizer = 
         [[UILongPressGestureRecognizer alloc]
         initWithTarget:self 
         action:@selector(longPressDetected:)];
    longPressRecognizer.minimumPressDuration = 3;
    longPressRecognizer.numberOfTouchesRequired = 1;
    [self.view addGestureRecognizer:longPressRecognizer];
    [longPressRecognizer release];
 

Gesture Recognizer Action Methods:

For each of the above definitions, below are the associated action methods.
 
/*--------------------------------------------------------------
* One finger, two taps 
*-------------------------------------------------------------*/
- (void)oneFingerTwoTaps
{
  NSLog(@"Action: One finger, two taps");
}
 
/*--------------------------------------------------------------
* Two fingers, two taps
*-------------------------------------------------------------*/
- (void)twoFingersTwoTaps {
  NSLog(@"Action: Two fingers, two taps");
} 
 
/*--------------------------------------------------------------
* One finger, swipe up
*-------------------------------------------------------------*/
- (void)oneFingerSwipeUp:(UISwipeGestureRecognizer *)recognizer 
{ 
  CGPoint point = [recognizer locationInView:[self view]];
  NSLog(@"Swipe up - start location: %f,%f", point.x, point.y);
}
 
/*--------------------------------------------------------------
* One finger, swipe down
*-------------------------------------------------------------*/
- (void)oneFingerSwipeDown:(UISwipeGestureRecognizer *)recognizer 
{ 
  CGPoint point = [recognizer locationInView:[self view]];
  NSLog(@"Swipe down - start location: %f,%f", point.x, point.y);
}
 
/*--------------------------------------------------------------
* Two finger rotate   
*-------------------------------------------------------------*/
- (void)twoFingersRotate:(UIRotationGestureRecognizer *)recognizer 
{
  // Convert the radian value to show the degree of rotation
  NSLog(@"Rotation in degrees since last change: %f", [recognizer rotation] *  
(180 / M_PI));
}
 
/*--------------------------------------------------------------
* Two finger pinch
*-------------------------------------------------------------*/
- (void)twoFingerPinch:(UIPinchGestureRecognizer *)recognizer 
{
  NSLog(@"Pinch scale: %f", recognizer.scale);
}
 
/*--------------------------------------------------------------
* Two finger pan
*-------------------------------------------------------------*/ 
-(void)panGestureMoveAround:(UIPanGestureRecognizer *)gesture;
{
    UIView *piece = [gesture view];
 
//We pass in the gesture to a method that will help us align our touches
 so that the pan and pinch will seems to originate between the fingers 
instead of other points or center point of the UIView
    [self adjustAnchorPointForGestureRecognizer:gesture];
 
    if ([gesture state] == UIGestureRecognizerStateBegan ||
 [gesture state] == UIGestureRecognizerStateChanged) {
 
        CGPoint translation = [gesture translationInView:[piece superview]];
        [piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y+ 
translation.y*0.1)];
        [gesture setTranslation:CGPointZero inView:[piece superview]];
    }else if([gestureRecognizer state] == UIGestureRecognizerStateEnded)
    {
       //Put the code that you may want to execute when the UIView became 
       larger than certain value or just to reset them back to their original
       transform scale
    }
} 

/*--------------------------------------------------------------
* Long press
*-------------------------------------------------------------*/ 
- (IBAction)longPressDetected:(UIGestureRecognizer *)sender 
{
    statusLabel.text = @"Long Press";
}

বুধবার, ২১ মার্চ, ২০১২

Create .ipa file or submitting app to appstore with static library like Core-Plot

In one of my project for beta testing, I select "Product->Archive". The archiving completes successfully, and the Organizer shows the archive file that was just created.

I select the archive, click on the "Share" button, it prompts me for location  to store it, and then creates a .xcarchive package to the location. I was expecting it to generate a .ipa, but it never presented me with any options.

 
Then I try to submit app and Xcode 4 was giving me the following error: “[Your App Name] does not contain a single-bundle application or contains multiple products. Please select another archive, or adjust your scheme to create a single-bundle application.”



It was a static library adding issue. I am specifically using Core Plot and it’s instruction set hasn’t been updated for Xcode 4 yet.  Here are the things I had to do to get Core Plot to bundle correctly with my App to submit it:
  1. Click on the Core Plot project which should be a child of your App’s project.
  2. Click on the Project CorePlot-CocoaTouch and go to the Build Settings.  Set “Skip Install” to Yes.
  3. Click on the CorePlot-CocoaTouch target and set “Skip Install” to Yes.
  4. Click Build Phases and under Copy Headers, move all of the Public and Private entries to the Project section.
You should then be able to build your project for Archive and submit to Apple.

রবিবার, ১৮ মার্চ, ২০১২

Add Done Button at numberpad in iPhone

Every keyboard type except number pad offer return key. By using which user can hide keyboard. When looking at the number pad, you'll notice that there is an unused space on its bottom left. That's where we are going to plug in our custom "return" key.

In fact every other keyboard type (except for the pretty similar UIKeyboardTypePhonePad) does offer the possibility to be dismissed by setting the returnKeyType property of the corresponding UITextInputTraits implementor. When looking at the number pad, you'll notice that there is an unused space on its bottom left. That's where we are going to plug in our custom "return" key.



To achieve this we can create two images and like done button in number pad and set this images to a button for state UIControlStateNormal  and UIControlStateHighlighted for iOS 3 and iOS 4. Then add this button to keyboard when keyboard shows.

   

Now back to coding. First we need to know when the number pad is going to be slides up on the screen so we can put our custom button before that happens. Luckily there’s a notification for exactly that purpose, and registering for it is as easy as:
 
 //Add observers for the respective notifications (depending on the os version)
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 3.2) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardDidShow:)
                                                     name:UIKeyboardDidShowNotification
                                                   object:nil];
 
    }
    else
    {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillShow:)
                                                     name:UIKeyboardWillShowNotification
                                                   object:nil];
    }
 
Don't forget to remove the observer from the notification center in the appropriate place once you're done with the whole thing:
 
[[NSNotificationCenter defaultCenter] removeObserver:self];
 
Now we’re getting to the heart of it. All we have to do in the keyboardWillShow method is to locate the keyboard view and add our button to it. iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView. In iOS 3 keyboard view is part of a second UIWindow of our application as others have already figured out. So we take a reference to that window (it will be the second window in most cases, so objectAtIndex:1 in the code below is fine), traverse its view hierarchy until we find the keyboard and add the button to its lower left. So, we need to check device version first then add done button to keyboard.
 
- (void)keyboardWillShow: (NSNotification *)notification {
    if(!addDone){
        return;
    }else{
        if ([[[UIDevice currentDevice] systemVersion] floatValue] < 3.2) {
            [self performSelector:@selector(addHideKeyboardButtonToKeyboard) 
                           withObject:nil afterDelay:0];
        }
    }       
} 
 
 
- (void)keyboardDidShow:(NSNotification *)notification {
    if(!addDone){
       return;
    }else{
       if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 3.2) {
           [self performSelector:@selector(addHideKeyboardButtonToKeyboard) withObject:nil afterDelay:0];
       }
    }  
}
 

- (void)addHideKeyboardButtonToKeyboard {
    UIWindow *keyboardWindow = nil;
    for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
       if (![[testWindow class] isEqual:[UIWindow class]]) {
           keyboardWindow = testWindow;
           break;
       }
    }
    if (!keyboardWindow) return;
   
    // Locate UIKeyboard.
    UIView *foundKeyboard = nil;
    for (UIView *possibleKeyboard in [keyboardWindow subviews]) {
      
       // iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView.
       if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"]) {
           possibleKeyboard = [[possibleKeyboard subviews] objectAtIndex:0];
       }                                                                               
      
       if ([[possibleKeyboard description] hasPrefix:@"<UIKeyboard"]) {
           foundKeyboard = possibleKeyboard;
           break;
       }
    }
   
    if (foundKeyboard) {
       [self addButtonToKeyboard];
    }
}



 
-(void)addButtonToKeyboard {
    // create custom button
    doneButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
    doneButton.frame = CGRectMake(0, 163, 106, 53);
    doneButton.adjustsImageWhenHighlighted = NO;
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 3.0) {
       [doneButton setImage:[UIImage imageNamed:@"DoneUp3.png"] forState:UIControlStateNormal];
       [doneButton setImage:[UIImage imageNamed:@"DoneDown3.png"] forState:UIControlStateHighlighted];
    } else {       
       [doneButton setImage:[UIImage imageNamed:@"DoneUp.png"] forState:UIControlStateNormal];
       [doneButton setImage:[UIImage imageNamed:@"DoneDown.png"] forState:UIControlStateHighlighted];
    }
    [doneButton addTarget:self action:@selector(removeNumberKeyboardWhenDonePressed:) forControlEvents:UIControlEventTouchUpInside];
    // locate keyboard view
    UIView *keyboard;
    UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
    for(int i=0; i<[tempWindow.subviews count]; i++) {
       keyboard = [tempWindow.subviews objectAtIndex:i];
       // keyboard found, add the button
       if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 3.2) {
           if([[keyboard description] hasPrefix:@"<UIPeripheralHost"] == YES)
               [keyboard addSubview:doneButton];
       } else {
           if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES)
               [keyboard addSubview:doneButton];
       }
    }
    buttonAdded = YES;
} 
 
-(void)addButtonToKeyboard {
    // create custom button
    doneButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
    doneButton.frame = CGRectMake(0, 163, 106, 53);
    doneButton.adjustsImageWhenHighlighted = NO;
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 3.0) {
       [doneButton setImage:[UIImage imageNamed:@"DoneUp3.png"] forState:UIControlStateNormal];
       [doneButton setImage:[UIImage imageNamed:@"DoneDown3.png"] forState:UIControlStateHighlighted];
    } else {       
       [doneButton setImage:[UIImage imageNamed:@"DoneUp.png"] forState:UIControlStateNormal];
       [doneButton setImage:[UIImage imageNamed:@"DoneDown.png"] forState:UIControlStateHighlighted];
    }
    [doneButton addTarget:self action:@selector(removeNumberKeyboardWhenDonePressed:) forControlEvents:UIControlEventTouchUpInside];
    // locate keyboard view
    UIView *keyboard;
    UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
    for(int i=0; i<[tempWindow.subviews count]; i++) {
       keyboard = [tempWindow.subviews objectAtIndex:i];
       // keyboard found, add the button
       if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 3.2) {
           if([[keyboard description] hasPrefix:@"<UIPeripheralHost"] == YES)
               [keyboard addSubview:doneButton];
       } else {
           if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES)
               [keyboard addSubview:doneButton];
       }
    }
    buttonAdded = YES;
}
 

That’s it! The empty space for our button starts at coordinate
 (0, 163) and has the dimensions (106, 53). 



We are done. This project can be downloaded from here.