Отскок UIImageView назад при перетаскивании с экрана


Что мне нужно, так это когда UIImageView утаскивают с экрана, чтобы он отскочил назад, когда его отпустят. У меня он работает в левой и верхней сторонах, вот что я делаю.

    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {          

        if (!CGRectContainsPoint(self.view.frame, imageView.frame.origin)){  

            CGFloat newX = 0.0f;
            CGFloat newY = 0.0f;

            // If off screen upper and left

            if (imageView.frame.origin.x < 0.0f){
                CGFloat negX = imageView.frame.origin.x * -1;
                newX = negX;
            }else{
                newX = imageView.frame.origin.x;
            }

            if (imageView.frame.origin.y < 0.0f){
                CGFloat negY = imageView.frame.origin.y * -1;
                newY = negY;
            }else{
                newY = imageView.frame.origin.y;
            }


            CGRect newPoint = CGRectMake(newX, newY, imageView.frame.size.width, imageView.frame.size.height);

            [UIView beginAnimations:@"BounceAnimations" context:nil];
            [UIView setAnimationDuration:.5];
            [letterOutOfBounds play];
            [imageView setFrame:newPoint];

            [UIView commitAnimations];

        }
    }
}
Поэтому я хотел бы добиться того же самого для правой и нижней сторон. Но я застрял на этом на некоторое время. Есть Идеи?
2 2

2 ответа:

Как насчет чего-то вроде следующего?

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    UIImageView *imageView = nil;

    BOOL moved = NO;
    CGRect newPoint = imageView.frame;

    // If off screen left

    if (newPoint.origin.x < 0.0f){
        newPoint.origin.x *= -1.0;
        moved = YES;
    }

    // if off screen up

    if (newPoint.origin.y < 0.0f){
        newPoint.origin.y *= -1.0;
        moved = YES;
    }

    // if off screen right

    CGFloat howFarOffRight = (newPoint.origin.x + newPoint.size.width) - imageView.superview.frame.size.width;
    if (howFarOffRight > 0.0)
    {
        newPoint.origin.x -= howFarOffRight * 2;
        moved = YES;
    }

    // if off screen bottom

    CGFloat howFarOffBottom = (newPoint.origin.y + newPoint.size.height) - imageView.superview.frame.size.height;
    if (howFarOffBottom > 0.0)
    {
        newPoint.origin.y -= howFarOffBottom * 2;
        moved = YES;
    }

    if (moved)
    {
        [UIView beginAnimations:@"BounceAnimations" context:nil];
        [UIView setAnimationDuration:.5];
        [letterOutOfBounds play];
        [imageView setFrame:newPoint];

        [UIView commitAnimations];
    }
}
Когда я читаю ваш код, логика "если слева, переместите его обратно в вид на то же расстояние, на котором он был вне экрана."Честно говоря, это не совсем имеет смысл для меня (почему, когда отскакивает назад, координата зависит от того, насколько далеко от экрана она была), но я пытался соблюдать это в логике "вне экрана справа" и "вне экрана снизу". Очевидно, моя логика использует superview из imageView для определения ширины содержащее представление, но если это не подходит, замените его тем, что есть.

Редактировать:

Я лично делаю это с распознавателями жестов, такими как:

UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self.imageView addGestureRecognizer:pan];
self.imageView.userInteractionEnabled = YES;

Таким образом, распознаватель жестов для анимации перемещения изображения назад будет:

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static CGRect originalFrame; // you could make this an ivar if you want, but just for demonstration purposes

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        originalFrame = self.imageView.frame;
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        CGPoint translate = [gesture translationInView:gesture.view];

        CGRect newFrame = originalFrame;

        newFrame.origin.x += translate.x;
        newFrame.origin.y += translate.y;

        gesture.view.frame = newFrame;
    }
    else if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateCancelled)
    {
        CGRect newFrame = gesture.view.frame;

        newFrame.origin.x = fmaxf(newFrame.origin.x, 0.0);
        newFrame.origin.x = fminf(newFrame.origin.x, gesture.view.superview.bounds.size.width - newFrame.size.width);

        newFrame.origin.y = fmaxf(newFrame.origin.y, 0.0);
        newFrame.origin.y = fminf(newFrame.origin.y, gesture.view.superview.bounds.size.height - newFrame.size.height);

        // animate how ever you want ... I generally just do animateWithDuration

        [UIView animateWithDuration:0.5 animations:^{
            gesture.view.frame = newFrame;
        }];
    }
}

Или, если вам нужен распознаватель жестов, который просто предотвращает перетаскивание изображения с экрана в первую очередь, это будет:

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static CGRect originalFrame;

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        originalFrame = self.imageView.frame;
    }
    else if (gesture.state == UIGestureRecognizerStateChanged)
    {
        CGPoint translate = [gesture translationInView:gesture.view];

        CGRect newFrame = originalFrame;

        newFrame.origin.x += translate.x;
        newFrame.origin.x = fmaxf(newFrame.origin.x, 0.0);
        newFrame.origin.x = fminf(newFrame.origin.x, gesture.view.superview.bounds.size.width - newFrame.size.width);

        newFrame.origin.y += translate.y;
        newFrame.origin.y = fmaxf(newFrame.origin.y, 0.0);
        newFrame.origin.y = fminf(newFrame.origin.y, gesture.view.superview.bounds.size.height - newFrame.size.height);

        gesture.view.frame = newFrame;
    }
}

Кстати, в iOS 7 Вы можете дать анимация вида изображения обратно в исходное положение немного подпрыгивает с помощью новых animationWithDuration с параметрами usingSpringWithDampening и initialSpringVelocity:

[UIView animateWithDuration:1.0
                      delay:0.0
     usingSpringWithDamping:0.3
      initialSpringVelocity:0.1
                    options:0
                 animations:^{
                     // set the new `frame` (or update the constraint constant values that
                     // will dictate the `frame` and call `layoutViewsIfNeeded`)
                 }
                 completion:nil];

Альтернативно, в iOS7, вы также можете использовать динамику UIKit для добавления UISnapBehavior:

self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
self.animator.delegate = self;

UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.viewToAnimate snapToPoint:CGPointMake(self.viewToAnimate.center.x, self.view.frame.size.height - 50)];

// optionally, you can control how much bouncing happens when it finishes, e.g., for a lot of bouncing:
//
// snap.damping = 0.2;

// you can also slow down the snap by adding some resistance
//
// UIDynamicItemBehavior *resistance = [[UIDynamicItemBehavior alloc] initWithItems:@[self.viewToAnimate]];
// resistance.resistance = 20.0;
// resistance.angularResistance = 200.0;
// [self.animator addBehavior:resistance];

[self.animator addBehavior:snap];

Я думаю, что самый простой способ-проверить, вышел ли ваш imageView из вашего self.view.

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {  
    if (!CGRectContainsRect(self.view.frame, imageView.frame)){
        // Your animation to bounce.
    }
}