如何使用平移手势识别器更改不同视图的布局约束?

时间:2019-01-18 02:49:42

标签: ios swift uigesturerecognizer

我有一个函数,每次按下按钮时,都会在主视图中添加一个新的UIView。我希望每个视图都是可拖动的。目前,我正在通过向每个视图添加平移手势识别器来完成此操作,这会更改每个视图的布局约束。

我的问题是,在移动每个视图之前,我没有正确禁用它们的原始约束,因此新约束和旧约束发生冲突。

这是我的代码:

import UIKit

class ViewController5: UIViewController, UINavigationControllerDelegate, UIGestureRecognizerDelegate {

var textPosXConstraint = NSLayoutConstraint()
var textPosYConstraint = NSLayoutConstraint()

var numberOfViews = 0

var startingConstantPosX: CGFloat  = 0.0
var startingConstantPosY: CGFloat  = 100.0

override func viewDidLoad() {
    super.viewDidLoad()

    navigationItem.title = "edit views"
    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "add", style: .plain, target: self, action: #selector(addView))
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearThis))
}

@objc func addView() {

    let view1 = UIView()
    view1.backgroundColor = UIColor.yellow
    view1.frame = CGRect()
    view.addSubview(view1)
    view1.translatesAutoresizingMaskIntoConstraints = false

    view1.widthAnchor.constraint(equalToConstant: 80).isActive = true
    view1.heightAnchor.constraint(equalToConstant: 80).isActive = true

    textPosXConstraint = view1.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0)
    textPosXConstraint.isActive = true

    textPosYConstraint = view1.centerYAnchor.constraint(equalTo: view.topAnchor, constant: 100)
    textPosYConstraint.isActive = true

    numberOfViews+=1
    view1.tag = numberOfViews

    //add pan gesture
    let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
    panGestureRecognizer.delegate = self
    view1.addGestureRecognizer(panGestureRecognizer)
}

@objc func clearThis() {
    for view in view.subviews {
        view.removeFromSuperview()
    }
    numberOfViews = 0
}

@objc func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {

    guard let currentView = gestureRecognizer.view else { return }

    if gestureRecognizer.state == .began {

        print("view being moved is: \(currentView.tag)")

        startingConstantPosY = textPosYConstraint.constant
        startingConstantPosX = textPosXConstraint.constant

    } else if gestureRecognizer.state == .changed {

        let translation = gestureRecognizer.translation(in: self.view)

        let newXConstant = self.startingConstantPosX + translation.x
        let newYConstant = self.startingConstantPosY + translation.y

        currentView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: startingConstantPosX).isActive = false
        currentView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: newXConstant).isActive = true

    }
}
}

理想情况下,它将起作用,因此用户可以根据需要添加任意数量的视图并四处移动。

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

您需要保留为视图创建的位置约束的引用,以便可以在创建新约束之前更改其constant的值或设置isActive = false

  1. 创建名为UIView的{​​{1}}的子类,该子类具有MovableViewNSLayoutConstraint?的{​​{1}}属性。
  2. 在创建约束之前先设置这些属性,然后再设置约束horizontalConstraint
  3. 当您需要移动verticalConstraint时,请更新isActive = trueMovableView的{​​{1}}属性。

    constant

    或者,您可以在创建,分配和激活新约束之前设置horizontalConstraintverticalConstraint

    moveableView.horizontalConstraint?.constant = newXConstant
    moveableView.verticalConstraint?.constant = newYConstant
    

有关四年前我写的可拖动视图的实现,请参见draggable views。它已经过时,需要更新,但是您可能会从中得到一些想法。另请阅读其下方的注释,其中@Matt解释您可以跳过对可拖动视图使用约束的操作。即使屏幕的其余部分也可以添加不参与 Auto Layout 的视图到屏幕。


我修复了您的代码:

  • 您没有提供horizontalConstraint?.isActive = false的实现,但是约束属性似乎是verticalConstraint?.isActive = false而不是成员属性。您需要将它们作为成员属性,以支持多个可移动视图。
  • 您交换了moveableView.horizontalConstraint?.isActive = false moveableView.verticalConstraint?.isActive = false moveableView.horizontalConstraint = ... moveableView.horizontalConstraint?.isActive = true moveableView.verticalConstraint = ... moveableView.verticalConstraint?.isActive = true MovableView,这就是造成跳跃的原因。

static

如果每次调用startingConstantPosX时都将startingConstantPosY设置回class MovableView: UIView { var horizontalConstraint: NSLayoutConstraint? var verticalConstraint: NSLayoutConstraint? } class ViewController: UIViewController, UINavigationControllerDelegate, UIGestureRecognizerDelegate { var numberOfViews = 0 var startingConstantPosX: CGFloat = 0.0 var startingConstantPosY: CGFloat = 100.0 override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "edit views" navigationItem.rightBarButtonItem = UIBarButtonItem(title: "add", style: .plain, target: self, action: #selector(addView)) navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Clear", style: .plain, target: self, action: #selector(clearThis)) } @objc func addView() { let view1 = MovableView() view1.backgroundColor = UIColor.yellow view1.frame = CGRect() view.addSubview(view1) view1.translatesAutoresizingMaskIntoConstraints = false view1.widthAnchor.constraint(equalToConstant: 80).isActive = true view1.heightAnchor.constraint(equalToConstant: 80).isActive = true view1.horizontalConstraint = view1.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0) view1.horizontalConstraint?.isActive = true view1.verticalConstraint = view1.centerYAnchor.constraint(equalTo: view.topAnchor, constant: 100) view1.verticalConstraint?.isActive = true numberOfViews+=1 view1.tag = numberOfViews //add pan gesture let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan)) panGestureRecognizer.delegate = self view1.addGestureRecognizer(panGestureRecognizer) } @objc func clearThis() { for view in view.subviews { view.removeFromSuperview() } numberOfViews = 0 } @objc func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) { guard let currentView = gestureRecognizer.view as? MovableView else { return } if gestureRecognizer.state == .began { print("view being moved is: \(currentView.tag)") startingConstantPosX = currentView.horizontalConstraint?.constant ?? 0 startingConstantPosY = currentView.verticalConstraint?.constant ?? 0 } else if gestureRecognizer.state == .changed { let translation = gestureRecognizer.translation(in: self.view) let newXConstant = startingConstantPosX + translation.x let newYConstant = startingConstantPosY + translation.y currentView.horizontalConstraint?.constant = newXConstant currentView.verticalConstraint?.constant = newYConstant } } } ,则无需跟踪起始位置就可以简化代码:

translation