UIView Animation Cancel - ios

I want to make a UIView go off the screen upon a touch and come back after a small delay after touch up. So far:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if ([touch view] == myMainView)
{
[disappearingView.layer removeAllAnimations];
[UIView animateWithDuration:0.5
delay:0
usingSpringWithDamping:0.7
initialSpringVelocity:0.2
options:UIViewAnimationOptionBeginFromCurrentState
animations:^
{
disappearingView.transform = CGAffineTransformMakeScale(0, 0);
}
completion:^(BOOL finished)
{
}];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if ([touch view] == myMainView)
{
[UIView animateWithDuration:0.5
delay:1
usingSpringWithDamping:0.7
initialSpringVelocity:0.2
options:UIViewAnimationOptionBeginFromCurrentState
animations:^
{
disappearingView.transform = CGAffineTransformMakeScale(1, 1);
}
completion:^(BOOL finished)
{
}];
}
}
Above code works fine so far. However, if the user lift the finger up and touch down again before the 1 second delay expires, disappearing view still comes back, even with the touch down. If you touch down and up randomly, animation is very inconsistent.
I want view to come back only after 1 second and there are no touches at all.

Set a flag in touchesBegan and clear it again in touchesEnded. In your animation block for touchesEnded, check the flag and do not rescale to (1,1) if the flag is set. That way if you get a second touch after the first has ended but before the animation completes, it will not come back.

Related

MKMapView pin drag from another view

I'm thinking about a Google Map street view pin drag and drop system on iOS. The user selects a pin from another view (let's say UINavigationBar) and drag it to a position on map and then drop it.
I'm a bit lost with it. Do you have a workflow for this type of interaction ?
It is quite a complex task but it can be divided into a couple of simple steps.
Make a custom UIView, which after touching will follow the touch movement.
Example
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_originalPosition = self.view.center;
_touchOffset = CGPointMake(self.view.center.x-position.x,self.view.center.y-position.y);
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint position = [touch locationInView: self.view.superview];
[UIView animateWithDuration:.001
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
self.view.center = CGPointMake(position.x+_touchOffset.x, position.y+_touchOffset.y);
}
completion:^(BOOL finished) {}];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint positionInView = [touch locationInView:self.view];
CGPoint newPosition;
if (CGRectContainsPoint(_desiredView.frame, positionInView)) {
newPosition = positionInView;
// _desiredView is view where the user can drag the view
} else {
newPosition = _originalPosition;
// its outside the desired view so lets move the view back to start position
}
[UIView animateWithDuration:0.4
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
self.view.center = newPosition
// to
}
completion:^(BOOL finished) {}];
}
When user releases the finger, you will have to get the location of the touch.
Calculate the touch location in map view coordinates and place the pin.
Hope it points you to the right direction.

(iOS) Drag and drop, Snap to grid, Check proper area

I've read all the stuff about drag'n'drop and gestureRecognizer but haven't found the solution.
I got "gamefield" 10x10 and I want to implement drag'n'drop with snap to grid and check if element placed on "gamefield".
I got my 8 "TempButtons" added to UIView buttonsSuperView with action:#selector(imageMoved:withEvent:).
- (IBAction) imageMoved:(id) sender withEvent:(UIEvent *) event
{
UIControl *control = sender;
originalPosition = [self getButton:sender];
UITouch *touch = [[event allTouches] anyObject];
point = [touch locationInView:control];
float step = 31.0; // Grid step size.
CGPoint center = control.center;
center.x += step * floor((point.x / step));
center.y += step * floor((point.y / step));
control.center = center;
}
It works, I can move any button with grid step 31px. I can place it everywhere, but I have to put it on UIView fieldView. If it placed right there it's OK and I can replace it on another tile. But if I place it outside fieldView the button should return on its place. Something like this:
if ([self.fieldView pointInside:point withEvent:nil])
{
control.center = center;
} else {
[UIView animateWithDuration:0.4
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
control.center = originalPosition;
// to
}
completion:^(BOOL finished) {}];
}
If this code placed on - (IBAction) imageMoved:(id) sender withEvent:(UIEvent *) event method it makes button get back to its "originalposition" all the time its moving. But I need to check if its placed properly only on 'touchesEnded'. But that's another method and I don't understand how to realize which button pressed and its original coords to get back if placed improperly.
Maybe there's a better way to use 'UIView' instead of 'UIButton' but I still don't understand how to do that.
Thanks in advance.
Kind Regards,
Nick
Since no one answered my question I've spent some time and found working solution.
- (IBAction) imageMoved:(id) sender withEvent:(UIEvent *) event
{
dragObject = sender;
originalPosition = [self getButton:sender];
UITouch *touch = [[event allTouches] anyObject];
point = [touch locationInView:dragObject];
float step = 31.0; // Grid step size.
CGPoint center = dragObject.center;
center.x += step * floor((point.x / step));
center.y += step * floor((point.y / step));
dragObject.center = center;
[sender addTarget:self action:#selector(touchesEnded:withEvent:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (![fieldView pointInside:dragObject.center withEvent:nil])
{
[UIView animateWithDuration:0.4
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
dragObject.center = CGPointMake(originalPosition.x + 15.5, originalPosition.y + 15.5);
}
completion:^(BOOL finished) {}];
}
}
Now it works like charm and I can move along. My buttons moving freely snapped to grid and when pleced outside game field they move back to their original position.
Regards,
Nick

How to properly implement a draggable "drawer" custom UIView

I have a custom UIView at the bottom on my view-controller that acts as a "drawer" control. About 90 pixels of the view is visible. When a user taps the view, it animates upward revealing other options, controls, etc. After another tap, the view animates back down to its original position. Effectively "closing the drawer".
I've also implemented code so that the view tracks with a dragging gesture:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(self.frame.origin.y >= 80) {
UITouch *theTouch = [touches anyObject];
CGPoint location = [theTouch locationInView:self];
CGPoint previousLocation = [theTouch previousLocationInView:self];
CGFloat target = location.y - previousLocation.y;
self.frame = CGRectOffset(self.frame, 0, target);
}
}
All of this is working as expected. However, the view is not responding correctly to a quick "flick" gesture. In the touchesEnded:withEvent: method I'm animating the view from where the user lifted his/her finger to its destination. This works well if the user drags slowly then releases. But if they flick quickly, the view still has to process a 0.5 second duration which isn't smooth to the eye. Here's all of the custom view's code:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(self.frame.origin.y >= 80) {
UITouch *theTouch = [touches anyObject];
CGPoint location = [theTouch locationInView:self];
CGPoint previousLocation = [theTouch previousLocationInView:self];
CGFloat target = location.y - previousLocation.y;
self.frame = CGRectOffset(self.frame, 0, target);
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(self.frame.origin.y == kDefaultUpY) {
_comingFromUp = YES;
_comingFromDown = NO;
}
else if(self.frame.origin.y == kDefaultDownY) {
_comingFromUp = NO;
_comingFromDown = YES;
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
//responding to tap
if(self.frame.origin.y == kDefaultUpY) {
[UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
self.frame = CGRectMake(0, kDefaultDownY, self.frame.size.width, self.frame.size.height);
} completion:nil];
return;
}
else if(self.frame.origin.y == kDefaultDownY) {
[UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
self.frame = CGRectMake(0, kDefaultUpY, self.frame.size.width, self.frame.size.height);
} completion:nil];
return;
}
//responding to drag
if(_comingFromDown) {
if(self.frame.origin.y < kDefaultDownY) {
//dragging up -- go up
[UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
self.frame = CGRectMake(0, kDefaultUpY, self.frame.size.width, self.frame.size.height);
} completion:nil];
}
else if(self.frame.origin.y > kDefaultDownY) {
//dragging down -- stay down
[UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
self.frame = CGRectMake(0, kDefaultDownY, self.frame.size.width, self.frame.size.height);
} completion:nil];
}
}
else if(_comingFromUp) {
if(self.frame.origin.y < kDefaultUpY) {
//dragging up -- stay up
[UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
self.frame = CGRectMake(0, kDefaultUpY, self.frame.size.width, self.frame.size.height);
} completion:nil];
}
else if(self.frame.origin.y > kDefaultUpY) {
//dragging down -- go down
[UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
self.frame = CGRectMake(0, kDefaultDownY, self.frame.size.width, self.frame.size.height);
} completion:nil];
}
}
}
How can I properly respond to the gestures associated with a "drawer" view? Is there a standard way of achieving this kind of functionality?
Use gesture recognizers rather than implementing touch handling code yourself. You can attach both a swipe gesture recognizer and a pan gesture recognize, and set up the pan gesture so the swipe has to fail before the pan is triggered. Make the animation for the swipe gesture be faster. Something like 0.1 or 0.2 seconds is probably right for a swipe gesture.
Something to watch out for, though: In iOS 7, a swipe gesture from the bottom of the screen triggers the system settings drawer to come up from the bottom of the screen. Our app Face Dancer has a controls drawer, and we recently discovered that under iOS 7 its hard to slid our controls drawer up without triggering the system settings drawer instead. We have a "thumb" region for dragging, and we are adding a tap gesture that will fully display it/hide it in one step.

Move UIView left to right while they move down the screen

I am trying to create an iOS app with blocks floating down the screen(UIViews). I have them floating down the screen but I also want the user to be able to move them on the x axis as they are falling. I tried to do it with the code below but they just fall and don't move left to right. My problem is I am trying to move it with my finger left to right as it is already moving town the screen. How can I adapt the code below to work?
Note: I was able to move the views left to right without them moving down the screen and I was able to move them down the screen without moving them left to right. The problem arises when I combine both.
ANIMATION FOR Y AXIS
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:letView.speed];
[UIView setAnimationDelay:0.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
letView.layer.frame = CGRectMake(letView.layer.frame.origin.x, [[UIScreen mainScreen] bounds].size.height, letView.layer.frame.size.width, letView.layer.frame.size.height);
[UIView commitAnimations];
ANIMATION FOR TOUCH
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
//Goes through an array of views to see which one to move
for (LetterView * view in _viewArray) {
if (CGRectContainsPoint(view.frame, touchLocation)) {
dragging = YES;
currentDragView = view;
[currentDragView.layer removeAllAnimations];
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (dragging) {
CGPoint location = touchLocation;
currentDragView.center = CGPointMake(location.x, currentDragView.center.y);
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
dragging = NO;
[self checkForCorrectWord];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:currentDragView.speed];
[UIView setAnimationDelay:0.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
currentDragView
.layer.frame = CGRectMake(currentDragView.layer.frame.origin.x, [[UIScreen mainScreen] bounds].size.height, currentDragView.layer.frame.size.width, currentDragView.layer.frame.size.height);
[UIView commitAnimations];
}
I've got a demo project that shows you how to make a "boat" animate down the screen in an s-curve, like this:
The solution I use is to make a keyframe animation (actually it's a keyframe animation that forms part of a grouped animation, but you might not need the rest of the grouped animation: it is the keyframe animation that makes the curved path shape). Perhaps you could adapt what I'm doing, modifying it to your own purposes?
The code is available for download here:
https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch17p501groupedAnimation
It is discussed in detail in my book. Keyframe animations generally:
http://www.apeth.com/iOSBook/ch17.html#_keyframe_animation
This particular example:
http://www.apeth.com/iOSBook/ch17.html#_grouped_animations

How to move to the next screen when the image drops onto when I tap the button?

I wrote the animation concept in viewDidLoad method i wrote:
scaleFactor=2;
angle=180;
CGRect frame = CGRectMake(10,10,45,45);
UIView *box;
box=[[UIView alloc] initWithFrame:frame];
box.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"images673.png"];
[self.view addSubView:box];
// This code is for the touch event of an object in the screen
// For the object movement i wrote the method which is shown below:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch=[touches anyObject];
CGPoint location=[touch locationInView:self.view];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate:self];
[UICiew setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
box.center=location;
[UIView commitAnimations];
}
// And then,now i wrote a button which has to navigate to another class file// General navigation principle i wrote
-(IBAction)settings:(id)sender
{
aScreen *abc=[[aScreen alloc]init];
[self.navigationController pushViewController:abc animated:YES];
[abc release];
}
Now the object is moving where i tap on the screen,but when i tap on the button,the image is not placing on the top of the button.What to do.Please help me.I took an image and then I made some animation for it to move till where I tap in the screen. But at the same time, I took a button and I wrote a button event action in viewcontroller. When I tap the button, the image is not moving onto the button.
What I want to do is if I tap the button, the image has to drop onto the button and it should display the next view controller. Can anyone help me in doing so? How to move to the next screen when the image drops onto when I tap the button?
Im not sure i fully understand the problem without seeing your code but cant you do something like this?
-(ibaction)buttonevent:(id)sender{
cgrect frame = image.frame;
frame.origin.x = button.frame.x;
frame.origin.y = button.frame.y;
image.frame = frame;
image.alpha = 0;
[self pushviewcontroller:newViewcontroller animated:yes];
}
hi try this code i have checked. This is working for me when you check for button and the image you are dragging intersect then fire the method where it moves to next screen
// Handles the continuation of a touch.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
//NSLog(#"touchesMoved");
UITouch *touch = [touches anyObject];
if ([touch view] == mKey) {
NSUInteger touchCount = 0;
// Enumerates through all touch objects
for (UITouch *touch in touches) {
// Send to the dispatch method, which will make sure the appropriate subview is acted upon
[self dispatchTouchEvent:[touch view] toPosition:[touch locationInView:self.view]];
touchCount++;
}
}
}
// Checks to see which view, or views, the point is in and then sets the center of each moved view to the new postion.
// If views are directly on top of each other, they move together.
-(void)dispatchTouchEvent:(UIView *)theView toPosition:(CGPoint)position
{
// Check to see which view, or views, the point is in and then move to that position.
if (CGRectContainsPoint([mKey frame], position))
{
NSLog(#"touchesMoved");
mKey.center = position;
}
if(CGRectIntersectsRect([mKey frame], [inVisible frame]) && k==0)
{
self.view.userInteractionEnabled=NO;
NSLog(#"thirdPiew frame");
[inVisible setHighlighted:YES];
[inVisible setShowsTouchWhenHighlighted:YES];
k=1;
[self performSelector:#selector(modalPresentationButtonPressed:)];
}
}

Resources