Changing instance variable bugs animation in UIView - ios

I am having a problem while trying to make a custom "sliding" object from scratch. I have a view self.taskView which should either stick to the left or stick to the right. This is working fine until I try to change the state of an instance variable called self.task.
This is a (webm) animation of it working "correctly": working.webm
But this only works when I do NOT change the instance variable self.task.state
This is a (webm) animation of it NOT working "correctly": not_working.webm
So I know that it is the code that changes the state of the task that is bugging it because if I just comment out all the [self setState:BOOL] statements it works as seen in "working.webm".
Any tips or ideas are appreciated. But I am a novice so please keep it simple if it is possible.
// Code to move the green UIview right or left
-(IBAction)handlePan:(UIPanGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
self.origin = CGPointMake(self.taskView.frame.origin.x, self.taskView.frame.origin.y);
} else if (sender.state == UIGestureRecognizerStateChanged) {
CGPoint touchLocation = [sender locationInView:self];
CGPoint location = CGPointMake(touchLocation.x, (self.taskView.frame.size.height/2));
self.taskView.center = location;
} else if (sender.state == UIGestureRecognizerStateEnded) {
if (self.origin.x == 0) {
// Stick right
if (self.taskView.center.x > ((self.frame.size.width/10)*4)) {
[self setPosition:NO]; // This sets the position of the view
[self setState:NO]; // This code is bugging the "animation"
} else { // Else stick to the left
[self setPosition:YES];
[self setState:YES];
}
} else {
// Stick left
if (self.taskView.center.x < ((self.frame.size.width/10)*6)) {
[self setPosition:YES];
[self setState:YES];
} else { // Else stick right
[self setPosition:NO];
[self setState:NO];
}
}
/*[UIView animateWithDuration:0.2
delay:0.01
options:UIViewAnimationOptionAllowUserInteraction
animations:^
{
[self.taskView setFrame:position];
} completion:^(BOOL finished){
}];*/
} else if (sender.state == UIGestureRecognizerStateCancelled) {
}
}
// THIS METHOD IS BUGGING THE "ANIMATION"
- (void)setState:(BOOL)state {
self.task.state = [NSNumber numberWithBool:state];
}

I think the problem is that you're trying to run a time-consuming method on the main thread, and that's why your animations are choppy.
Your problem as you mentioned resides here:
- (void)setState:(BOOL)state {
self.task.state = [NSNumber numberWithBool:state];
}
This is a method that probably does something more than just changing a property (to my understanding from your comments this is a database object)
What you could do, is to update this object on a background thread:
- (void)setState:(BOOL)state {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{
self.task.state = [NSNumber numberWithBool:state];
});
}

Related

Rotate UIImageView, then Reverse Rotation when 'Tapped' (Animation)

So, I've been working for hours trying to fix this, and I think It might just be missing something so obvious.
What Im trying to do is have an animation of a line (like a clock second hand) rotating around clockwise, but when the screen is pressed, it stops, but then when pressed again it starts again but the rotation reverses and the line starts rotating anticlockwise...
This is the code I used to rotate the line about a fixed point, and it works:
- (void) spinWithOptions: (UIViewAnimationOptions) options {
[UIView animateWithDuration: 0.0f
delay: 0.0f
options: options
animations: ^{
Hand.layer.anchorPoint = CGPointMake(0.5,1);
Hand.transform = CGAffineTransformRotate(Hand.transform, M_PI / 40);
}
completion: ^(BOOL finished) {
if (finished) {
if (animating) {
// if flag still set, keep spinning with constant speed
[self spinWithOptions: UIViewAnimationOptionCurveLinear];
} else if (options != UIViewAnimationOptionCurveEaseOut) {
// one last spin, with deceleration
//[self spinWithOptions: UIViewAnimationOptionCurveEaseOut];
}
}
}];
}
- (void) startSpin {
if (!animating) {
animating = YES;
[self spinWithOptions: UIViewAnimationOptionCurveEaseIn];
Clockwise = NO;
}
}
- (void) spinWithOptions_Anti: (UIViewAnimationOptions) options {
[UIView animateWithDuration: 0.0f
delay: 0.0f
options: options
animations: ^{
Hand.layer.anchorPoint = CGPointMake(0.5,1);
Hand.transform = CGAffineTransformRotate(Hand.transform, -M_PI / 40);
}
completion: ^(BOOL finished) {
if (finished) {
if (animating) {
// if flag still set, keep spinning with constant speed
[self spinWithOptions: UIViewAnimationOptionCurveLinear];
} else if (options != UIViewAnimationOptionCurveEaseOut) {
// one last spin, with deceleration
//[self spinWithOptions: UIViewAnimationOptionCurveEaseOut];
}
}
}];
}
- (void) startSpin_Anti {
if (!animating) {
animating = YES;
[self spinWithOptions_Anti: UIViewAnimationOptionCurveEaseIn];
Clockwise = YES;
}
}
- (void) stopSpin {
animating = NO;
}
So at the start of the application, the line begins rotating clockwise, when the ViewDidload calls startSpin, which calls spinWithOptions...
Sure enough, if I press the screen, the rotation animation stops, with this...
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event{
NSLog(#"Dircetion: %i", Clockwise);
if(animating){
[self stopSpin];
}
else{
if(Clockwise){
[self startSpin];
NSLog(#"must be clockwise");
}
else{
[self startSpin_Anti];
NSLog(#"anti");
}
}
}
But when pressed again it starts rotating as It should but never changes direction... it only ever rotates clockwise...
I just can't seem to see why it won't change direction... even the NSLogs say the bool of Clockwise is correct...

Swipe UIView off screen

I'd like to be able to have a UIView (or, if necessary, UIViewController), which can be 'dragged'/flicked/swiped off screen - but locked to the vertical axis (so the view would go up or down off the screen to show the view below). This is sort of like the Facebook app's web views.
Could this perhaps be achieved with UIGravityBehavior?
Any ideas would be fantastic.
I have made something similar a while ago by using a UIPanGestureRecognizer. Here is how this would work:
float slidingMenuOffsetSum;
- (void)handleSlidingMenuPan:(UIPanGestureRecognizer *)gestureRecognizer {
CGPoint translation = [gestureRecognizer translationInView:_slidingMenuView];
slidingMenuOffsetSum += translation.y;
CGPoint newSlidingMenuCenter = CGPointMake(_slidingMenuView.center.x, _slidingMenuView.center.y + translation.y);
if (_slidingMenuView.frame.origin.y <= 512 && _slidingMenuView.frame.origin.y >= 0) {
_slidingMenuView.center = newSlidingMenuCenter;
}
[gestureRecognizer setTranslation:CGPointZero inView:_slidingMenuView];
if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
[UIView animateWithDuration:0.3
animations:^{
[_slidingMenuView setUserInteractionEnabled:false];
if (slidingMenuOffsetSum > 0) {
if (slidingMenuOffsetSum > _slidingMenuView.frame.size.height / 4) {
[self setSlidingMenuHidden:false];
} else {
[self returnSlidingMenuToPosition];
}
} else {
if (slidingMenuOffsetSum < -_slidingMenuView.frame.size.height / 4) {
[self setSlidingMenuHidden:true];
} else {
[self returnSlidingMenuToPosition];
}
}
}
completion:^(BOOL finished) {
slidingMenuOffsetSum = 0;
[_slidingMenuView setUserInteractionEnabled:true];
}];
}
}
You can adjust the values at which the sliding menu can move, and the thresholds for detecting if the view should go back to its original position, or eg. go off the screen.

UICollectionView interactiveTransitionToCollectionViewLayout crashes with exception

I have a collectionViewController with horizontal collectionView like in Paper App. I added pan gesture to change from one layout to other and I used interactive transition. It works well if you drag and wait when animation is finished, but if you drag faster several times and don't wait animation to be finished or canceled app throw an exception:
Assertion failure in -[UICollectionView _finishInteractiveTransitionShouldFinish:finalAnimation:], /SourceCache/UIKit_Sim/UIKit-2935.137/UICollectionView.m:2691
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the collection was not prepared for an interactive transition. see startInteractiveTransitionToCollectionViewLayout:completion:'
Gesture handler code :
- (void)oneFingerGesture:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded ||
sender.state == UIGestureRecognizerStateCancelled)
{
if (self.transitionLayout.transitionProgress > 0.2) {
[self.collectionView finishInteractiveTransition];
} else {
[self.collectionView cancelInteractiveTransition];
}
}else
{
CGPoint point = [sender locationInView:sender.view];
if (sender.state == UIGestureRecognizerStateBegan && !self.transitionLayout && !_isInTransition)
{
invertPan = self.largeLayout == self.collectionView.collectionViewLayout;
UICollectionViewLayout *toLayout = invertPan ? self.smallLayout : self.largeLayout;
self.transitionLayout = [self.collectionView startInteractiveTransitionToCollectionViewLayout:toLayout
completion:^(BOOL completed, BOOL finish) {
self.transitionLayout = nil;
_isInTransition = NO; }];
self.initialTapPoint = point;
_isInTransition = YES;
}else if(sender.state == UIGestureRecognizerStateChanged && self.transitionLayout && _isInTransition)
{
CGFloat distance = _initialTapPoint.y - point.y;
if (invertPan) {
distance = -distance;
}
CGFloat dimension = self.collectionView.bounds.size.height - 200;
CGFloat progress = MAX(MIN(((distance)/ dimension), 1.0), 0.0);
[self.transitionLayout setTransitionProgress:progress];
}
}
}
The documentation states that calls to finishInteractiveTransition: and cancelInteractiveTransition: will install the layout object the transition is going to and from respectively. However, this appears to not happen immediately. Thus, if the gesture triggering and driving the transition can happen in quick succession, it is not sufficient to check whether the current layout is a UICollectionViewTransitionLayout or a subclass thereof before calling one of the two methods to end the transition.
I solved the problem by introducing a BOOL ivar (_isFinishingOrCancellingTransition) to avoid ending a transition that is already in the process of ending:
if (_isFinishingOrCancellingTransition) return;
if (!self.transitionLayout) return;
if (self.collectionView.collectionViewLayout != self.transitionLayout) return;
_isFinishingOrCancellingTransition = YES;
if (self.transitionLayout.transitionProgress > 0.5)
{
[self.collectionView finishInteractiveTransition];
}
else
{
[self.collectionView cancelInteractiveTransition];
}
and then resetting the BOOL when the transition is completed:
self.transitionLayout = [self.collectionView startInteractiveTransitionToCollectionViewLayout:toLayout completion:^(BOOL completed, BOOL finish) {
self.transitionLayout = nil;
_isFinishingOrCancellingTransition = NO;
}];

UIView block animation not beginning from current state

I'm trying to animate a some subviews of a subview of an annotation view.
So, to be more clear, the gray pin is my annotation view, which has an invisible subview, and this subview contains those 4 colored circle views.
annotation view
| invisible view (called typeSelectionView)
| yellow view (called typeButton)
| blue view (called typeButton)
| green view (called typeButton)
| red view (called typeButton)
First the circles are not visible (transform scale (0, 0) and their center at the head of the gray pin), then, when I select the gray pin, those circles come from the pin head growing and moving to their original position (the one on the picture above).
When I deselect the pin, the opposite animation happens.
And when I select and deselect (or the opposite) before the animation is finished, it changes the way it was going (if it was growing it shrinks) reverting the animation in the middle. That is why I need the UIViewAnimationOptionBeginFromCurrentState.
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
// in case the typeSelectionView was already being showed by another annotationView
if (self.typeSelectionView.superview != annotationView) {
[self.typeSelectionView removeFromSuperview];
[self.typeSelectionView showTypeButtons:NO animated:NO completion:NULL];
}
[annotationView addSubview:self.typeSelectionView];
[self.typeSelectionView showTypeButtons:YES animated:YES completion:NULL];
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)annotationView
{
if (self.typeSelectionView.superview) {
[self.typeSelectionView showTypeButtons:NO
animated:YES
completion:^(BOOL finished) {
if (finished) {
[self.typeSelectionView removeFromSuperview];
}
}];
}
}
- (void)showTypeButtons:(BOOL)show animated:(BOOL)animated completion:(void (^)(BOOL))completion
{
void (^block)(void) = ^{
for (int i = 0; i < self.typeButtons.count; i++) {
TypeButton *typeButton = self.typeButtons[i];
if (show) {
typeButton.center = [self _typeButtonCenterForIndex:i numberOfButtons:self.typeButtons.count];
typeButton.transform = CGAffineTransformIdentity;
} else {
typeButton.center = CGPointMake(viewWidth / 2, viewheight + 5); // pin head center
typeButton.transform = CGAffineTransformMakeScale(0, 0);
}
}
};
if (animated) {
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:block
completion:^(BOOL finished) {
if (completion) {
completion(finished);
}
}];
} else {
block();
if (completion) {
completion(YES);
}
}
}
So far no problem, but when one gray pin is displaying the typeSelectionView (like in the picture above) and then I select another pin, it's like this part:
if (self.typeSelectionView.superview != annotationView) {
[self.typeSelectionView removeFromSuperview];
[self.typeSelectionView showTypeButtons:NO animated:NO completion:NULL];
}
did nothing.
The 4 circles simple jump from one pin to the other without any animation.
What I would expect is that the [self.typeSelectionView showTypeButtons:NO animated:NO completion:NULL]; set those buttons to be "hidden" (scale (0, 0) and center in the head of the pin) without any animation (because the invisible view doesn't have a superview anymore, so they are not even showing), and then when I add the typeSelectionView to the new annotationView with [annotationView addSubview:self.typeSelectionView];, I animate the circles to their original position again with [self.typeSelectionView showTypeButtons:YES animated:YES completion:NULL];.
Btw, doing [typeButton.layer removeAllAnimations] at the beginning of showTypeButtons:animated:completion: does nothing.
How do I solve this?
Thanks in advance.
EDIT: (BONUS if someone can explain that to me... and help me solve it) =)
Suppose the circles are showing like in the picture above, then this code:
NSLog(#"type selected");
[self.typeSelectionView showTypeButtons:NO
animated:YES
completion:^(BOOL finished) {
NSLog(#"finisheddddd: %d", finished);
}];
[self.typeSelectionView showTypeButtons:NO
animated:YES
completion:^(BOOL finished) {
NSLog(#"finished: %d", finished);
}];
generates the following output:
type selected
finished: 1
finisheddddd: 1
So the second animation finishes first and both animations finish with finished == YES.
Also the second animation (which finishes first) finishes instantly and not after the end of the animation time.
O.o
Check the code I wrote but not tested. Please experiment with it to get your result
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)annotationView
{
if (self.typeSelectionView.superview)
{
[self.typeSelectionView showTypeButtons:NO
animated:YES
completion:^(BOOL finished) {
if (finished) {
[self.typeSelectionView removeFromSuperview];
}
}];
}
}
- (void)showTypeButtons:(BOOL)show animated:(BOOL)animated completion:(void (^) (BOOL))completion
{
[UIView animateWithDuration:(animated) ?0.5f : 0.0f
delay:(self.animationStarted)? 0.5 : 0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.animationStarted = YES;
[self setButtonVisible:show];
}
completion:^(BOOL finished) {
self.animationStarted = NO;
if (completion) {
completion(finished);
}
}];
}
-(void)setButtonVisible:(BOOL)visible
{
[self.typeButtons enumerateObjectsUsingBlock:^(TypeButton typeButton, NSUInteger idx, BOOL *stop) {
if (visible)
{
typeButton.center = [self _typeButtonCenterForIndex:idx numberOfButtons:self.typeButtons.count];
typeButton.transform = CGAffineTransformIdentity;
}
else
{
typeButton.center = CGPointMake(viewWidth / 2, viewheight + 5); // pin head center
typeButton.transform = CGAffineTransformMakeScale(0, 0);
}
}];
}
Turns out the problem is that when I set my typeButtons non-animated, they are not redraw instantly, so they don't change their current state, so what happens is:
one mapView:didSelectAnnotationView: method call:
- show the buttons animated
***redraw cycle here***
another mapView:didSelectAnnotationView: method call:
- hide the buttons non-animated
(no redraw cycle here, so buttons are still showing)
- show the buttons animated
So I came up with this shitty solution:
- (void)showTypeButtons:(BOOL)show animated:(BOOL)animated completion:(void (^)(BOOL))completion
{
void (^block)(void) = ^{
for (int i = 0; i < self.typeButtons.count; i++) {
TypeButton *typeButton = self.typeButtons[i];
if (show) {
typeButton.center = [self _typeButtonCenterForIndex:i numberOfButtons:self.typeButtons.count];
typeButton.transform = CGAffineTransformIdentity;
} else {
typeButton.center = CGPointMake(viewWidth / 2, viewheight + 5); // pin head center
typeButton.transform = CGAffineTransformMakeScale(0, 0);
}
}
};
if (animated) {
[UIView animateWithDuration:0.5
delay:0
options:self.typeButtonsChangedWithoutAnimation ? 0 : UIViewAnimationOptionBeginFromCurrentState
animations:block
completion:^(BOOL finished) {
if (completion) {
completion(finished);
}
}];
self.typeButtonsChangedWithoutAnimation = NO;
} else {
self.typeButtonsChangedWithoutAnimation = YES;
block();
if (completion) {
completion(YES);
}
}
}
Without the UIViewAnimationOptionBeginFromCurrentState, the animation starts from its actual state and not from its draw state.
One solution that I found, but did not work was: What is the most robust way to force a UIView to redraw?
Even if it did work, I wouldn't want it either...
Btw, I'm not gonna accept this answer, because it sucks.
It does not actually solve my question, which is to change the current state, not pass through it.
I'm just posting it in case someone sees and gets an idea of how to actually solve my original question.

iOS (iPad) Drag & Drop in a UISplitViewController

I'm working on a D&D in a UISplitViewController (based on Xcode template project), from the MasterViewController to the DetailViewController.
Basically, what I'm doing is creating a UILongPressGestureRecognizer and placing it on the self.tableview property of the MasterViewController.
Below is my gesture recognizer.
- (void)gestureHandler:(UIGestureRecognizer*)gesture
{
CGPoint location;
NSIndexPath* indexPath = [self.tableView indexPathForRowAtPoint:[gesture locationInView:self.tableView]];
if (gesture.state == UIGestureRecognizerStateBegan) {
// Create draggable view
NSString* imageCanvasName = [self imageNameByIndexPath:indexPath isDetail:YES];
UIImage* imageCanvas = [UIImage imageNamed:imageCanvasName];
_draggedView = [[UIImageView alloc] initWithImage:imageCanvas];
// Create drag-feedback window, add the drag-view and make the drag-window visible
CGRect windowFrame = self.view.window.frame;
_dragFeedbackWindow = [[UIWindow alloc] initWithFrame:windowFrame];
location = [gesture locationInView:gesture.view.window];
[_draggedView setCenter:location];
[_dragFeedbackWindow addSubview:_draggedView];
[_dragFeedbackWindow setHidden:NO];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
// Update drag-view location
location = [gesture locationInView:gesture.view.window];
[_draggedView setCenter:location];
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
// Disconnect drag-view and hide drag-feedback window
[_draggedView removeFromSuperview];
[_dragFeedbackWindow setHidden:YES];
// If drop is in a valid location...
if ([self.tableView pointInside:_draggedView.center withEvent:nil] == NO)
{
// Get final location in detailViewController coordinates
location = [gesture locationInView:self.detailViewController.view];
[_draggedView setCenter:location];
[self.detailViewController.view addSubview:_draggedView];
}
}
else {
NSLog(#"%s unrecognized gesture %d", __FUNCTION__, gesture.state);
}
}
All works very nicely when the iPad is in portrait mode - the drag, the drop - the works.
My problem starts if the iPad is rotated...
In such a case, my _draggedView appears "counter-rotated" - it will "reflect" the iPad's rotation - until dropped.
It's like I must apply some rotation to _dragFeedbackWindow - but I tried a number of things, failing...
Any idea?
Thanks!
OK - I figured out the "WHY" this happens, and the "HOW" to fix (and will attach the handler code to do this correctly, all below.
Here's the code... "Just" add it to your MAsterViewController (if - like me - you want to drag from the master to the detail...)
// A simple UIView extension to rotate it to a given orientation
#interface UIView(oriented)
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation;
#end
#implementation UIView(oriented)
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation {
CGFloat angle = 0.0;
switch (orientation) {
case UIInterfaceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIInterfaceOrientationLandscapeLeft:
angle = - M_PI / 2.0f;
break;
case UIInterfaceOrientationLandscapeRight:
angle = M_PI / 2.0f;
break;
default: // as UIInterfaceOrientationPortrait
angle = 0.0;
break;
}
self.transform = CGAffineTransformMakeRotation(angle);
}
Now, the LongPress gesture handler...
- (void)gestureHandler:(UIGestureRecognizer*)gesture
{
CGPoint location;
UIWindow* dragFeedback = [UIApplication sharedApplication].delegate.window;
if (gesture.state == UIGestureRecognizerStateBegan) {
// Create draggable view
_draggedView = [[UIImageView alloc] initWithImage:#"someImage.png"];
// Required to adapt orientation... WORKS, BUT WHY NEEDED???
[_draggedView rotateToOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
// Create drag-feedback window, add the drag-view and make the drag-window visible
location = [gesture locationInView:dragFeedback];
[_draggedView setCenter:location];
[dragFeedback addSubview:_draggedView];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
// Update drag-view location
location = [gesture locationInView:dragFeedback];
[_draggedView setCenter:location];
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
// Disconnect drag-view and hide drag-feedback window
[_draggedView removeFromSuperview];
// Get final location in detailViewController coordinates
location = [gesture locationInView:self.detailViewController.view];
[_draggedView setCenter:location];
// "Noramlize" orientation... WORKS, BUT WHY NEEDED???
[_draggedView rotateToOrientation:UIInterfaceOrientationPortrait];
[self.detailViewController.view addSubview:_draggedView];
}
else {
NSLog(#"%s unrecognized gesture %d", __FUNCTION__, gesture.state);
}
}
This works like a charm - let me know how it works for you (if you need a sample project, let me know...)
Thanks for your solution! You got me 90% of the way there. In payment, I will give you the last 10% :)
The issue seems to be that the UIWindow never gets rotated, only its subviews do! So the solution is to use a subview.
I've also added some code that shows how to see which cell is selected (assuming your master view is a UITableViewController).
- (IBAction)handleGesture:(UIGestureRecognizer *)gesture
{
CGPoint location;
UIWindow *window = [UIApplication sharedApplication].delegate.window;
//The window doesn't seem to rotate, so we'll get the subview, which does!
UIView *dragFeedback = [window.subviews objectAtIndex:0];
if (gesture.state == UIGestureRecognizerStateBegan) {
location = [gesture locationInView:dragFeedback];
//which cell are we performing the long hold over?
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:[gesture locationInView:self.tableView]];
UITableViewCell *selecteCell = [self.tableView cellForRowAtIndexPath:indexPath];
NSLog(#"Cell %#", selecteCell);
// Create draggable view
_draggedView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"someImage.png"]];
// Create drag-feedback window, add the drag-view and make the drag-window visible
[_draggedView setCenter:location];
[dragFeedback addSubview:_draggedView];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
// Update drag-view location
location = [gesture locationInView:dragFeedback];
[_draggedView setCenter:location];
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
// Disconnect drag-view and hide drag-feedback window
[_draggedView removeFromSuperview];
// Get final location in detailViewController coordinates
location = [gesture locationInView:self.detailViewController.view];
[_draggedView setCenter:location];
[self.detailViewController.view addSubview:_draggedView];
}
else {
NSLog(#"%s unrecognized gesture %d", __FUNCTION__, gesture.state);
}
}

Resources