Вам необходимо сохранить ссылку на ограничения позиции, которые вы создаете для вида, чтобы вы могли либо изменить их значение constant
, либо установить isActive = false
перед созданием новых ограничений.
- Создайте подкласс
UIView
с именем MovableView
, который имеет NSLayoutConstraint?
свойства для horizontalConstraint
и verticalConstraint
.
- Установите эти свойства при создании ограничений перед установкой их
isActive = true
.
Если вам нужно переместить MovableView
, обновите свойство constant
для horizontalConstraint
и verticalConstraint
.
moveableView.horizontalConstraint?.constant = newXConstant
moveableView.verticalConstraint?.constant = newYConstant
Кроме того, вы можете установить 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
См. перетаскиваемые представления для реализации перетаскиваемых представлений, которые я написал 4 года назад. Он устарел и нуждается в обновлении, но вы можете получить некоторые идеи из него. Также прочитайте комментарии ниже, где @Matt объясняет, что вы можете пропустить использование ограничений для перетаскиваемого представления. Можно добавить на экран виды, которые не участвуют в Auto Layout , даже если остальная часть экрана это делает.
Я исправил твой код:
- Вы не предоставили реализацию
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
обратно на .zero
каждый раз, когда вызывается handlePan
, вы можете упростить код, не отслеживая начальную позицию:
@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)")
} else if gestureRecognizer.state == .changed {
let translation = gestureRecognizer.translation(in: self.view)
currentView.horizontalConstraint?.constant += translation.x
currentView.verticalConstraint?.constant += translation.y
gestureRecognizer.setTranslation(.zero, in: self.view)
}
}