【问题标题】:How can I move a UIView to another parent while panning?平移时如何将 UIView 移动到另一个父级?
【发布时间】:2021-10-27 01:19:15
【问题描述】:

我正在使用 UIKit 和 SpriteKit 在 Swift 中制作游戏原型。库存(在此屏幕截图的底部)是 UIViewUIImageView 各个项目的子视图。在这个例子中,一个橡子。

我让橡子识别“平移”手势,所以我可以拖动它。但是,它会将其呈现在层次结构中的其他视图之下。我希望它从库存中弹出并位于所有内容之上(甚至在其父视图之上),这样我就可以将它放到游戏中其他地方的其他视图中。

这就是我在橡子视图上的panHandler

  @objc func panHandler(gesture: UIPanGestureRecognizer){
    switch (gesture.state) {
    case .began:
      removeFromSuperview()
      controller.view.addSubview(self)
    case .changed:
      let translation = gesture.translation(in: controller.view)
      if let v = gesture.view {
        v.center = CGPoint(x: v.center.x + translation.x, y: v.center.y + translation.y)
      }
      gesture.setTranslation(CGPoint.zero, in: controller.view)
    default:
      return
    }
  }

问题出在.began 的情况下,当我将它从超级视图中删除时,平移手势立即取消。是否可以从超级视图中删除视图并将其作为子视图添加到其他地方,同时保持平移手势?

或者,如果我的方法完全错误,您能否指点我如何使用另一种方法来实现我的目标?

【问题讨论】:

  • 使用UITouch?使用 Sprite Kit 开发游戏时,应尽可能与 UIKit 类保持距离。我什至远离Timer
  • @ElTomato 你是说我不应该将库存实现为 UIViews,而是在游戏场景本身中实现它?
  • Sí,先生。这是一个糟糕的 Sprite Kit 游戏设计。
  • 嗯,这可以解释为什么我如此挣​​扎。 :) 感谢您的提示!
  • 如果您确实想使用 UIView 并拖动一些东西,而不是自己用平移手势跟踪拖动,您应该使用允许您设置预览的内置拖放机制拖动图像:-) 参见例如Adopting Drag and Drop in a Custom View

标签: ios swift uikit


【解决方案1】:

小答案是,如果您不在视图上调用 removeFromSuperview() 并将其作为子视图立即添加到控制器视图中,您可以保持手势工作,但这不是正确的方法,因为如果用户取消拖动,您将不得不再次重新添加到主视图,如果该视图您的拖动很重,它会很快变得迟钝和混乱

答案很长,在我看来是正确的做法,而苹果在所有拖放 api 中实际所做的是

您实际上可以对要拖动的视图进行快照并将其添加为包含所有视图的控制器视图的子视图,然后调用bringSubviewToFront(_ view: UIView) 以确保它是层次结构中最顶层的视图并传递在您为拖动视图拍摄的快照中

.began 中您可以隐藏原始视图,在.ended 中您可以再次显示它

.ended 上,您可以拍摄该快照并将其添加到放置视图中,也可以对其放置坐标执行任何其他操作

我做了一个示例项目来应用它 这是故事板设计和视图层次结构

这里是 ViewController 代码

class ViewController: UIViewController {
    
    @IBOutlet var topView: UIView!
    @IBOutlet var bottomView: UIView!
    
    @IBOutlet var smallView: UIView!
    
    var snapshotView: UIView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let pan = UIPanGestureRecognizer(target: self, action: #selector(panning(_:)))
        smallView.addGestureRecognizer(pan)
    }
    
    @objc private func panning(_ pan: UIPanGestureRecognizer) {
        switch pan.state {
        case .began:
            smallView.backgroundColor = .systemYellow
            snapshotView = smallView.snapshotView(afterScreenUpdates: true)
            smallView.backgroundColor = .white
            
            if let snapshotView = self.snapshotView {
                self.snapshotView = snapshotView
                view.addSubview(snapshotView)
                view.bringSubviewToFront(snapshotView)
                snapshotView.backgroundColor = .blue
                snapshotView.center = bottomView.convert(smallView.center, to: view)
            }
            
        case .changed:
            guard let snapshotView = snapshotView else {
                fallthrough
            }
            
            smallView.alpha = 0
            
            let translation = pan.translation(in: view)
            snapshotView.center = CGPoint(x: snapshotView.center.x + translation.x, y: snapshotView.center.y + translation.y)
            pan.setTranslation(.zero, in: view)
            
        case .ended:
            if let snapshotView = snapshotView {
                let frame = view.convert(snapshotView.frame, to: topView)
                if topView.frame.contains(frame) {
                    topView.addSubview(snapshotView)
                    snapshotView.frame = frame
                    
                    smallView.alpha = 1
                } else {
                    bottomView.addSubview(snapshotView)
                    let newFrame = view.convert(snapshotView.frame, to: bottomView)
                    snapshotView.frame = newFrame
                    UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseInOut]) {
                        snapshotView.frame = self.smallView.frame
                    } completion: { _ in
                        self.snapshotView?.removeFromSuperview()
                        self.snapshotView = nil
                        self.smallView.alpha = 1
                    }
                }
            }
            
        default: break
        }
    }
}

结局是这样的

【讨论】:

  • 调用addSubview 会自动将视图带到前面。将子视图放在前面只是将其移动到最后一个子视图。
  • 效果很好,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-06
  • 2020-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多