move image following finger with fix x,y iOS7 - ios

How I can move image with fix x,y coordinate smooth (following user finger) in xcode 5.
I have make some code but it doesn't move following my finger gesture (image move instantly to new location)
here my .m
#import "ViewController.h"
#import "MyScene.h"
#interface ViewController()
#property (nonatomic, strong) UISwipeGestureRecognizer *leftSwipeGestureRecognizer;
#property (nonatomic, strong) UISwipeGestureRecognizer *rightSwipeGestureRecognizer;
#end
#implementation ViewController
- (IBAction)HandlePan:(UIPanGestureRecognizer *)recognizer{
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0,0) inView:self.view];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.leftSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipes:)];
self.rightSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipes:)];
self.leftSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
self.rightSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:self.leftSwipeGestureRecognizer];
[self.view addGestureRecognizer:self.rightSwipeGestureRecognizer];
}
- (void)handleSwipes:(UISwipeGestureRecognizer *)sender
{
if (sender.direction == UISwipeGestureRecognizerDirectionLeft)
{
CGPoint labelPosition = CGPointMake(self.pesawat.frame.origin.x - 30.0, self.pesawat.frame.origin.y);
self.pesawat.frame = CGRectMake( labelPosition.x , labelPosition.y , self.pesawat.frame.size.width, self.pesawat.frame.size.height);
}
if (sender.direction == UISwipeGestureRecognizerDirectionRight)
{
CGPoint labelPosition = CGPointMake(self.pesawat.frame.origin.x + 30.0, self.pesawat.frame.origin.y);
self.pesawat.frame = CGRectMake( labelPosition.x , labelPosition.y , self.pesawat.frame.size.width, self.pesawat.frame.size.height);
}
}
and here my .h
#interface ViewController : UIViewController
#property (nonatomic, retain) IBOutlet UIImageView *pesawat;
#end

As a very basic example,
You should use a panGestureRecognizer:
UIPanGestureRecognizer * panner = [[UIPanGestureRecognizer alloc]init];
[panner addTarget:self action:#selector(handlePan:)];
[self.view addGestureRecognizer:panner];
Then:
- (void) handlePan:(UIPanGestureRecognizer *)panner {
CGPoint translation = [panner translationInView:panner.view];
CGPoint newCenter = CGPointMake(self.pesawat.center.x + translation.x, self.pesawat.center.y);
int offset = 30; // pixels to offset
if (newCenter.x < offset) newCenter = CGPointMake(offset, newCenter.y);
else if (newCenter.x > self.view.bounds.size.width - offset) newCenter = CGPointMake(self.view.bounds.size.width - offset, newCenter.y);
self.pesawat.center = newCenter;
[panner setTranslation:CGPointMake(0, 0) inView:panner.view];
}

I suggest you to maintain the y position, change the x
basketView.center = CGPointMake(point.x, basketView.center.y);
Or I recommend you to follow these tutorials:
1) http://blog.spritebandits.com/2012/04/17/iphone-drawing-on-screen-follow-a-finger/
2) http://www.raywenderlich.com/44270/sprite-kit-tutorial-how-to-drag-and-drop-sprites
Hopefully this info will helps you.

I do this with a Long Press Gesture recognizer. Like this:
UILongPressGestureRecognizer *ges = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(lp:)];
ges.minimumPressDuration = 0.1;
self.pesawat.userInteractionEnabled = YES;
[self.pesawat addGestureRecognizer:ges];
Then add this:
-(void)lp:(UILongPressGestureRecognizer*)ges{
float x = ([ges locationInView:self.GraphicBox].x);
float y = ([ges locationInView:self.GraphicBox].y);
[imgView setCenter:CGPointMake(x,y)];
}
That should do the trick for you.

Related

iOS: Resize and Rotate UIView Concurrently

Using a UIPanGestureRecognizer in my view controller, I'm trying to draw a view (ArrowView) at an angle based upon the touch location. I'm trying to use CGAffineTransformRotate to rotate the view based up the angle between the first touch and the current touch, but this isn't working unless the view has already been drawn by at lease 20 or more pixels. Also, when drawing, the view doesn't always line up under my finger. Is this the correct approach for this situation? If not, does anyone recommend a better way of accomplishing this? If so, what am I doing wrong?
ViewController.m
#implementation ViewController {
ArrowView *_selectedArrowView;
UIColor *_selectedColor;
CGFloat _selectedWeight;
CGPoint _startPoint;
}
- (void)viewDidLoad {
[super viewDidLoad];
_selectedColor = [UIColor yellowColor];
_selectedWeight = 3;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panHandler:)];
[self.view addGestureRecognizer:pan];
}
- (void) panHandler: (UIPanGestureRecognizer *) sender {
if (sender.state == UIGestureRecognizerStateBegan) {
//Instantiate the arrow
CGPoint touchPoint = [sender locationInView:sender.view];
_startPoint = touchPoint;
_selectedArrowView = [[ArrowView alloc] initWithFrame:CGRectMake(touchPoint.x, touchPoint.y, 0, 25) withColor:_selectedColor withWeight:_selectedWeight];
_selectedArrowView.delegate = self;
[self.view addSubview:_selectedArrowView];
[self.view bringSubviewToFront:_selectedArrowView];
} else if (sender.state == UIGestureRecognizerStateChanged) {
//"Draw" the arrow based upon finger postion
CGPoint touchPoint = [sender locationInView:sender.view];
[_selectedArrowView drawArrow:_startPoint to:touchPoint];
}
}
#end
ArrowView.m
- (void) drawArrow: (CGPoint) startPoint to: (CGPoint) endPoint {
startPoint = [self convertPoint:startPoint fromView:self.superview];
endPoint = [self convertPoint:endPoint fromView:self.superview];
if (_initialAngle == -1000 /*Initially set to an arbitrary value so I know when the draw began*/) {
_initialAngle = atan2(startPoint.y - endPoint.y, startPoint.x - endPoint.x);
[self setPosition:0];
} else {
CGFloat ang = atan2(startPoint.y - endPoint.y, startPoint.x - endPoint.x);
ang -= _initialAngle;
self.transform = CGAffineTransformRotate(self.transform, ang);
CGFloat diff = (endPoint.x - self.bounds.size.width);
NSLog(#"\n\n diff: %f \n\n", diff);
self.bounds = CGRectMake(0, 0, self.bounds.size.width + diff, self.bounds.size.height);
_endPoint = CGPointMake(self.bounds.size.width, self.bounds.size.height);
[self setNeedsDisplay];
}
}
- (void) setPosition: (CGFloat) anchorPointX {
CGPoint layerLoc;
if (anchorPointX == 0) {
layerLoc = CGPointMake(self.layer.bounds.origin.x, self.layer.bounds.origin.y + (self.layer.bounds.size.height / 2));
} else {
layerLoc = CGPointMake(self.layer.bounds.origin.x + self.layer.bounds.size.width, self.layer.bounds.origin.y + (self.layer.bounds.size.height / 2));
}
CGPoint superLoc = [self convertPoint:layerLoc toView:self.superview];
self.layer.anchorPoint = CGPointMake(anchorPointX, 0.5);
self.layer.position = superLoc;
}
- (CGFloat) pToA: (CGPoint) touchPoint {
CGPoint start;
if (_dotButtonIndex == kDotButtonFirst) {
start = CGPointMake(CGRectGetMaxX(self.bounds), CGRectGetMaxY(self.bounds));
} else {
start = CGPointMake(CGRectGetMinX(self.bounds), CGRectGetMinY(self.bounds));
}
return atan2(start.y - touchPoint.y, start.x - touchPoint.x);
}
Link to project on GitHub: Project Link
Figured it out.
I had to make it have an initial width so the angle would work.
_initialAngle = atan2(startPoint.y - endPoint.y, startPoint.x - (endPoint.x + self.frame.size.width));

SpriteKit drag and drop through wall

I did simple drag and drop in SpriteKit with physics. It works as expected, but when I use fast drag, element goes through wall.
self.runner is wall
self.runner2 is square
Wall have dynamic set to NO.
Movie shows everything: https://www.dropbox.com/s/ozncf9i16o1z80o/spritekit_sample.mov?dl=0
Tested on simulator and real device, both iOS 7.
I want to prevent square from going through wall. Any ideas?
#import "MMMyScene.h"
static NSString * const kRunnerImg = #"wall.png";
static NSString * const kRunnerName = #"runner";
static NSString * const kRunnerImg2 = #"zebraRunner.png";
static NSString * const kRunnerName2 = #"runner";
static const int kRunnerCategory = 1;
static const int kRunner2Category = 2;
static const int kEdgeCategory = 3;
#interface MMMyScene () <SKPhysicsContactDelegate>
#property (nonatomic, weak) SKNode *draggedNode;
#property (nonatomic, strong) SKSpriteNode *runner;
#property (nonatomic, strong) SKSpriteNode *runner2;
#end
#implementation MMMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.runner = [SKSpriteNode spriteNodeWithImageNamed:kRunnerImg];
self.runner.texture = [SKTexture textureWithImageNamed:kRunnerImg];
[self.runner setName:kRunnerName];
[self.runner setPosition:CGPointMake(160, 300)];
[self.runner setSize:CGSizeMake(320, 75)];
[self addChild:self.runner];
self.runner.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(320, 75)];
self.runner.physicsBody.categoryBitMask = kRunnerCategory;
self.runner.physicsBody.contactTestBitMask = kRunner2Category;
self.runner.physicsBody.collisionBitMask = kRunner2Category;
self.runner.physicsBody.dynamic = NO;
self.runner.physicsBody.allowsRotation = NO;
self.runner2 = [SKSpriteNode spriteNodeWithImageNamed:kRunnerImg2];
[self.runner2 setName:kRunnerName2];
[self.runner2 setPosition:CGPointMake(100, 100)];
[self.runner2 setSize:CGSizeMake(75, 75)];
self.runner2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(75, 75)];
self.runner2.physicsBody.categoryBitMask = kRunner2Category;
self.runner2.physicsBody.contactTestBitMask = kRunnerCategory;
self.runner2.physicsBody.collisionBitMask = kRunnerCategory;
self.runner2.physicsBody.dynamic = YES;
self.runner2.physicsBody.allowsRotation = NO;
[self addChild:self.runner2];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody.categoryBitMask = kEdgeCategory;
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = 0;
self.physicsWorld.gravity = CGVectorMake(0,0);
self.physicsWorld.contactDelegate = self;
}
return self;
}
- (void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == kRunnerCategory )
{
NSLog(#"collision");
//self.draggedNode = nil;
}
}
- (void)didMoveToView:(SKView *)view {
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanFrom:)];
[[self view] addGestureRecognizer:gestureRecognizer];
}
- (void)panForTranslation:(CGPoint)translation {
CGPoint position = [self.draggedNode position];
if([[self.draggedNode name] isEqualToString:kRunnerName]) {
[self.draggedNode setPosition:CGPointMake(position.x + translation.x, position.y + translation.y)];
}
}
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
CGPoint touchLocation = [recognizer locationInView:recognizer.view];
touchLocation = [self convertPointFromView:touchLocation];
[self selectNodeForTouch:touchLocation];
}
else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:recognizer.view];
translation = CGPointMake(translation.x, -translation.y);
[self panForTranslation:translation];
[recognizer setTranslation:CGPointZero inView:recognizer.view];
}
}
- (void)selectNodeForTouch:(CGPoint)touchLocation {
SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation];
if(![self.draggedNode isEqual:touchedNode]) {
self.draggedNode = touchedNode;
// self.draggedNode.physicsBody.affectedByGravity = NO;
}
}
#end
You're setting the position of the node directly, this bypasses collision detection. If you move fast, the next position can be on the other side of the wall (or close enough so that physics will resolve the collision by moving the dragged node outside and above the wall).
Enabling usesPreciseCollisionDetection on the dynamic body may improve the situation but may not entirely prevent it. Actually it may not make any difference due to setting the node position directly instead of moving the body using force/impulse.
To fix this behavior you can't rely on contact events. Instead use bodyAlongRayStart:end: to determine if there's a blocking body between the node's current and the next position. Even so this will only work with your current setup, but couldn't prevent the ray successfully passing through small gaps in the wall where the dynamic body wouldn't be able to fit through.
While dragging nodes, if you change their position in the following ways,
draggedNode.position = CGPoint(x: position.x + translation.x, y: position.y + translation.y)
and
SKAction.move(to: CGPoint(x: 100, y: sprite.position.y), duration: 1))
nodes will lose their physics properties. That's why they pass through walls or other nodes. Therefore you need to change their velocity or apply force or impulse.
I created an example project in Github. And the video is here

How To apply UIGestureRecognizerDelegate On UIView Instead of UIViewController

Hi all I am doing pinch zoom & pan on UIImageView which is inside a UIView for this I have written code which is working fine . My problem is when i am rotation OR pinching the image,it's not rotating OR panning from centre part of images. If i am doing same code on UIViewController this code is working great for Rotation & panning also .Please help me what i have mistaken here
in GestureRecognizer_UIView.h file :
#import <UIKit/UIKit.h>
#interface GestureRecognizer_UIView : UIView<UIGestureRecognizerDelegate>
#property (weak, nonatomic) IBOutlet UIImageView *imageView; #property CGFloat lastRotation;
#end
In GestureRecognizer_UIView.m file
#import "GestureRecognizer_UIView.h"
#implementation GestureRecognizer_UIView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
}
return self;
}
-(void)awakeFromNib
{
UIRotationGestureRecognizer *rotationG = [[UIRotationGestureRecognizer alloc]
initWithTarget:self action:#selector(rotationImage:)];
rotationG.delegate = self;
UIPinchGestureRecognizer *pinchGestureRecongnizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(changeImage:)];
pinchGestureRecongnizer.delegate = self;
UIPanGestureRecognizer *panGesture=[[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(panpan:)];
panGesture.delegate = self;
[_imageView addGestureRecognizer:pinchGestureRecongnizer];
[_imageView addGestureRecognizer:rotationG];
[_imageView addGestureRecognizer:panGesture];
}
- (void)panpan:(UIPanGestureRecognizer *)sender {
[self bringSubviewToFront:_imageView];
CGPoint translation = [sender translationInView:self];
CGPoint imageViewPosition = _imageView.center;
imageViewPosition.x += translation.x;
imageViewPosition.y += translation.y;
_imageView.center = imageViewPosition;
[sender setTranslation:CGPointZero inView:self];
}
-(void)rotationImage:(UIRotationGestureRecognizer*)gesture {
[self bringSubviewToFront:gesture.view];
CGPoint location = [gesture locationInView:self];
gesture.view.center = CGPointMake(location.x, location.y);
if ([gesture state] == UIGestureRecognizerStateEnded) {
self.lastRotation = 0;
return;
}
CGAffineTransform currentTransform = _imageView.transform;
CGFloat rotation = 0.0 - (self.lastRotation - gesture.rotation);
CGAffineTransform newTransform = CGAffineTransformRotate(currentTransform, rotation);
_imageView.transform = newTransform;
self.lastRotation = gesture.rotation;
}
- (void)changeImage:(UIPinchGestureRecognizer*)pinchGestureRecognizer {
[self bringSubviewToFront:pinchGestureRecognizer.view];
CGPoint location = [pinchGestureRecognizer locationInView:self];
pinchGestureRecognizer.view.center = CGPointMake(location.x, location.y);
pinchGestureRecognizer.view.transform = CGAffineTransformScale(pinchGestureRecognizer.view.transform, pinchGestureRecognizer.scale, pinchGestureRecognizer.scale);
pinchGestureRecognizer.scale = 1;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
#end

Pan gesture interferes with scroll

I have three view controllers that are part of a UIScrollView. I want to be able to swipe between the three, although one of the view controllers has a UIPanGestureRecognizer. I use this pan gesture recognizer to allow the user to drag their finger up and down to increase and decrease the height of a rectangular UIView. Therefore, this UIPanGestureRecognizer only really needs to know about the upwards/downwards panning, and the scroll view can use the horizontal panning.
An example of this, is like the home screen; you can swipe left or right, but also swipe down to get spotlight. I want this kind of mechanism.
This is my code for the pan:
- (void)pan:(UIPanGestureRecognizer *)aPan; // When pan guesture is recognised
{
CGPoint location = [aPan locationInView:self.view]; // Location of finger on screen
CGRect secondRect = CGRectMake(210.0, 45.0, 70.0, 325.0); // Rectangles of maximimum bar area
CGRect minuteRect = CGRectMake(125.0, 45.0, 70.0, 325.0);
CGRect hourRect = CGRectMake(41.0, 45.0, 70.0, 325.0);
if (CGRectContainsPoint(secondRect, location)) { // If finger is inside the 'second' rectangle
CGPoint currentPoint = [aPan locationInView:self.view];
currentPoint.y -= 80; // Make sure animation doesn't go outside the bars' rectangle
if (currentPoint.y < 0) {
currentPoint.y = 0;
}
else if (currentPoint.y > 239) {
currentPoint.y = 239;
}
currentPoint.y = 239.0 - currentPoint.y;
CGFloat pointy = currentPoint.y - fmod(currentPoint.y, 4.0);
[UIView animateWithDuration:0.01f // Animate the bars to rise as the finger moves up and down
animations:^{
CGRect oldFrame = secondBar.frame;
secondBar.frame = CGRectMake(oldFrame.origin.x, (oldFrame.origin.y - (pointy - secondBar.frame.size.height)), oldFrame.size.width, (pointy));
}];
CGFloat result = secondBar.frame.size.height - fmod(secondBar.frame.size.height, 4.0);
secondInt = (result / 4.0); // Update labels with new time
self->secondLabel.text = [NSString stringWithFormat:#"%02d", secondInt];
}
The code is basically repeated for three separate rectangular UIViews.
If anyone can tell me how to get the homescreen-style panning/swiping into my app, that would be great!!
Alright, here is the short answer:
You have to use UIGestureRecognizer's method -requireGestureRecognizerToFail:.
And here is the long answer:
You have to make the pan gesture recognizer of your scroll view to succeed only if the pan gesture recognizer of TimerViewController fails. However that gesture (TimerViewController's gesture) should only succeed if the initial movement is vertical. If it is horizontal it should fail.
To accomplish this we have to subclass UIPanGestureRecognizer and modify it to fit those needs.
Here is what you have to do:
Disregard ALL the changes you made from my previous answer
Add VerticalPanGestureRecognizer to your project.
Modify TimerViewController as shown.
Modify ScrollViewController as shown.
VerticalPanGestureRecognizer.h
#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface VerticalPanGestureRecognizer : UIPanGestureRecognizer
#end
VerticalPanGestureRecognizer.m
#import "VerticalPanGestureRecognizer.h"
#interface VerticalPanGestureRecognizer ()
#property (nonatomic, assign) CGPoint origLoc;
#end
#implementation VerticalPanGestureRecognizer
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.origLoc = [[touches anyObject] locationInView:self.view.superview];
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.state == UIGestureRecognizerStatePossible) {
CGPoint loc = [[touches anyObject] locationInView:self.view.superview];
CGFloat deltaX = fabs(loc.x - self.origLoc.x);
CGFloat deltaY = fabs(loc.y - self.origLoc.y);
if (deltaY < deltaX)
self.state = UIGestureRecognizerStateFailed;
}
[super touchesMoved:touches withEvent:event];
}
#end
TimerViewController.h
// Your imports here
#interface TimerViewController : UIViewController
{
// Your ivars here
}
// Add the following property
#property (nonatomic, strong) UIPanGestureRecognizer *pan;
// Your methods here
#end
TimerViewController.m
#import "TimerViewController.h"
#import "VerticalPanGestureRecognizer.h"
#implementation TimerViewController
#synthesize pan = _pan;
// prefersStatusBarHidden method here
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil // Initialise view controller
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Instantiate the pan gesture as "VerticalPanGestureRecognizer"
self.pan = [[VerticalPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)]; // Create recogniser for a pan guesture
self.pan.maximumNumberOfTouches = self.pan.minimumNumberOfTouches = 1;
[self.view addGestureRecognizer:self.pan];
}
return self;
}
// The rest of your code here
#end
ScrollViewController.m
- (void)viewDidLoad
{
// Your code here
TimerViewController *tvc = [[TimerViewController alloc]init];
CGRect frame = tvc.view.frame;
frame.origin.x = 320;
tvc.view.frame = frame;
// Add the following line
[self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:tvc.pan];
[self addChildViewController:tvc];
[self.scrollView addSubview:tvc.view];
[tvc didMoveToParentViewController:self];
// More code here
}
This new approach works perfectly. I tested it.
Let me know if you have more questions.
Cheers!
UPDATE
To answer the question you posted on the comments, here is what you have to do:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
BarsViewController *bvc = [[BarsViewController alloc]init];
[self addChildViewController:bvc];
[self.scrollView addSubview:bvc.view];
[bvc didMoveToParentViewController:self];
TimerViewController *tvc = [[TimerViewController alloc]init];
CGRect frame = tvc.view.frame;
frame.origin.x = 320;
tvc.view.frame = frame;
[self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:tvc.pan];
[self addChildViewController:tvc];
[self.scrollView addSubview:tvc.view];
[tvc didMoveToParentViewController:self];
StopwatchViewController *svc = [[StopwatchViewController alloc] init];
frame = svc.view.frame;
frame.origin.x = 320*2;
svc.view.frame = frame;
[self addChildViewController:svc];
[self.scrollView addSubview:svc.view];
[svc didMoveToParentViewController:self];
self.scrollView.contentSize = CGSizeMake(320*3, self.view.frame.size.height);
self.scrollView.pagingEnabled = YES;
[self.scrollView setShowsHorizontalScrollIndicator:NO];
}
Again, I tested it and it's working. You just have to add the gesture recognizer for the bars

'Adapt' my current UISwipeGestureRecognizer code for flicking through images to an iPhoto style utilising UIPanGestureRecognizer

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!
If you want a scrolling behaviour like the Photos application you shouldn't use a UIGestureRecognizer at all. Use a UIPageViewController with UIPageViewControllerTransitionStyleScroll, or if you need to support versions lower than iOS6, use a UIScrollView.
Check out Apple's sample project PhotoScroller for an example using UIScrollView.

Resources