iOS >> 拖动视图正在跳回原始位置 >> 自动布局结合 UIPanGestureRecognizer 问题

Posted

技术标签:

【中文标题】iOS >> 拖动视图正在跳回原始位置 >> 自动布局结合 UIPanGestureRecognizer 问题【英文标题】:iOS >> Dragged View is Jumping Back to Original Position >> Auto Layout Combined with UIPanGestureRecognizer Issue 【发布时间】:2013-04-14 08:15:47 【问题描述】:

我有几个用户应该能够拖放的 UIView。我使用 UIPanGestureRecognizer 来管理 d&d 操作(参见下面的代码)。 d&d 动作工作正常,但是当我开始拖动另一个 UIView 时,我刚刚放下的那个正在跳回原来的位置。

- (void)handlePan:(UIPanGestureRecognizer *)recognizer 

    CGPoint translation = [recognizer translationInView:self.view];

    switch (recognizer.state) 
        case UIGestureRecognizerStateBegan:
            for (UIView* checkView in dragAndDropImageViewsArray) 
                if (checkView == recognizer.view) 
                    [checkView.superview bringSubviewToFront:checkView]; //this is done to make sure the currently dragged object is the top one
                
            
            break;

        case UIGestureRecognizerStateChanged:
            recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y);
            break;

        case UIGestureRecognizerStateEnded:
            NSLog(@"UIGestureRecognizerStateEnded");
            break;

        default:
            break;
    

    [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];


我尝试了几种“解决”问题但导致其他问题的方法:

取消自动布局:这完全解决了它,但现在我必须开始计算和管理不同设备和方向的对象位置和大小。 使用 layer.zPosition 而不是bringSubviewToFront:使对象出现在其他对象之上,同时显然避免了“跳回”,但不允许我更改不同可拖动对象之间的层次结构。

我开始加深对自动布局的理解,但是当我觉得我要寻找的东西应该非常简单时,它变得非常复杂;我看到了许多处理执行“重新自动布局”的方法,但我不清楚如何使用它们。

这里有简单的解决方案吗?喜欢调用覆盖 IB Auto-Layout 约束的方法并根据 UIView 新位置重新定义它们?

非常感谢任何帮助。

【问题讨论】:

【参考方案1】:

我认为,如果您使用自动布局并且想要更改视图的位置,则需要更新视图上的约束,而不是手动设置视图的中心。从描述中听起来您正在设置中心,但是当应用程序接下来布置视图(例如,您拖动另一个视图)时,它会将中心重新设置为自动布局所说的任何内容。

我不确定您应用的其余部分是什么样的,但您可能能够找到附近的视图并为其添加约束,或者您可能更喜欢只更新拖动视图的约束并设置前导空间和手动到超级视图的顶部空间。如果您有一个方便的集合,您还可以清除所有约束,然后重新添加它们,因为您希望在视图之间保持填充。

要在代码中设置约束,您可以清除移动视图的约束并重新添加它们,或者如果您在 IB 中有视图,您可以将约束拖到代码中以创建插座。在代码中查看 removeConstraints 和 addConstraints 方法。

最后,如果它在没有自动布局的情况下按您希望的方式工作,您可以尝试在移动的视图上设置 translatesAutoresizingMaskIntoConstraints = YES,但请注意这可能会导致其他地方发生冲突。

【讨论】:

谢谢 James - 您能否更具体地了解如何使用 removeConstraints 和 addConstraints 方法?将视图放在新位置后,如何捕获“新约束”数组?如果有一个很好的教程可以指导我 - 我真的很感激。 尝试 UIView 文档的管理约束部分来添加/删除。如果你只是想让它停留在它着陆的地方,你可以尝试将顶部填充和前导填充设置到视图。使用视觉约束,将视图从顶部设置为 400,从前沿设置为 200 看起来像 [NSLayoutConstraint constraintsWithVisualFormat:@"|-(=200)-[movedView]" options:nil metrics:nil views:@@" moveView": Recognizer.view] [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(=400)-[movedView]" options:nil metrics:nil views:@@"movedView":recognizer.view]【参考方案2】:

这一切都可以使用 Interface Builder。这是我用来执行此操作的代码:

@property (weak,nonatomic) IBOutlet NSLayoutConstraint *buttonXConstraint;
@property (weak,nonatomic) IBOutlet NSLayoutConstraint *buttonYConstraint;

然后将这些 IBOutlets 连接到 Interface Builder 中的水平约束(X 位置)和垂直约束(Y 位置)。确保约束连接到基础视图,而不是最近的视图。

连接一个平移手势,然后使用以下代码拖动您的对象:

- (IBAction)panPlayButton:(UIPanGestureRecognizer *)sender

    if(sender.state == UIGestureRecognizerStateBegan)

     else if(sender.state == UIGestureRecognizerStateChanged)
        CGPoint translation = [sender translationInView:self.view];

        //Update the constraint's constant
        self.buttonXConstraint.constant += translation.x;
        self.buttonYConstraint.constant += translation.y;

        // Assign the frame's position only for checking it's fully on the screen
        CGRect recognizerFrame = sender.view.frame;
        recognizerFrame.origin.x = self.buttonXConstraint.constant;
        recognizerFrame.origin.y = self.buttonYConstraint.constant;

        // Check if UIImageView is completely inside its superView
        if(!CGRectContainsRect(self.view.bounds, recognizerFrame)) 
            if (self.buttonYConstraint.constant < CGRectGetMinY(self.view.bounds)) 
                self.buttonYConstraint.constant = 0;
             else if (self.buttonYConstraint.constant + CGRectGetHeight(recognizerFrame) > CGRectGetHeight(self.view.bounds)) 
                self.buttonYConstraint.constant = CGRectGetHeight(self.view.bounds) - CGRectGetHeight(recognizerFrame);
            

            if (self.buttonXConstraint.constant < CGRectGetMinX(self.view.bounds)) 
                self.buttonXConstraint.constant = 0;
             else if (self.buttonXConstraint.constant + CGRectGetWidth(recognizerFrame) > CGRectGetWidth(self.view.bounds)) 
                self.buttonXConstraint.constant = CGRectGetWidth(self.view.bounds) - CGRectGetWidth(recognizerFrame);
            
        

        //Layout the View
        [self.view layoutIfNeeded];
     else if(sender.state == UIGestureRecognizerStateEnded)

    

    [sender setTranslation:CGPointMake(0, 0) inView:self.view];

我在其中添加了代码来检查框架并确保它不会超出视图。如果您想让对象部分离开屏幕,请随意将其取出。

我花了一点时间才意识到是使用 AutoLayout 重置了视图,但约束会在这里做你需要的!

【讨论】:

这对我有用。但是,检查框架位置是否完全在屏幕中对移动的限制远小于超级视图边界。

以上是关于iOS >> 拖动视图正在跳回原始位置 >> 自动布局结合 UIPanGestureRecognizer 问题的主要内容,如果未能解决你的问题,请参考以下文章

阻止 UIImageView 返回到其在视图中的原始位置?

在 iOS 13 上拖动时,UICollectionView 单元格捕捉到原始位置

iOS中的触摸事件

在更改其嵌套的 UILabel 时移动 UIView 会导致视图跳回初始位置

bringSubviewToFront 将 UIView 移回原始位置

如何在 Grid 中拖动和移动子视图以交换它们的位置