I use the following code to swipe left and right with a UISwipeGestureRecognizer to show photos in my app in a similar way to iPhoto.
CATransition *animation = [CATransition animation];
[animation setDuration:0.5];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromRight];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[[self.imageHolder layer] addAnimation:animation forKey:@"SwitchToView"];
This slides the next image in and the old image out like shown in the pictures below:
Image One:
Transition:
Image Two:
I was wondering how to make this like iPhoto when it dawned on me I should use a UIPanGestureRecognizer instead.
I need to 'control' the animation with my finger in the way iPhoto does, namely by allowing me to start dragging to the next image and then reverse the drag direction. Also, in a similar way to iPhoto I'd like the next image to slide in if I release my finger when more of the next image is showing than the first image (this HASN'T happened in the transition picture above and if I released my finger at this point in iPhoto it would slide back to the first image.
I've never used a UIPanGestureRecognizer before so if anybody can help me out that'd be great.
EDIT:
Using the code provided by Levi, I have created a solution that works nicely with 3 sample images. For anybody else who is interested in using a UIPanGestureRecognizer, the code is:
Interface:
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UIGestureRecognizerDelegate>
{
int imageIndex;
}
@property (nonatomic, strong) UIImageView* imageView;
@property (nonatomic, strong) UIImageView* leftImageView;
@property (nonatomic, strong) UIImageView* rightImageView;
@end
Implementation:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self->imageIndex = 0;
float xFactor;
UIInterfaceOrientation currentOrientation = self.interfaceOrientation;
if(UIInterfaceOrientationIsLandscape(currentOrientation)){
xFactor = 256;
}
else{
xFactor = 0;
}
self.imageView = [[UIImageView alloc] initWithFrame:self.view.frame];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.image = [UIImage imageNamed:@"69B4356B-1CB2-4A2F-867E-9C086251DF11-12668-0000036A534E9B6D"];
self.imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
CGRect leftFrame = self.view.frame;
leftFrame.origin.x -= leftFrame.size.width+xFactor;
self.leftImageView = [[UIImageView alloc] initWithFrame:leftFrame];
self.leftImageView.contentMode = UIViewContentModeScaleAspectFit;
self.leftImageView.image = [UIImage imageNamed:@"42517D93-F8BF-42E7-BB44-53B099A482AA-12668-0000036A49BCECAA"];
self.leftImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
CGRect rightFrame = self.view.frame;
rightFrame.origin.x += rightFrame.size.width+xFactor;
self.rightImageView = [[UIImageView alloc] initWithFrame:rightFrame];
self.rightImageView.contentMode = UIViewContentModeScaleAspectFit;
self.rightImageView.image = [UIImage imageNamed:@"C6AC2508-243B-464B-A71F-96DD7F18673D-12668-00000369F3AFD3FC"];
self.rightImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.imageView];
[self.view addSubview:self.leftImageView];
[self.view addSubview:self.rightImageView];
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self.view addGestureRecognizer:recognizer];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGRect leftFrame = self.leftImageView.frame;
CGRect currentFrame = self.imageView.frame;
CGRect rightFrame = self.rightImageView.frame;
float duration = 0.0;
float factor;
UIInterfaceOrientation currentOrientation = self.interfaceOrientation;
if(UIInterfaceOrientationIsPortrait(currentOrientation)){
factor = 768;
}
else{
factor = 1024;
}
if (self.imageView.center.x < 0) { // Present the right image
duration = 0.3 * ABS(self.rightImageView.frame.origin.x / factor);
leftFrame.origin.x = -2 * factor;
currentFrame.origin.x = -1 * factor;
rightFrame.origin.x = 0;
self->imageIndex = 1;
} else if (self.imageView.center.x > factor) { // Present the left image
duration = 0.3 * ABS(self.leftImageView.frame.origin.x / factor);
leftFrame.origin.x = 0;
currentFrame.origin.x = factor;
rightFrame.origin.x = 2 * factor;
self->imageIndex = -1;
} else { // leave the middle image
duration = 0.3 * ABS(self.imageView.frame.origin.x / factor);
leftFrame.origin.x = -1 * factor;
currentFrame.origin.x = 0;
rightFrame.origin.x = factor;
self->imageIndex = 0;
}
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.leftImageView.frame = leftFrame;
self.imageView.frame = currentFrame;
self.rightImageView.frame = rightFrame;
} completion:^(BOOL finished) {
}];
} else {
CGPoint translation = [recognizer translationInView:recognizer.view];
CGPoint leftCenter = self.leftImageView.center;
CGPoint currentCenter = self.imageView.center;
CGPoint rightCenter = self.rightImageView.center;
leftCenter.x += translation.x;
currentCenter.x += translation.x;
rightCenter.x += translation.x;
self.leftImageView.center = leftCenter;
self.imageView.center = currentCenter;
self.rightImageView.center = rightCenter;
[recognizer setTranslation:CGPointMake(0, 0) inView:self.imageView];
}
}
- (void)willAnimateRotationToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
{
CGPoint leftCenter = self.leftImageView.center;
CGPoint currentCenter = self.imageView.center;
CGPoint rightCenter = self.rightImageView.center;
if(UIInterfaceOrientationIsPortrait(toInterfaceOrientation)){
leftCenter.x = 384+(((-self->imageIndex)-1)*768);
currentCenter.x = 384+((-self->imageIndex)*768);
rightCenter.x = 384+(((-self->imageIndex)+1)*768);
}
else{
leftCenter.x = 512+(((-self->imageIndex)-1)*1024);
currentCenter.x = 512+((-self->imageIndex)*1024);
rightCenter.x = 512+(((-self->imageIndex)+1)*1024);
}
self.leftImageView.center = leftCenter;
self.imageView.center = currentCenter;
self.rightImageView.center = rightCenter;
}
@end
You can add a UIPanGestureRecognizer
to your image view. You would have a method assigned to it where you do something like:
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
} else {
CGPoint translation = [recognizer translationInView:recognizer.view];
CGPoint center = recognizer.view.center;
UIImageView *imageViewToPresent = nil;
if (translation.x > 0) {
imageViewToPresent = [self leftImageView];
} else {
imageViewToPresent = [self leftImageView];
}
CGPoint actualCenter = recognizer.view.center;
CGPoint nextCenter = imageViewToPresent.center;
actualCenter.x += translation.x;
nextCenter.x += translation.x;
recognizer.view.center = actualCenter;
imageViewToPresent.view.center = nextCenter;
[recognizer setTranslation:CGPointMake(0, 0) inView:self.imageView];
}
}
In the UIGestureRecognizerStateEnded
part, you can decide which picture to show.
Or just use a ScrollView with paging enabled. It should have the same effect. However, it would require to have all the images loaded at the same time. Using Pan Recognizer, you need 3 image views tops (left, right, current).
Hope this helps!
EDIT:
In the header file you should add 2 more Image Views:
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UIGestureRecognizerDelegate>
@property (nonatomic, strong) UIImageView* imageView;
@property (nonatomic, strong) UIImageView* leftImageView;
@property (nonatomic, strong) UIImageView* rightImageView;
@end
The implementation would be:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView = [[UIImageView alloc] initWithFrame:self.view.frame];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.image = [UIImage imageNamed:@"69B4356B-1CB2-4A2F-867E-9C086251DF11-12668-0000036A534E9B6D"];
self.imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
CGRect leftFrame = self.view.frame;
leftFrame.origin.x -= leftFrame.size.width;
self.leftImageView = [[UIImageView alloc] initWithFrame:leftFrame];
self.leftImageView.contentMode = UIViewContentModeScaleAspectFit;
self.leftImageView.image = [UIImage imageNamed:@"42517D93-F8BF-42E7-BB44-53B099A482AA-12668-0000036A49BCECAA"];
self.leftImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
CGRect rightFrame = self.view.frame;
rightFrame.origin.x += rightFrame.size.width;
self.rightImageView = [[UIImageView alloc] initWithFrame:rightFrame];
self.rightImageView.contentMode = UIViewContentModeScaleAspectFit;
self.rightImageView.image = [UIImage imageNamed:@"C6AC2508-243B-464B-A71F-96DD7F18673D-12668-00000369F3AFD3FC"];
self.rightImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.imageView];
[self.view addSubview:self.leftImageView];
[self.view addSubview:self.rightImageView];
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self.view addGestureRecognizer:recognizer];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGRect leftFrame = self.leftImageView.frame;
CGRect currentFrame = self.imageView.frame;
CGRect rightFrame = self.rightImageView.frame;
float duration = 0.0;
if (self.imageView.center.x < 0) { // Present the right image
duration = 0.3 * ABS(self.rightImageView.frame.origin.x / self.view.frame.size.width);
leftFrame.origin.x = -2 * self.view.frame.size.width;
currentFrame.origin.x = -1 * self.view.frame.size.width;
rightFrame.origin.x = 0;
} else if (self.imageView.center.x > self.view.frame.size.width) { // Present the left image
duration = 0.3 * ABS(self.leftImageView.frame.origin.x / self.view.frame.size.width);
leftFrame.origin.x = 0;
currentFrame.origin.x = self.view.frame.size.width;
rightFrame.origin.x = 2 * self.view.frame.size.width;
} else { // leave the middle image
duration = 0.3 * ABS(self.imageView.frame.origin.x / self.view.frame.size.width);
leftFrame.origin.x = -1 * self.view.frame.size.width;
currentFrame.origin.x = 0;
rightFrame.origin.x = self.view.frame.size.width;
}
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.leftImageView.frame = leftFrame;
self.imageView.frame = currentFrame;
self.rightImageView.frame = rightFrame;
} completion:^(BOOL finished) {
}];
} else {
CGPoint translation = [recognizer translationInView:recognizer.view];
CGPoint currnetCenter = self.imageView.center;
CGPoint leftCenter = self.leftImageView.center;
CGPoint rightCenter = self.rightImageView.center;
currnetCenter.x += translation.x;
leftCenter.x += translation.x;
rightCenter.x += translation.x;
self.imageView.center = currnetCenter;
self.leftImageView.center = leftCenter;
self.rightImageView.center = rightCenter;
[recognizer setTranslation:CGPointMake(0, 0) inView:self.imageView];
}
}
@end
Of course it still need work (e.g. to support more than 3 hardcoded images). It may help you with the rotation issue if you would use IBOutlets, and set the auto resizing masks from the Interface Builder.
Have fun!