How to add images while moving finger across UIView? - objective-c

I am having issues adding an image across a view while moving finger across the screen.
Currently it is adding the image multiple times but squeezing them too close together and not really following my touch.
EDIT:
What I want:
After taking or choosing an image, the user can then select another image from a list. I want the user to touch and move their finger across the view and the selected image will appear where they drag their finger, without overlapping, in each location.
This works:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
currentTouch = [touch locationInView:self.view];
CGRect myImageRect = CGRectMake(currentTouch.x, currentTouch.y, 80.0f, 80.0f);
myImage = [[UIImageView alloc] initWithFrame:myImageRect];
[myImage setImage:[UIImage imageNamed:#"dot.png"]];
[self.view addSubview:myImage];
[myImage release];
}
New Question: How do I add spaces in this so that the image isn't so snug when drawing it on the view?

You might want to explain your question more, what exactly are you trying to achieve! If you dont want the images to overlap than you can try this!
UITouch * touch = [touches anyObject];
touchPoint = [touch locationInView:imageView];
prev_touchPoint = [touch previousLocationInView:imageView];
if (ABS(touchPoint.x - prev_touchPoint.x) > 80
|| ABS(touchPoint.y - prev_touchPoint.y) > 80) {
_aImageView = [[UIImageView alloc] initWithImage:aImage];
_aImageView.multipleTouchEnabled = YES;
_aImageView.userInteractionEnabled = YES;
[_aImageView setFrame:CGRectMake(touchPoint.x, touchPoint.y, 80.0, 80.0)];
[imageView addSubview:_aImageView];
[_aImageView release];
}

I'm sorry because I'm in company and I can't post too big data(the code). The squeezing is because you have not checked the touch point's distance with last touch point. Check a point whether it's in a view:bool CGRectContainsPoint (CGRect rect,CGPoint point);I mean remember a touch point in touchesBegan:. Update it if the new touches in touchesMomved: is bigger than image's width or left. And put the add image view in a method and call it use - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg.

you can also use UISwipeGestureRecognizer as below instead of touchesMoved method,while swiping across the screen.in viewDidload:method,
UISwipeGestureRecognizer *swipeup = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipedone:)];
swipeup.direction = UISwipeGestureRecognizerDirectionUp;
swipeup.numberOfTouchesRequired=1;
[self.view addGestureRecognizer:swipeup];
method definition:
-(IBAction)swipedone:(UISwipeGestureRecognizer*)recognizer
{
NSLog(#"swiped");
UIImageView* _aImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"1.png"]];
_aImageView.frame = CGRectMake(10, 10, 100, 100);
_aImageView.multipleTouchEnabled = YES;
_aImageView.userInteractionEnabled = YES;
CGPoint point = [recognizer locationInView:recognizer.view];
[_aImageView setFrame:CGRectMake(point.x, point.y, 80.0, 80.0)];
[self.view addSubview:_aImageView];
[_aImageView release];
}
currently i am using this code for swipe up.i think it will works fine.once try it.

Related

How do I detect a touch event on a moving UIImageView?

When researching "How do I detect a touch event on a moving UIImageView?" I've come across several answers and I tried to implement them to my app. Nothing I've come across seems to work. I'll explain what I'm trying to do then post my code. Any thoughts, suggestions, comments or answers are appreciated!
My app has several cards floating across the screen from left to right. These cards are various colors and the object of the game is the drag the cards down to their similarly colored corresponding container. If the user doesn't touch and drag the cards fast enough, the cards will simply drift off the screen and points will be lost. The more cards contained in the correct containers, the better the score.
I've written code using core animation to have my cards float from the left to right. This works. However when attempting to touch a card and drag it toward it's container, it isn't correctly detecting that I'm touching the UIImageView of the card.
To test if my I'm properly implementing the code to move a card, I've also written some code allows movement for a non-moving card. In this case my touch is being detected and acting accordingly.
Why can I only interact with stationary cards? After researching this quite a bit it seems that the code:
options:UIViewAnimationOptionAllowUserInteraction
is the key ingredient to get my moving UIImages to be detected. However I tried this doesn't seem to have any effect.
I another key thing that I may be doing wrong is not properly utilizing the correct presentation layer. I've added code like this to my project and I also only works on non-moving objects:
UITouch *t = [touches anyObject];
UIView *myTouchedView = [t view];
CGPoint thePoint = [t locationInView:self.view];
if([_card.layer.presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Card!");
}
else{
NSLog(#"backgound touched");
}
After trying these types of things I'm getting stuck. Here is my code to understand this a bit more completely:
#import "RBViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface RBViewController ()
#property (nonatomic, strong) UIImageView *card;
#end
#implementation RBViewController
- (void)viewDidLoad
{
srand(time (NULL)); // will be used for random colors, drift speeds, and locations of cards
[super viewDidLoad];
[self setOutFirstCardSet]; // this sends out 4 floating cards across the screen
// the following creates a non-moving image that I can move.
_card = [[UIImageView alloc] initWithFrame:CGRectMake(400,400,100,100)];
_card.image = [UIImage imageNamed:#"goodguyPINK.png"];
_card.userInteractionEnabled = YES;
[self.view addSubview:_card];
}
the following method sends out cards from a random location on the left side of the screen and uses core animation to drift the card across the screen. Notice the color of the card and the speed of the drift will be randomly generated as well.
-(void) setOutFirstCardSet
{
for(int i=1; i < 5; i++) // sends out 4 shapes
{
CGRect cardFramei;
int startingLocation = rand() % 325;
CGRect cardOrigini = CGRectMake(-100,startingLocation + 37, 92, 87);
cardFramei.size = CGSizeMake(92, 87);
CGPoint origini;
origini.y = startingLocation + 37;
origini.x = 1200;
cardFramei.origin = origini;
_card.userInteractionEnabled = YES;
_card = [[UIImageView alloc] initWithFrame:cardOrigini];
int randomColor = rand() % 7;
if(randomColor == 0)
{
_card.image = [UIImage imageNamed:#"goodguy.png"];
}
else if (randomColor == 1)
{
_card.image = [UIImage imageNamed:#"goodguyPINK.png"];
}
else if (randomColor == 2)
{
_card.image = [UIImage imageNamed:#"goodGuyPURPLE.png"];
}
else if (randomColor == 3)
{
_card.image = [UIImage imageNamed:#"goodGuyORANGE.png"];
}
else if (randomColor == 4)
{
_card.image = [UIImage imageNamed:#"goodGuyLightPINK.png"];
}
else if (randomColor == 5)
{
_card.image = [UIImage imageNamed:#"goodGuyBLUE.png"];
}
else if (randomColor == 6)
{
_card.image = [UIImage imageNamed:#"goodGuyGREEN.png"];
}
_card.userInteractionEnabled = YES; // this is also written in my viewDidLoad method
[[_card.layer presentationLayer] hitTest:origini]; // not really sure what this does
[self.view addSubview:_card];
int randomSpeed = rand() % 20;
int randomDelay = rand() % 2;
[UIView animateWithDuration:randomSpeed + 10
delay: randomDelay + 4
options:UIViewAnimationOptionAllowUserInteraction // here is the method that I thought would allow me to interact with the moving cards. Not sure why I can't
animations: ^{
_card.frame = cardFramei;
}
completion:NULL];
}
}
notice the following method is where I put CALayer and hit test information. I'm not sure if I'm doing this correctly.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
UIView *myTouchedView = [t view];
CGPoint thePoint = [t locationInView:self.view];
thePoint = [self.view.layer convertPoint:thePoint toLayer:self.view.layer.superlayer];
CALayer *theLayer = [self.view.layer hitTest:thePoint];
if([_card.layer.presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Shape!"); // This only logs when I touch a non-moving shape
}
else{
NSLog(#"backgound touched"); // this logs when I touch the background or an moving shape.
}
if(myTouchedView == _card)
{
NSLog(#"Touched a card");
_boolHasCard = YES;
}
else
{
NSLog(#"Didn't touch a card");
_boolHasCard = NO;
}
}
I want the following method to work on moving shapes. It only works on non-moving shapes. Many answers say to have the touch ask which class the card is from. As of now all my cards on of the same class (the viewController class). When trying to have the cards be their own class, I was having trouble having that view appear on my main background controller. Must I have various cards be from different classes for this to work, or can I have it work without needing to do so?
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if([touch view]==self.card)
{
CGPoint location = [touch locationInView:self.view];
self.card.center=location;
}
}
This next method resets the movement of a card if the user starts moving it and then lifts up on it.
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(_boolHasCard == YES)
{
[UIView animateWithDuration:3
delay: 0
options:UIViewAnimationOptionAllowUserInteraction
animations: ^{
CGRect newCardOrigin = CGRectMake(1200,_card.center.y - 92/2, 92, 87);
_card.frame = newCardOrigin;
}
completion:NULL];
}
}
#end
The short answer is, you can't.
Core Animation does not actually move the objects along the animation path. They move the presentation layer of the object's layer.
The moment the animation begins, the system thinks the object is at it's destination.
There is no way around this if you want to use Core Animation.
You have a couple of choices.
You can set up a CADisplayLink on your view controller and roll your own animation, where you move the center of your views by a small amount on each call to the display link. This might lead to poor performance and jerky animation if you're animating a lot of objects however.
You can add a gesture recognizer to the parent view that contains all your animations, and then use layer hit testing on the paren't view's presentation view to figure out which animating layer got tapped, then fetch that layer's delegate, which will be the view you are animating. I have a project on github that shows how to do this second technique. It only detects taps on a single moving view, but it will show you the basics: Core Animation demo project on github.
(up-votes always appreciated if you find this post helpful)
It looks to me that your problem is really with just an incomplete understanding of how to convert a point between coordinate spaces. This code works exactly as expected:
- (void)viewDidLoad
{
[super viewDidLoad];
CGPoint endPoint = CGPointMake([[self view] bounds].size.width,
[[self view] bounds].size.height);
CABasicAnimation *animation = [CABasicAnimation
animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:[[_imageView layer] position]];
animation.toValue = [NSValue valueWithCGPoint:endPoint];
animation.duration = 30.0f;
[[_imageView layer] addAnimation:animation forKey:#"position"];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint thePoint = [t locationInView:self.view];
thePoint = [[_imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
if([[_imageView layer].presentationLayer hitTest:thePoint])
{
NSLog(#"You touched a Shape!");
}
else{
NSLog(#"backgound touched");
}
}
Notice the line in particular:
thePoint = [[_imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
When I tap on the layer image view while it's animating, I get "You touched a Shape!" in the console window and I get "background touched" when I tap around it. That's what you're wanting right?
Here's a sample project on Github
UPDATE
To help with your follow up question in the comments, I've written the touchesBegan code a little differently. Imagine that you've add all of your image views to an array (cleverly named imageViews) when you create them. You would alter your code to look something like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *t = [touches anyObject];
CGPoint thePoint = [t locationInView:self.view];
for (UIImageView *imageView in [self imageViews]) {
thePoint = [[imageView layer] convertPoint:thePoint
toLayer:[[self view] layer]];
if([[imageView layer].presentationLayer hitTest:thePoint]) {
NSLog(#"Found it!!");
break; // No need to keep iterating, we've found it
} else{
NSLog(#"Not this one!");
}
}
}
I'm not sure how expensive this is, so you may have to profile it, but it should do what you're expecting.

How can I get touch offset when using UITouch

How can I get touch offset when using UITouch? I am doing a project like a remote control. I have a view set to multi-touched and I want the view to act like a touch pad for Mac, so I need the touch offsets when people move to control the mouse. Any ideas?
This can be accomplished with a UIPanGestureRecognizer by measuring the diagonal distance from the center of the screen to the current touch location.
#import <QuartzCore/QuartzCore.h>
Declare the gesture and hook it to self.view so that the entire screen responds to touch events.
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(myPanRecognizerMethod:)];
[pan setDelegate:self];
[pan setMaximumNumberOfTouches:2];
[pan setMinimumNumberOfTouches:1];
[self.view setUserInteractionEnabled:YES];
[self.view addGestureRecognizer:pan];
Then in this method, we use the gesture recognizers state: UIGestureRecognizerStateChanged to update and integer that measures the diagonal distance between touch location and screen center as the touch location changes.
-(void)myPanRecognizerMethod:(id)sender
{
[[[(UITapGestureRecognizer*)sender view] layer] removeAllAnimations];
if ([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateChanged) {
CGPoint touchLocation = [sender locationOfTouch:0 inView:self.view];
NSNumber *distanceToTouchLocation = #(sqrtf(fabsf(powf(self.view.center.x - touchLocation.x, 2) + powf(self.view.center.y - touchLocation.y, 2))));
NSLog(#"Distance from center screen to touch location is == %#",distanceToTouchLocation);
}
}

How to add movable UIView as subview with dynamic size at runtime in objective C?

I need to add a UIView as subview of a UIView where a UIImageview is displayed. What I need to implement is that when the image is loaded in my UIIMageview, at the same time I am showing the UIView call it as newView. In the beginning,I am showing it with static size say 190X170 and alpha as 0.5. Once i tap my finger on that view,it should move on the whole UIImageview. Once it is moved at the last,that coordinated I am taking and cropping my image with the same points. Now I am done with the moving part using the code below.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(newView.frame, touchLocation))
{
dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
if (dragging)
{
newView.layer.borderColor = [UIColor clearColor].CGColor;
newView.layer.borderWidth = 1.0f;
CGRect frame = newView.frame;
frame.origin.x = newView.frame.origin.x + touchLocation.x - oldX;
x = frame.origin.x;
NSLog(#"x value : %d",x);
frame.origin.y = newView.frame.origin.y + touchLocation.y - oldY;
y = frame.origin.y;
NSLog(#"y value : %d",y);
newView.frame = frame;
}
oldX = touchLocation.x;
oldY = touchLocation.y;
}
What I need to implement is to resize the UIView on taping two fingers like that of UIScrollview. I tried to implement the UIScrollview but it is not giving me good effect. I tried to implement
NSUInteger resizingMask = NSViewHeightSizable | NSViewWidthSizable;
[self.view setAutoresizesSubviews:YES];
[newView setAutoresizingMask:resizingMask];
but in vain. Nothing is taking place.
Can anybody show me a path for the same. Thanks in advance.
To provide the zoom in zoom out feature you can use the UIPinchGuestureRecognizer. If you are using iOS 5 then it will be pretty easy for you.
here is the very cool tutorial for that hope it will help for you.
http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more

Identify specific image view from multiple imageviews

I have a single image view, and I set other image views on it and put them in different locations. I want to apply FLIP ANIMATION on mage view. How can do it? when if(i==1) then my imageview is flipped. img1 is my image view and I want to identify it. I allocate 52 images in `imagearr
My code:
-(void)setCards
{
int k=0;
CGFloat dx = self.view.frame.size.width/2;
CGFloat dy = self.view.frame.size.height/2;
int cnt = [imagearr count];
for (int j=1; j<3; j++) // Number of cards
{
for (int i=1; i<5; i++) // Number of players
{
int rnd = (arc4random() % cnt)-1;
UIImage *img = [imagearr objectAtIndex:rnd];
UIImageView *img1 = [[UIImageView alloc] initWithImage:img];
CGRect frame = img1.frame;
frame.size.width = frameX;
frame.size.height = frameY;
[img1 setFrame:frame];
[img1 setTag:k+i];
[UIView beginAnimations:#"Move" context:nil];
[UIView setAnimationDuration:0.50];
if (i==1)
{
[img1 setCenter:CGPointMake(dx+100, dy+(j*15))];
}
if (i==2)
{
[img1 setCenter:CGPointMake(dx+(j*15), dy+60)];
}
if (i==3)
{
[img1 setCenter:CGPointMake(dx-130, dy-(j*15))];
}
if (i==4)
{
[img1 setCenter:CGPointMake(dx-(j*20), dy-95)];
}
[self.view addSubview:img1];
[UIView commitAnimations];
[img1 release];
}
k=k+10;
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
int t = [touch view].tag;
}
I still can't identify a specific imageview. Please help me to solve this problem.
Setting tag for individual imageView is the best approach. Using tag you could even detect multiples images placed inside tableview cells and set action for selection of individual tag as well.
I think you should take a look at the Apple sample project GeekGameBoard. It implements a solitaire game and you will be able to learn how to select CALayers* representing cards, drag them around, and have them interact with each other.
*Which will be both easier and probably better from a performance perspective than using views.
Associate a unique tag values with each UIImageView instances.
Code for reference.
myImageView1.tag = 1;
myImageView1.tag = 2;
myImageView1.tag = 3;
Apple Link For the code:
Detect imageview from multiple imageview
Stack Link As Well
Here is the discussion for to detect the images from the multiple images
Good Luck
There will be different images in different imageView so each imageView will be on different position.
in your touch method do you have to check the bounds of the touch and then you have to match which imageView lies under your touch region.According to that you can proceed your functionality.
You can probably place a invisible UIbutton over your imageviews and capture touch event. The effect is the same and its much more cleaner and simpler. Let me know if you need more info. Would be happy to edit my answer here.
you could try something similar to this
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event //here enable the touch
{
// get touch event
UITouch *touch = [[event allTouches] anyObject];
int t = [touch view].tag;
UIImageView *imageView = (UIImageView *) [self.view viewWithTag:t];
//your logic here
}

Remove border for UIPopoverController

I have a requirement where in I have to display a custom border for UIPopoverController's popover view instead of the default "Black theme" border. Is it possible?
I cannot use the default black border because it doesnt suite the application's color theme.
There is no provision in the SDK to do this. I have also googled to see if someone else have faced this problem and if they have solved it, but with no luck!
Awaiting suggestions.
Thanks,
Raj
Solved this by using UIView and also by overriding the hitTest in the main rootViewController's view to see if the touch point is outside that view. If so, the event will be consumed to dismiss the new popover, otherwise the event will be forwarded to the new popover.
add popview as subview, code is:
//!you must define the dimBackgroundView and set view in head file firstly,
//action for a button,to add set view as a subview
- (IBAction)openSetting:(id)sender {
if(!dimBackgroundView)
{
dimBackgroundView = [[UIView alloc] initWithFrame:self.view.bounds];
}
dimBackgroundView.backgroundColor = [[UIColor clearColor] colorWithAlphaComponent:0.0];
[self.view addSubview:dimBackgroundView];
SettingViewController *set = [[SettingViewController alloc]initWithNibName:nil bundle:nil];
[set.view setFrame:CGRectMake(120, 50, 400, 600)];
self.setView = set;
//add shadow
set.view.layer.shadowOffset = CGSizeMake(3, 3);
set.view.layer.shadowColor = [UIColor colorWithRed:70.0/255.0 green:70.0/255.0 blue:80.0/255.0 alpha:1.0].CGColor;
set.view.layer.shadowOpacity = 0.8;
[self.view addSubview:set.view];
}
//check touch position, if touch position is outside of setview, remove it from superview
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event{
UITouch *touch = [[event allTouches] anyObject];
if ([self.setView.view superview] && self.dimBackgroundView == touch.view) {
[self.dimBackgroundView removeFromSuperview];
[self.setView.view removeFromSuperview];
}
}