我有一个函数,每次按下按钮时,都会在主视图中添加一个新的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
}
}
}
理想情况下,它将起作用,因此用户可以根据需要添加任意数量的视图并四处移动。
感谢您的帮助!
答案 0 :(得分:1)
您需要保留为视图创建的位置约束的引用,以便可以在创建新约束之前更改其constant
的值或设置isActive = false
。
UIView
的{{1}}的子类,该子类具有MovableView
和NSLayoutConstraint?
的{{1}}属性。horizontalConstraint
。当您需要移动verticalConstraint
时,请更新isActive = true
和MovableView
的{{1}}属性。
constant
或者,您可以在创建,分配和激活新约束之前设置horizontalConstraint
和verticalConstraint
。
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