I've created a minimum working example of the issue on https://github.com/rDivaDuck/TestAppError
My HomeController is a UICollectioviewController and I have a settings launcher class that I'm trying to animate up from the bottom on button press from the navigation bar, the code I have achieves this by deactivating a down constraint and activating an up constraint and then calling window.layoutifneeded() to animate the change. However, I'm still thrown a typical constraint conflict error that the down constraint is still active.
I can achieve the effect fine using .frame and animating a CGRect change but I want to get the same effect using autolayout.
class SettingsLauncher {
var homeController: HomeController?
let shadowView = UIView()
let MenuView: UIView = {
let View = UIView(frame: .zero)
View.backgroundColor = UIColor.white
View.translatesAutoresizingMaskIntoConstraints = false
return View
}()
var downConstraint: NSLayoutConstraint?
var upConstraint: NSLayoutConstraint?
func showSettings() {
if let window = UIApplication.shared.keyWindow{
shadowView.backgroundColor = UIColor(white: 0,
alpha: 0.4)
shadowView.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(settingsDown)))
window.addSubview(shadowView)
shadowView.frame = window.frame
shadowView.alpha = 0
window.addSubview(MenuView)
downConstraint = MenuView.topAnchor.constraint(equalTo: window.bottomAnchor)
downConstraint?.isActive = true
upConstraint = MenuView.topAnchor.constraint(equalTo: window.bottomAnchor, constant: -300)
MenuView.heightAnchor.constraint(equalToConstant: 300).isActive = true
MenuView.leadingAnchor.constraint(equalTo: window.leadingAnchor).isActive = true
MenuView.trailingAnchor.constraint(equalTo: window.trailingAnchor).isActive = true
MenuView.layoutIfNeeded()
window.layoutIfNeeded()
settingsUp()
}
}
func settingsUp() {
downConstraint?.isActive = false
upConstraint?.isActive = true
UIView.animate(withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: .curveEaseOut,
animations: {
self.shadowView.alpha = 1
if let window = UIApplication.shared.keyWindow {
window.layoutIfNeeded()
}
}, completion: nil)
}
@objc func settingsDown() {
downConstraint?.isActive = true
upConstraint?.isActive = false
UIView.animate(withDuration: 0.5,
animations: {
self.shadowView.alpha = 0
if let window = UIApplication.shared.keyWindow {
window.layoutIfNeeded()
}
}, completion: nil )
}
}
(
"<NSLayoutConstraint:0x600001b5e440 V:[UIWindow:0x7fde18514030]-(-300)-[UIView:0x7fde18525ea0] (active)>",
"<NSLayoutConstraint:0x600001b5f250 V:[UIWindow:0x7fde18514030]-(0)-[UIView:0x7fde18525ea0] (active)>"
)
How can I fix this?
Hello and welcome to StackOverflow, there are multiple errors in your project. The main problem is that every time you are tapping the button and calling showSettings()
your adding new constraints to MenuView that are in conflict with the previous ones so to fix this problem you should execute the constraint part of the code only once:
var viewInitialized = false
func showSettings() {
if let window = UIApplication.shared.keyWindow {
if !viewInitialized {
viewInitialized = true
shadowView.backgroundColor = UIColor(white: 0, alpha: 0.4)
shadowView.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(settingsDown)))
window.addSubview(shadowView)
shadowView.frame = window.frame
shadowView.alpha = 0
window.addSubview(MenuView)
downConstraint = MenuView.topAnchor.constraint(equalTo: window.bottomAnchor)
downConstraint?.isActive = true
upConstraint = MenuView.topAnchor.constraint(equalTo: window.bottomAnchor, constant: -300)
MenuView.heightAnchor.constraint(equalToConstant: 300).isActive = true
MenuView.leadingAnchor.constraint(equalTo: window.leadingAnchor).isActive = true
MenuView.trailingAnchor.constraint(equalTo: window.trailingAnchor).isActive = true
MenuView.layoutIfNeeded()
window.layoutIfNeeded()
}
settingsUp()
}
}
Also in settingsDown()
switch the activations statement so you deactivate the constraint before activating the other, sometimes this causes a warning.
@objc func settingsDown() {
upConstraint?.isActive = false
downConstraint?.isActive = true
...
The other problems of the project that I found:
AppDelegate.swift
why are you creating a new window? There is already one and you can use it. Just delete this line of code that is useless window = UIWindow(frame: UIScreen.main.bounds)
AppDelegate.swift
? add it to the storyboard as the first view controller. Same thing for the HomeController
add a CollectionViewController to the storyboard and change its class to HomeController
SettingsLauncher.swift
shadowView
is missing the constraints