在父 UIView 中移动 UIView (UIPanGestureRecognizer)

Posted

技术标签:

【中文标题】在父 UIView 中移动 UIView (UIPanGestureRecognizer)【英文标题】:Moving UIView within Parent UIView (UIPanGestureRecognizer) 【发布时间】:2013-06-21 12:02:37 【问题描述】:

我正在使用以下代码来移动 UIView 中存在的 UIImageView。

- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer 

CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
                                     recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];

我想让 UIView 不会移动到父视图之外。此刻图像视图能够在整个屏幕上移动。

【问题讨论】:

【参考方案1】:

首先获取UIImageView 的新框架,并使用CGRectContainsRect() 方法检查它是否完全在其superView 内。如果是,则将UImageView's 框架设置为新框架。

- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer 

    CGPoint translation = [recognizer translationInView:self.view];
    CGRect recognizerFrame = recognizer.view.frame;
    recognizerFrame.origin.x += translation.x;
    recognizerFrame.origin.y += translation.y; 

    // Check if UIImageView is completely inside its superView
    if (CGRectContainsRect(self.view.bounds, recognizerFrame)) 
        recognizer.view.frame = recognizerFrame;
    
    // Else check if UIImageView is vertically and/or horizontally outside of its
    // superView. If yes, then set UImageView's frame accordingly.
    // This is required so that when user pans rapidly then it provides smooth translation.
    else 
        // Check vertically
        if (recognizerFrame.origin.y < self.view.bounds.origin.y) 
            recognizerFrame.origin.y = 0;
                
        else if (recognizerFrame.origin.y + recognizerFrame.size.height > self.view.bounds.size.height) 
            recognizerFrame.origin.y = self.view.bounds.size.height - recognizerFrame.size.height;
        

        // Check horizantally
        if (recognizerFrame.origin.x < self.view.bounds.origin.x) 
            recognizerFrame.origin.x = 0;
        
        else if (recognizerFrame.origin.x + recognizerFrame.size.width > self.view.bounds.size.width) 
            recognizerFrame.origin.x = self.view.bounds.size.width - recognizerFrame.size.width;
        
    

    // Reset translation so that on next pan recognition
    // we get correct translation value
    [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];

确保您传递了 superView 的 boundsUIImageViewframe,以便 CGRects 位于同一坐标系中。

【讨论】:

这一切都很好,除了:你不知道识别器.view 的超级视图是 self.view 并且如果用户非常快速地平移到超级视图的范围之外,你会得到丑陋的效果识别器的帧冻结在超级视图边界的中间。最后,如果该用户继续平移,则视图将继续平移而不再处于用户的手指之下。总而言之,你可以做得更好。 @micantox 我同意你的一些观点并更新了我的答案。对于superView,我知道self.view是imageView的superView,因为他自己在自己的代码中使用了self.view作为superView。 @IconicDigital 看看我更新的答案,在所有情况下都有正确的翻译效果。 我不确定我错过了什么,但 UIPanGestureRecognizer 没有我所在位置的“框架”属性......所以识别器框架不起作用。我可以使用recognizer.view.frame,但是将recognizer.view.frame 设置为recognizerFrame 似乎不会做任何事情,因为我只是将相同的值分配回自身。我在下面尝试了一种不同的方法,它对我很有效,但仍然对这个方法很好奇。如果有人可以提供建议,我将不胜感激,谢谢。 @ReidBelton 您必须使用recognizer.view.frame 是正确的。它没有移动的原因可能是“你根本没有改变它的值”或“你改变了视图框架而不是你想要拖动的东西”。【参考方案2】:

尝试:

- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer 

    if (gesture.state==UIGestureRecognizerStateChanged || gesture.state == UIGestureRecognizerStateEnded)
        UIView *superview = recognizer.view.superview;
        CGSize superviewSize = superview.bounds.size;
        CGSize thisSize = recognizer.view.size;
        CGPoint translation = [recognizer translationInView:self.view];
        CGPoint center = CGPointMake(recognizer.view.center.x + translation.x,
                                 recognizer.view.center.y + translation.y);

        CGPoint resetTranslation = CGPointMake(translation.x, translation.y);

        if(center.x - thisSize.width/2 < 0)
            center.x = thisSize.width/2;
        else if (center.x + thisSize.width/2 > superviewSize.width)
            center.x = superviewSize.width-thisSize.width/2;
        else
            resetTranslation.x = 0; //Only reset the horizontal translation if the view *did* translate horizontally

        if(center.y - thisSize.height/2 < 0)
            center.y = thisSize.height/2;
        else if(center.y + thisSize.height/2 > superviewSize.height)
            center.y = superviewSize.height-thisSize.height/2;
        else
            resetTranslation.y = 0; //Only reset the vertical translation if the view *did* translate vertically

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

这样它就永远不会移出父视图边界,如果你试图将它移出边界,它只会“粘”在边缘!

【讨论】:

【参考方案3】:

micantox 答案的 Swift 版本

let gesture = UIPanGestureRecognizer(target: self, action: #selector(self.wasDragged(gestureRecognizer:)))
imageView.addGestureRecognizer(gesture)
imageView.isUserInteractionEnabled = true

@objc func wasDragged(gestureRecognizer: UIPanGestureRecognizer) 
if gestureRecognizer.state == UIGestureRecognizerState.changed || gestureRecognizer.state == UIGestureRecognizerState.ended 
   let superview = gestureRecognizer.view?.superview
   let superviewSize = superview?.bounds.size
   let thisSize = gestureRecognizer.view?.frame.size
   let translation = gestureRecognizer.translation(in: self.view)
   var center = CGPoint(x: gestureRecognizer.view!.center.x + translation.x, y: gestureRecognizer.view!.center.y + translation.y)
   var resetTranslation = CGPoint(x: translation.x, y: translation.y)

   if center.x - (thisSize?.width)!/2 < 0 
       center.x = (thisSize?.width)!/2
        else if center.x + (thisSize?.width)!/2 > (superviewSize?.width)! 
       center.x = (superviewSize?.width)!-(thisSize?.width)!/2
        else 
         resetTranslation.x = 0 //Only reset the horizontal translation if the view *did* translate horizontally
       

   if center.y - (thisSize?.height)!/2 < 0 
       center.y = (thisSize?.height)!/2
       else if center.y + (thisSize?.height)!/2 > (superviewSize?.height)! 
       center.y = (superviewSize?.height)!-(thisSize?.height)!/2
       else 
        resetTranslation.y = 0 //Only reset the vertical translation if the view *did* translate vertically
      

      gestureRecognizer.view?.center = center
      gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: self.view)
     

【讨论】:

【参考方案4】:

只有当 frame 位于其父视图的 bounds 内时,您才必须将 recognizer.view.center 设置为新值。在recognizer.view.superview.boundsrecognizer.view.frame 上使用CGRectContainsRect 来验证它们是否被包含。

如果要允许图像视图移动到其父视图之外直到视图的中心点超出父视图的边界,可以使用UIViewconvertPoint:toView 方法并验证新的CGPoint不在你父母的范围内。

【讨论】:

【参考方案5】:
if (gesture.state==UIGestureRecognizerStateChanged)

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

CGRect rectToCheck=CGRectMake(yourView.frame.origin.x+translation.x,       yourView.frame.origin.y+translation.y, CGRectGetWidth(yourView.frame),     CGRectGetHeight(yourView.frame));

     if(CGRectContainsRect(self.view.bounds,rectToCheck))// check that the rect that is   going to form lies within the bounds of self.view
     

     yourView.frame=CGRectMake(yourView.frame.origin.x+translation.x,  yourView.frame.origin.y+translation.y, CGRectGetWidth(yourView.frame),  CGRectGetHeight(yourView.frame)); 
     
     [gesture setTranslation:CGPointZero yourView]; // important to set this 

 

附:使用手势状态块来启动、平移、移除。即 UIGestureRecognizerStateBegan UIGestureRecognizerStateChanged UIGestureRecognizerStateEnded

【讨论】:

以上是关于在父 UIView 中移动 UIView (UIPanGestureRecognizer)的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 UIView 子视图没有在父视图中呈现? (附代码)

在父 UIView 中垂直居中 UILabel

如何在父 uiview 内设置 uiview 大小?

限制超级视图内的视图

使用addSubview将UIView移动到其他UIView中

移动带有子视图的 UIView