Selecting images and then moving them - iphone

I am trying to figure out how to select an object first then I am able to move it. For example I want to select cow first by touching it then I am able to move it. The reason being is when I am touching the screen it is moving both cow and cow1. When I only want to move one cow at a time. Any help will be appreciated.
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
cow.center = CGPointMake(touchLocation.x, touchLocation.y);
cow1.center = CGPointMake(touchLocation.x, touchLocation.y);
}

Try using UIPanGestureRecognizer, like this:
#synthesize myImage; //UIImageView
- (void)viewDidLoad
{
[super viewDidLoad];
[self startMoveImage];
}
-(void) startMoveImage{
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
[self.view addGestureRecognizer:panGestureRecognizer];
}
- (void)pan:(UIPanGestureRecognizer *)gesture
{
if ((gesture.state == UIGestureRecognizerStateChanged) ||
(gesture.state == UIGestureRecognizerStateEnded)) {
CGPoint position = [gesture locationInView:[myImage superview]];
[myImage setCenter:position];
}
}

There are many ways to implement this sort of thing. It depends on how your graphics are implemented, amongst other things, check out this Apple sample code:
http://developer.apple.com/library/ios/#samplecode/Touches/Introduction/Intro.html

The standard solution would be to add separate gesture recognizers for your two cow objects, e.g., in viewDidLoad:
UIPanGestureRecognizer *panCow1 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveCow:)];
[cow1 addGestureRecognizer:panCow1];
// if non ARC, make sure to add a line that says
// [panCow1 release];
UIPanGestureRecognizer *panCow2 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveCow:)];
[cow2 addGestureRecognizer:panCow2];
// if non ARC, make sure to add a line that says
// [panCow2 release];
And then you have your moveCow method look something like:
- (void)moveCow:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateChanged)
{
CGPoint translate = [sender translationInView:self.view];
sender.view.center = CGPointMake(sender.view.center.x + translate.x, sender.view.center.y + translate.y);
}
}
I haven't tested this code, but you get the idea. This is how I generally move separate subviews around...

Related

Touch events (touchesMoved) not working on UIViews inside UIScrollView

I have a UIView inside a UIScrollView, and the UIViewControllers for those views are not receiving the touch events. If I take the views out of the scroll view then it works.
UserInteraction is default ON of all views but it's still not working!
This must be possible and I'd be really grateful if someone could point me in the right direction!
Many Thanks,
You can use the following method
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[yourView addGestureRecognizer:panRecognizer];
And to handle it
-(void)move:(id)sender {
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateChanged){
// This will return you location in view
CGPoint currentPoint = [sender locationInView:self.view];
// This will return you location in Scrollview
CGPoint scrollPoint = [sender locationInView:[[sender view] superview]];
}
}
add the following code to implementation file then touches moved
#interface UIScrollView(Custom)
#end
#implementation UIScrollView(Custom)
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
}
#end

Detecting 2 swipes without interrupting touch

I am trying to implement game control that will be changes direction of unit
movement. So if i do right swipe it turns to the right, if i do down swipe it turns to the down direction etc.
It's cocos2d game, and i'am using CCNode+SFGestureRecognizers with UISwipeGestureRecognizer.
At now I have next implementation for it
UISwipeGestureRecognizer *rightSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleRightSwipe:)];
[self addGestureRecognizer:rightSwipeGestureRecognizer];
rightSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
rightSwipeGestureRecognizer.delegate = self;
[rightSwipeGestureRecognizer release];
UISwipeGestureRecognizer *upSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleUpSwipe:)];
[self addGestureRecognizer:upSwipeGestureRecognizer];
upSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
upSwipeGestureRecognizer.delegate = self;
[upSwipeGestureRecognizer release];
UISwipeGestureRecognizer *leftSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleLeftSwipe:)];
[self addGestureRecognizer:leftSwipeGestureRecognizer];
leftSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
leftSwipeGestureRecognizer.delegate = self;
[leftSwipeGestureRecognizer release];
UISwipeGestureRecognizer *downSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleDownSwipe:)];
[self addGestureRecognizer:downSwipeGestureRecognizer];
downSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
downSwipeGestureRecognizer.delegate = self;
[downSwipeGestureRecognizer release];
but the problem is that for to recognize the following gesture you need to lift your finger from the screen. if you do not lift a finger after the first swipe, it will recognize only the first swipe.
at now:
How it should be:
What the best way to do that? Thanks!
cancelsTouchesInView should help, it prevents the cancellation of touch events when a gesture recognizer has recognized a gesture. This will allow other gesture recognizers to continue checking for their gesture.
rightSwipeGestureRecognizer.cancelsTouchesInView = NO;
upSwipeGestureRecognizer.cancelsTouchesInView = NO;
leftSwipeGestureRecognizer.cancelsTouchesInView = NO;
downSwipeGestureRecognizer.cancelsTouchesInView = NO;
Ok, there is my solution. I dont think that is really good solution, but in my case it works, because i need getting direction at every time interval.
So, in my CCLayer subclass:
#define SWIPE_LENGTH 60
#define SWIPE_TANGENT 2
...
#property(nonatomic, assign) CGPoint latestLocation;
...
-(id) init
{
if( (self=[super init]) )
{
self.isTouchEnabled = YES;
}
return self;
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
self.latestLocation = location;
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
CGPoint delataLocation = CGPointMake(location.x - self.latestLocation.x, location.y - self.latestLocation.y);
if ( sqrt(pow((delataLocation.x), 2) + pow((delataLocation.y), 2) ) > SWIPE_LENGTH ) {
if (delataLocation.y > 0 && delataLocation.y > SWIPE_TANGENT * ABS(delataLocation.x))
{
[self handleSwipeWithDirestion:SDirectionTypeUp];
} else if (delataLocation.y < 0 && ABS(delataLocation.y) > SWIPE_TANGENT * ABS(delataLocation.x))
{
[self handleSwipeWithDirestion:SDirectionTypeDown];
} else if (delataLocation.x > 0 && delataLocation.x > SWIPE_TANGENT * ABS(delataLocation.y))
{
[self handleSwipeWithDirestion:SDirectionTypeRight];
} else if (delataLocation.x < 0 && ABS(delataLocation.x) > SWIPE_TANGENT * ABS(delataLocation.y))
{
[self handleSwipeWithDirestion:SDirectionTypeLeft];
}
self.latestLocation = location;
}
}

How would I drag UITextView around the view screen?

I have a project where a UITextView (for multilines) can be dragged around the screen. So far my solution to this has been an overlay of an invisible UIButton which when dragged its center is the same as the UITextView's center.
However I've seen apps that seem to just allow the UITextView to be dragged and edited on the fly so it seems there might not be an overlay in those but I'm not sure.
Thoughts?
By the way, c in this code is the UIButton and this is how I have moved it thus far:
- (void) draggedOut: (UIControl *) c withEvent: (UIEvent *) ev
{
if(self.interfaceOrientation == UIInterfaceOrientationPortrait)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
}
- (void)panTextView:(UIPanGestureRecognizer *)recognizer {
NSLog(#"panning");
location1 = [recognizer translationInView:draggableTextView];
recognizer.view.center = CGPointMake(recognizer.view.center.x + location1.x,
recognizer.view.center.y + location1.y);
[recognizer setTranslation:CGPointMake(0,0) inView:draggableTextView];
location1 =[recognizer locationInView:draggableTextView];
NSLog(#"tranlation %#",NSStringFromCGPoint(location1));
[_imgpic addSubview:recognizer.view];
appDelegate.txt=draggableTextView.text;
}
call this method after creating textview.
Well have not been able to manipulate the actual uitextview.
First tried making a button overlay that could be moved and could be pressed to start editing, but it wasn't centered properly.
Then tried the above method to move the UITextView itself. But it would only work on touches or drags. (Note this was a modified form of touchesBegan & touchesMoved)
Ended up with a UIScrollView with the UITextView as a subview. Now it can move smoothly just that it can be moved from any place on the screen. Not optimal but is the best result to thus keep everything else intact.
Does the textView need to support scrolling? If so, this could get complicated.
But if not, there are two approaches. 1) subclass the textview and override touchesBegan, touchesMoved, touchesEnded. 2) write a gesture recognizer that processes the same messages and attach it to the textview.
Here's an example of a Gesture recognizer that will do the job:
#interface TouchMoveGestureRecognizer : UIGestureRecognizer
{
CGPoint _ptOffset;
}
#end
#implementation TouchMoveGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* t = [touches anyObject];
_ptOffset = [t locationInView: self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* t = [touches anyObject];
CGPoint pt = [t locationInView: self.view.superview];
pt.x -= _ptOffset.x;
pt.y -= _ptOffset.y;
CGRect r = self.view.frame;
r.origin = pt;
self.view.frame = r;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_ptOffset = CGPointMake(-1, -1);
}
#end
and, how to use it:
- (void)viewDidLoad {
[super viewDidLoad];
_textView.scrollEnabled = NO;
TouchMoveGestureRecognizer* gr = [[[TouchMoveGestureRecognizer alloc] init] autorelease];
[_textView addGestureRecognizer: gr];
}

iPhone:Programming UISlider to position at clicked location

How to set the slider to clicked position and get the slider value on the clicked location on UISlider in iPhone programming.
i know we can drag the slider to that position but i dont want to do it. Can you please tel me how to set the slider to clicked position? Is this possible to do?
Here is the part "left as a user exercise":
- (void) tapped: (UITapGestureRecognizer*) g {
UISlider* s = (UISlider*)g.view;
if (s.highlighted)
return; // tap on thumb, let slider deal with it
CGPoint pt = [g locationInView: s];
CGFloat percentage = pt.x / s.bounds.size.width;
CGFloat delta = percentage * (s.maximumValue - s.minimumValue);
CGFloat value = s.minimumValue + delta;
[s setValue:value animated:YES];
}
The way I did it is to subclass the slider and check in touchesBegan. If the user taps on the thumb button area (which we track) then ignore the tap, but any where else on the trackbar we do:
:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
// if we didn't tap on the thumb button then we set the value based on tap location
if (!CGRectContainsPoint(lastKnownThumbRect, touchLocation)) {
self.value = self.minimumValue + (self.maximumValue - self.minimumValue) * (touchLocation.x / self.frame.size.width);
}
[super touchesBegan:touches withEvent:event];
}
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
CGRect thumbRect = [super thumbRectForBounds:bounds trackRect:rect value:value];
lastKnownThumbRect = thumbRect;
return thumbRect;
}
You can simply add a UITapGestureRecognizer to the slider, then pull out the UIEvent and Touches associated with it to figure out where along the UISlider the tap took place. Then set your slider's value to this calculated value.
UPDATE:
First, setup your slider and add the gesture recognizer to it.
UISlider *slider = [[[UISlider alloc] init] autorelease];
…
<slider setup>
…
UITapGestureRecognizer *gr = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(sliderTapped:)] autorelease];
[slider addGestureRecognizer:gr];
Then implement the selector
- (void)sliderTapped:(UIGestureRecognizer *)gestureRecognizer {
<left as a user excercise*>
}
*Hint: Read the docs to figure out how to get the locationInView extrapolated and figure out what the slider should be
It seems like just subclassing UISlider and returning always true to the beginTracking produce the desired effect.
iOS 10 and Swift 3
class CustomSlider: UISlider {
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
return true
}
}
I was using the subclassed UISlider code to listen to ValueChanged events in ios6> in order to implement a snap-to-grid type of function.
This was broken when upgrading to iOS7 as it no longer fired the UIControlEventsValueChanged. For anyone having the same problem it is fixed by adding a line in the if() as below:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
// if we didn't tap on the thumb button then we set the value based on tap location
if (!CGRectContainsPoint(lastKnownThumbRect, touchLocation)) {
self.value = self.minimumValue + (self.maximumValue - self.minimumValue) * (touchLocation.x / self.frame.size.width);
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
[super touchesBegan:touches withEvent:event];
}
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
CGRect thumbRect = [super thumbRectForBounds:bounds trackRect:rect value:value];
lastKnownThumbRect = thumbRect;
return thumbRect;
}
Hope it helps someone :)
I have used this code. Get from: http://imagineric.ericd.net/2012/11/15/uislider-touch-to-set-value/
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(sliderTapped:)];
[slider addGestureRecognizer:gr];
- (void)sliderTapped:(UIGestureRecognizer *)g {
UISlider* s = (UISlider*)g.view;
if (s.highlighted)
return; // tap on thumb, let slider deal with it
CGPoint pt = [g locationInView: s];
CGFloat percentage = pt.x / s.bounds.size.width;
CGFloat delta = percentage * (s.maximumValue - s.minimumValue);
CGFloat value = s.minimumValue + delta;
[s setValue:value animated:YES];
}
Hope this can help you.

UIImage detecting touch and dragging

Fairly common question this, to which I have a few answers and I'm nearly there. I have a button which when pressed, will create an image (code as follows)
(numImages is set on load to ZERO and is used as a count up for the tag numbers of all images created)
UIImage *tmpImage = [[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", sender.tag]] retain];
UIImageView *myImage = [[UIImageView alloc] initWithImage:tmpImage];
numImages += 1;
myImage.userInteractionEnabled = YES;
myImage.tag = numImages;
myImage.opaque = YES;
[self.view addSubview:myImage];
[myImage release];
I then have a touchesBegan method which will detect what's touched. What I need it to do is to allow the user to drag the newly created image. It's nearly working, but the image flickers all over the place when you drag it. I can access the image which you click on as I can get it's TAG, but I just cannot drag it nicely.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
if (touch.view.tag > 0) {
touch.view.center = location;
}
NSLog(#"tag=%#", [NSString stringWithFormat:#"%i", touch.view.tag]);
}
- (void) touchesMoved:(NSSet *)touches withEvent: (UIEvent *)event {
[self touchesBegan:touches withEvent:event];
}
It works, in that I get an output of the tag for each image as I click on them. But when I drag, it flashes... any ideas?
In answer to my own question - I decided to create a class for handling the images I place on the view.
Code if anyone's interested....
Draggable.h
#import <Foundation/Foundation.h>
#interface Draggable : UIImageView {
CGPoint startLocation;
}
#end
Draggable.m
#import "Draggable.h"
#implementation Draggable
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
// Retrieve the touch point
CGPoint pt = [[touches anyObject] locationInView:self];
startLocation = pt;
[[self superview] bringSubviewToFront:self];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
// Move relative to the original touch point
CGPoint pt = [[touches anyObject] locationInView:self];
CGRect frame = [self frame];
frame.origin.x += pt.x - startLocation.x;
frame.origin.y += pt.y - startLocation.y;
[self setFrame:frame];
}
#end
and to call it
UIImage *tmpImage = [[UIImage imageNamed:"test.png"] retain];
UIImageView *imageView = [[UIImageView alloc] initWithImage:tmpImage];
CGRect cellRectangle;
cellRectangle = CGRectMake(0,0,tmpImage.size.width ,tmpImage.size.height );
UIImageView *dragger = [[Draggable alloc] initWithFrame:cellRectangle];
[dragger setImage:tmpImage];
[dragger setUserInteractionEnabled:YES];
[self.view addSubview:dragger];
[imageView release];
[tmpImage release];
Usually you get an implicit animation when you change center. Are you messing with -contentMode or calling -setNeedsDisplay by any chance?
You can explicitly request animation to avoid the delete and re-draw this way:
if (touch.view.tag > 0) {
[UIView beginAnimations:#"viewMove" context:touch.view];
touch.view.center = location;
[UIView commitAnimations];
}
Do note that NSLog() can be very slow (much slower than you'd expect; it's much more complicated than a simple printf), and that can cause trouble in something called as often as touchesMoved:withEvent:.
BTW, you're leaking tmpImage.