Как я могу изменить ограничения макета для разных представлений, используя распознаватель жестов панорамирования? - PullRequest
0 голосов
/ 18 января 2019

У меня есть функция, которая добавляет новый 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 голосов
/ 18 января 2019

Вам необходимо сохранить ссылку на ограничения позиции, которые вы создаете для вида, чтобы вы могли либо изменить их значение constant, либо установить isActive = false перед созданием новых ограничений.

  1. Создайте подкласс UIView с именем MovableView, который имеет NSLayoutConstraint? свойства для horizontalConstraint и verticalConstraint.
  2. Установите эти свойства при создании ограничений перед установкой их isActive = true.
  3. Если вам нужно переместить 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)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...