2

I recently converted an app from modal viewControllers to TabBarController and there is some code that used to work fine before the conversion but now I'm having a hard time making it work in the TabBar app.

What I want to do is update the UIin a tab based on user setting stored in UserDefaults, these settings happen in the Settings tab (ViewController) and I want to update the UI in a different tab based on the settings that were set by the user. Before the conversion the code was in the ViewDidLoad and it was working fine but now with TabBar there is no ViewDidLoad I'm using viewWillAppear, to update as soon as the tab is activated but isn't working.

Here is the code...

class UserInputViewController: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()
        // customizeApp() // everything works if called from here
    }

    override func viewWillAppear(_ animated: Bool) {
        customizeApp() // Doesn't work
    }
    override func viewDidAppear(_ animated: Bool) {
        // customizeApp() // Doesn't work
    }

     func customizeApp(){

        if isView1(){
            view1.isHidden = false
        }else{
            view1.isHidden = true
        }

        if isButton1(){
            button1.isHidden = false
        }else{
            button1.isHidden = true
        }

        if isTextField1(){
            textField1.isHidden = false
        }else{
            textField1.isHidden = true
        }

        if isTextField2(){
            textField2.isHidden = false
        }else{
            textField2.isHidden = true
        }

        /// Adjust Screen based on settings
        if textField1.isHidden && textField2.isHidden{
            constraintMainButton.constant = 7
            return
        }
        if textField1.isHidden {
            constraintMainButton.constant = 45
        }
        if textField2.isHidden {
            constraintMainButton.constant = 45
        }
    }

    func isView1()->Bool{// read UserDefaults}
    func isButton1()->Bool{// read UserDefaults}
    func isTextField1()->Bool{// read UserDefaults}
    func isTextField2()->Bool{// read UserDefaults}
}

I also tried viewDidLayoutSubviews but it didn't work either.

Any idea what could be wrong?

FYI - All values come as expected from UserDefaults.

EDIT:

My Issue was that I was repositioning my views but I wasn't repositioning them back to their original position. I know it sounds silly but I was tricked by the fact that the exact same code was working fine when I was calling customizeApp() from ViewDidLoad before changing to UITabBar. I ended up calling customizeApp() from viewDidAppear() method.

 class UserInputViewController: UIViewController{

    /// ... Code

    override func viewDidAppear(_ animated: Bool) {
        customizeApp()
    }

     func customizeApp(){

        /// ... Code

        /// Adjust Screen based on settings
        if textField1.isHidden && textField2.isHidden{
            constraintMainButton.constant = 7
            return
        }
        if textField1.isHidden {
            constraintMainButton.constant = 45
            return // added
        }
        if textField2.isHidden {
            constraintMainButton.constant = 45
            return // added
        }

        constraintMainButton.constant = 83 // added: Original Position
    }
    /// .... Code
}

Thanks

fs_tigre
  • 10,650
  • 13
  • 73
  • 146

2 Answers2

2

From Apple Documentation

As @deadbeef says call super in viewWillAppear and others.

Process of loading and displaying your view controller’s views

Storyboards make the process of loading and displaying your view controller’s views very simple. UIKit automatically loads views from your storyboard file when they are needed. As part of the loading process, UIKit performs the following sequence of tasks:

  1. Instantiates views using the information in your storyboard file.

  2. Connects all outlets and actions.

  3. Assigns the root view to the view controller’s view property.

  4. Calls the view controller’s awakeFromNib method.

    When this method is called, the view controller’s trait collection is empty and views may not be in their final positions.

  5. Calls the view controller’s viewDidLoad method.

Use viewDidLoad method to add or remove views, modify layout constraints, and load data for your views. Usually override this method to perform additional initialization on views that were loaded from nib files.

Before displaying a view controller’s views onscreen, UIKit gives you some additional chances to prepare those views before and after they are onscreen. Specifically, UIKit performs the following sequence of tasks:

  1. Calls the view controller’s viewWillAppear: method to let it know that its views are about to appear onscreen.
  2. Updates the layout of the views.
  3. Displays the views onscreen.
  4. Calls the viewDidAppear: method when the views are onscreen.

When you add, remove, or modify the size or position of views, remember to add and remove any constraints that apply to those views. Making layout-related changes to your view hierarchy causes UIKit to mark the layout as dirty. During the next update cycle, the layout engine computes the size and position of views using the current layout constraints and applies those changes to the view hierarchy.

Community
  • 1
  • 1
Dharma
  • 3,007
  • 3
  • 23
  • 38
2

Note that "it doesn't work" is really un-helpful. You need to describe what your code does or doesn't do, in detail, if you want help.

There are several things wrong with your code.

In viewDidLoad(), viewWillAppear(_:), and viewWillDisappear(_:)you must always call super.

Updating the constant on a constraint doesn't do anything until next time the view's layout is updated.

When you change your layout constraints in viewDidLoad(), it takes place before the layout has been finalized, so the changes are applied in the next layout pass.

If you want to make changes to constraints, you need to call layoutIfNeeded(). Try adding this to your customizeApp() function:

func customizeApp() {
  defer {
     self.view.layoutIfNeeded()
  }
}
Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Ok, turned out that my problem was that I was repositioning my views but I wasn't repositioning them back to its original position. I know it sounds silly but I was tricked by the fact that the exact same code was working fine when I was calling `customizeApp()` from `ViewDidLoad` but thanks to your explanation I was able to find the problem. FYI - I didn't need to call `self.view.layoutIfNeeded()` I just called `customizeApp()` from `viewDidAppear` and repositioned my views to their original position by adjusting their constraints. Thanks a lot for your help! – fs_tigre Jun 22 '17 at 22:28