Обновление ограничений с помощью UIPanGestureRecognizer

У меня есть черный separatorView в середине экрана, разделяющий topContainerView (оранжевый) и bottomContainerView (зеленый). SeparatorView можно перетаскивать вверх и вниз с помощью panGesture, но я не могу заставить верхний и нижний представления обновлять свои ограничения и изменять размер. Нижняя часть оранжевого представления и верхняя часть зеленого представления всегда должны соответствовать separatorView.

Вот код, который у меня есть (ОБНОВЛЕНО, чтобы включить объявления переменных):

let separatorView: UIView = {
    let view = UIView()
    view.backgroundColor = UIColor.black
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

let topContainerView : UIView = {
    let view = UIView()
    view.backgroundColor = UIColor.orange
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

let bottomContainerView : UIView = {
    let view = UIView()
    view.backgroundColor = UIColor.green
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

override func viewDidLoad() {
        super.viewDidLoad()
        self.addViews()
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(detectPan(recognizer:)))
        panGesture.delaysTouchesBegan = false
        panGesture.delaysTouchesEnded = false
        separatorView.addGestureRecognizer(panGesture)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func addViews() {
        
        view.addSubview(topContainerView)
        view.addSubview(bottomContainerView)
        view.addSubview(separatorView)
        
        separatorView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        separatorView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        separatorView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        separatorView.heightAnchor.constraint(equalToConstant: 50).isActive = true
        
        topContainerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        topContainerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        topContainerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        topContainerView.bottomAnchor.constraint(equalTo: separatorView.topAnchor).isActive = true
        
        bottomContainerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        bottomContainerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        bottomContainerView.topAnchor.constraint(equalTo: separatorView.bottomAnchor).isActive = true
        bottomContainerView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    }
    
    func detectPan(recognizer: UIPanGestureRecognizer) {
        let translation = recognizer.translation(in: self.view)
        separatorView.center = CGPoint(x: view.center.x, y: lastLocation.y + translation.y)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.bringSubview(toFront: separatorView)
        lastLocation = separatorView.center
    }

введите здесь описание изображения

введите здесь описание изображения


person kerbelda    schedule 06.02.2017    source источник
comment
Вы установили свои представления translatesAutoresizingMaskIntoConstraints на false?   -  person Honey    schedule 07.02.2017
comment
Я сделал, для каждого просмотра   -  person kerbelda    schedule 07.02.2017
comment
где ты это сделал? я не вижу этого в вашем коде   -  person Honey    schedule 07.02.2017
comment
Вы пытались изменить макет подвидов? view.layoutIfNeeded. Ваш код выглядит хорошо. Но похоже, что другие представления не требуют повторного размещения.   -  person dfd    schedule 07.02.2017
comment
Просто обновил код, чтобы показать эти объявленные переменные. Уже пробовал поместить view.layoutIfNeeded в detectPan и touchesBegan, ничего не изменилось.   -  person kerbelda    schedule 07.02.2017
comment
Не изменяйте свойство представления разделителя center, изменяйте свойство constanf ограничения CenterY разделителя. Autolayout сделает все остальное.   -  person Paulw11    schedule 07.02.2017
comment
Еще раз - в вашем detectPan вы пытались обновить макет?   -  person dfd    schedule 07.02.2017
comment
@dfd, который не будет работать, поскольку Autolayout просто вернет разделитель туда, где, согласно ограничениям, он должен быть; в центре. Когда вы используете автомакет, вы не можете напрямую возиться с фреймом; вы должны манипулировать ограничениями   -  person Paulw11    schedule 07.02.2017


Ответы (1)


Когда вы используете Autolayout, вы не можете просто изменить рамку элементов (это то, что вы делаете неявно, изменяя свойство center). Ну, вы можете, но это не повлияет на другие элементы (как вы обнаружили), и как только Autolayout сработает, ваши изменения в кадре будут сброшены.

Вам нужно манипулировать ограничениями, чтобы Autolayout давал желаемый результат.

В этом случае вам нужно изменить свойство constant вашего ограничения, которое связывает разделитель с центром представления. Для этого можно использовать значение перевода распознавателя жестов панорамирования. Единственная хитрость заключается в том, что это смещение относительно 0 в начале панорамирования, поэтому вам нужно включить любое смещение от любого предыдущего панорамирования.

let separatorView: UIView = {
    let view = UIView()
    view.backgroundColor = UIColor.black
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

let topContainerView : UIView = {
    let view = UIView()
    view.backgroundColor = UIColor.orange
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

let bottomContainerView : UIView = {
    let view = UIView()
    view.backgroundColor = UIColor.green
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

var centerConstraint: NSLayoutConstraint!

var startingConstant: CGFloat  = 0.0

override func viewDidLoad() {
    super.viewDidLoad()
    self.addViews()
    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(detectPan(recognizer:)))
    panGesture.delaysTouchesBegan = false
    panGesture.delaysTouchesEnded = false
    separatorView.addGestureRecognizer(panGesture)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func addViews() {

    view.addSubview(topContainerView)
    view.addSubview(bottomContainerView)
    view.addSubview(separatorView)

    separatorView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    separatorView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true

    self.centerConstraint = separatorView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
    self.centerConstraint.isActive = true

    separatorView.heightAnchor.constraint(equalToConstant: 50).isActive = true

    topContainerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    topContainerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
    topContainerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    topContainerView.bottomAnchor.constraint(equalTo: separatorView.topAnchor).isActive = true

    bottomContainerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    bottomContainerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
    bottomContainerView.topAnchor.constraint(equalTo: separatorView.bottomAnchor).isActive = true
    bottomContainerView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}

func detectPan(recognizer: UIPanGestureRecognizer) {

    switch recognizer.state {
    case .began:
        self.startingConstant = self.centerConstraint.constant
    case .changed:
        let translation = recognizer.translation(in: self.view)
        self.centerConstraint.constant = self.startingConstant + translation.y
    default:
        break
    }
}
person Paulw11    schedule 06.02.2017
comment
Да, это то, что я в итоге сделал. Изменение центра ничего не обновляло. Пришлось изменить константу centerYAnchor. Спасибо! - person kerbelda; 07.02.2017
comment
О, это потрясающе! Спасибо! В моем случае я просто присвоил значение recognizer.translation(in: self.view).y в качестве постоянного значения ограничения и все время задавался вопросом, почему мое представление прыгает. - person Baran Emre; 06.08.2019