Constraints resets when app is going in background - iOS 13
Asked Answered
C

7

25

I have set a view's leading, trailing constraints normally. I have set its height to static 325. And for bottom constraint I have set 2 constraints 1. with main view's bottom constraint to view's bottom constraint. 2. with main view's bottom constraint to view's top constraint. Now on user's action I just show hide view with animation. So when view gets displayed on the screen and the app goes in background then the view's constraint automatically gets altered and the view gets hidden. This issue is only occurring in iOS 13 devices.

I tried to update its constraints on viewWillAppear() but in iOS 13 the viewWillAppear of ViewControllers is also not called when app is activated from background. Also I don't think , that this is a good idea to update constraints.

Storyboard

Class File

class ViewController: UIViewController {

    @IBOutlet weak var topConstraint: NSLayoutConstraint!
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
            self.topConstraint.isActive = false
            self.bottomConstraint.isActive = true
            UIView.animate(withDuration: 0.3) {
                self.view.layoutIfNeeded()
            }
        }
    }
}

I don't want my constraints to be changed or updated when app state changes from foreground to background and vice versa.

Please help me with the same.

TIA

Copper answered 14/10, 2019 at 12:3 Comment(3)
You are mixing constraints with explicit frame settings, which will usually result in problems. Not quite clear what you want... When your view loads, you want to position contentView below the bottom (so it's "off-screen"), then you want to animate it moving up into view... Then, app goes to background, and you want the view to still be showing when app comes back to foreground? No new animation?Overelaborate
Yes. I want like this only. Actually the code that I've written in viewWillAppear() that is executed on user's action. But for explanation purpose I've created one demo.Copper
Assuming your animation position / sizing is working correctly with constraints only (no explicit .frame = ... statements), then your code should be working fine. ViewDidLoad() should not be called when the app transitions from background to foreground, unless you have some other code running on that event.Overelaborate
O
24

Also met this issue. Noticed that constraints keep reseting in case if they are not checked Installed in Interface Builder. So, as workaround keep all constraints Installed in IB and change isActive state just in code.

enter image description here

Oringa answered 30/10, 2019 at 20:44 Comment(3)
This actually didn't work for me, because if I set isActive to false, then when the app goes to the background and back, it would come back active.Latrell
This fixed my issue where when the user changes to dark mode the whole layout would go crazy.Lenee
This didn't work for me either - in addition to conflicting constraints in the storyboard, later some of the constraints were nil (due to conflicts, I think). What worked was setting them inside viewDidLayoutSubviews.Rasputin
L
12

The workaround that worked for me was to listen for UIApplication.willEnterForegroundNotification, and set isActive on all constraints at this point.

This notification seems to be published after the system (incorrectly) resets all constraints to their IB states, and before the app becomes visible again. So it's the perfect time to fix the constraints.

One caveat: when your constraints are contained in an autosizing table view cell, this notification seems to be triggered after the system calculates the height of the cell. So, you will need to trigger the table view to recalculate cell heights after updating your constraints (see this question for info on how to do that).

Latrell answered 20/4, 2020 at 22:7 Comment(0)
J
10

I found the issue is happening only in iOS 13.0 and above, Please try to make the constraint changes in ViewDidLayoutSubviews

      override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
            self.bottomConstraint.isActive = false
            self.topConstraint.isActive = true
    self.view.layoutIfNeeded()
}
Janson answered 16/10, 2019 at 23:45 Comment(1)
It worked like a charm. But why it is happening in iOS 13.0 only? & why viewWillAppear is not getting called?Waterage
J
4

You can achieve the solution using setting the priority and flipping the values.

 if !isShowing{
       self.bottomConstraint.priority = UILayoutPriority(rawValue: 250)
       self.topConstraint.priority = UILayoutPriority(rawValue: 750)
   }else{
       self.bottomConstraint.priority = UILayoutPriority(rawValue: 750)
       self.topConstraint.priority = UILayoutPriority(rawValue: 200)
   }
   isShowing = !isShowing
   self.subview.layoutIfNeeded()
Janson answered 16/10, 2019 at 23:57 Comment(0)
L
1

if you need to know "how can i inform when the apps comes background to foreground". You should use NotificationCenter to send a signal to all. But this is a not good practise.

Maybe you can tell your aim, and we can give you differ approach.

Leu answered 16/10, 2019 at 19:9 Comment(0)
S
0

I also had a similar problem whenever I try to update the constant of a constraint that have variations set on storyboard. To solve this, simply delete all variations and set them programmatically.

Variation screenshot

Substitutive answered 5/12, 2021 at 19:23 Comment(0)
C
-1

Well I got the solution. Actually, we can’t say it the solution. I just run the same code in iOS 13.1 or higher version devices and the issue was gone. So for now I can say this issue only occurs in iOS 13.0.

Copper answered 31/10, 2019 at 3:48 Comment(1)
I'm afraid your answer is not correct. I can confirm that this issue still exists on iOS 15Fibre

© 2022 - 2024 — McMap. All rights reserved.