I have a black separatorView in the middle of the screen separating a topContainerView (orange) and a bottomContainerView (green). The separatorView can be dragged up and down with a panGesture, but I can't get the top and bottom views to update their constraints and resize. The bottom of the orange view and the top of the green view should always stick with the separatorView.
Here's the code I have (UPDATED to include variable declarations):
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
}
When you are using Autolayout you can't simply modify the frame of elements (which is what you are doing implicitly by changing the center
property). Well, you can, but it won't affect any other elements (as you have found) and as soon as Autolayout is triggered your changes to the frame will be reset.
You need to manipulate the constraints so that Autolayout produces the result you want.
In this case you need to modify the constant
property of your constraint that ties the separator to the center of the view. You can use the translation value of the pan gesture recognizer to do this. The only tricky bit is that this translation is relative to 0 at the start of the pan, so you need to incorporate any offset from any previous pan.
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With