1

I have a UIViewController with a UILabel that needs to display either "lbs" or "kg". My app has a settings screen (another UIViewController) that is presented modally over the first view controller and the user can select either of the two units and save their preference. If the units are changed and the modal settings screen is dismissed, I of course want the label on the first view controller to be updated with the new units value (but without refreshing the whole view). I thought I knew how to make it work, but evidently I don't.

On my modal settings screen, I have a UISegmentedControl to allow the user to select units. Anytime it's changed, this function updates userDefaults:

  func saveUnitsSelection() {
    if unitsControl.selectedSegmentIndex == 0 {
      UserDefaultsManager.sharedInstance.preferredUnits = Units.pounds.rawValue
    } else {
      UserDefaultsManager.sharedInstance.preferredUnits = Units.kilograms.rawValue
    }
  }

Then they would likely dismiss the settings screen. So, I added this to viewDidLoad in my first view controller:

override func viewDidLoad() {
  super.viewDidLoad()

  let preferredUnits = UserDefaultsManager.sharedInstance.preferredUnits
  units.text = preferredUnits 
}

That didn't work, so I moved it to viewWillAppear() and that didn't work either. I did some research and some caveman debugging and found out that neither of those functions is called after the view has been loaded/presented the first time. It seems that viewWillAppear will be called a second time if I'm working within a hierarchy of UITableViewControllers managed by a UINavigationController, but isn't called when I dismiss my modal UIViewController to reveal the UIViewController underneath it.

Edit 1: Here's the view hierarchy I'm working with: enter image description here

I'm kinda stuck at this point and not sure what to try next.

Edit 2: The user can tap a 'Done' button in the navigation bar and when they do, the dismissSettings() function dismisses the Settings view:

class SettingsViewController: UITableViewController {

  let preferredUnits = UserDefaultsManager.sharedInstance.preferredUnits
   // some other variables set here

  override func viewDidLoad() {
    super.viewDidLoad()

    self.navigationController?.navigationBar.topItem?.title = "Settings"

    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .Plain, target: self, action: #selector(self.dismissSettings(_:)))

    if preferredUnits == Units.pounds.rawValue {
      unitsControl.selectedSegmentIndex = 0
     } else {
       unitsControl.selectedSegmentIndex = 1
     }
   }

  func dismissSettings(sender: AnyObject?) {
    navigationController?.dismissViewControllerAnimated(true, completion: nil)

  }
}
BaseZen
  • 8,650
  • 3
  • 35
  • 47
Jim
  • 1,260
  • 15
  • 37
  • Where did you add `func viewDidLoad()`? If it was in a view controller, Xcode should complain that you didn't declare `override`, and you should call super. I wonder if you're adding the code in the wrong class. – Aaron Brager Aug 05 '16 at 02:13
  • Also, can you describe, very specifically, the view hierarchy of the underneath view controller? Is it within a nav and/or tab controller? – Aaron Brager Aug 05 '16 at 02:14
  • Sorry about that, @AaronBrager. The `viewDidLoad` is in the first view controller. And I mistakenly deleted the `override` and `super.viewDidLoad` when I removed a bunch of superfluous code. I've corrected that above. I also added a screenshot of the view hierarchy. – Jim Aug 05 '16 at 02:29

1 Answers1

0

THE REAL PROBLEM

You misspelled viewWillAppear. You called it:

func viewWillAppear()

As far as Cocoa Touch is concerned, this is a random irrelevant function that hooks into nothing. You meant:

override func viewWillAppear(animated: Bool)

The full name of the first function is: "viewWillAppear"

The full name of the second function is: "viewWillAppear:animated"

Once you get used to this, the extreme method "overloading" that Cocoa Touch uses gets easier.

This is very different in other languages where you might at least get a warning.

The other lesson that everyone needs to learn when posting a question is: Include All Related Code!

Useful logging function I use instead of print or NSLog, to help find these things:

class Util {
    static func log(message: String, sourceAbsolutePath: String = #file, line: Int = #line, function: String = #function, category: String = "General") {
        let threadType = NSThread.currentThread().isMainThread ? "main" : "other"
        let baseName = (NSURL(fileURLWithPath: sourceAbsolutePath).lastPathComponent! as NSString).stringByDeletingPathExtension ?? "UNKNOWN_FILE"
        print("\(NSDate()) \(threadType) \(baseName) \(function)[\(line)]: \(message)")
    }
}

[Remaining previous discussion removed as it was incorrect guesses]

BaseZen
  • 8,650
  • 3
  • 35
  • 47
  • You're right it's a Show (e.g. Push) segue between the initial view controller (Calculator) and the navigation controller, but I promise, it's coming up from the bottom and covering the entire view when the Settings button is tapped and moving down to disappear when the Done button is tapped. To troubleshoot, I put "print("I made it to viewWillAppear()" inside of `viewWillAppear()' of the initial view controller and it doesn't print a 2nd time when Settings is dismissed. How did you print those events like that? I'll try that and maybe that will tell me something. – Jim Aug 05 '16 at 05:13
  • But I still need to see your dismiss code -- your analog of what I have. – BaseZen Aug 05 '16 at 05:17
  • And my dismiss code is different than yours, so have you tried it? – BaseZen Aug 05 '16 at 05:23
  • Oops, I didn't catch that difference in the dismiss code. I've changed it now in Edit 2. Unfortunately, behavior is still the same. – Jim Aug 05 '16 at 05:28
  • Works perfectly for me copying your code verbatim. I'd be curious to see where the problem lies. Corrupted Storyboard perhaps. Do a "Clean" / Build? Can you upload your entire project somewhere? – BaseZen Aug 05 '16 at 05:56
  • Here's a link: https://github.com/JCMcLovin/onerepmax/tree/upload-to-github Also, I did clean and build, but no change in behavior. When you run it, take note of the units label on the first scene, tap Settings in the upper left, change the units on the Settings scene then tap Done. You'll see the label doesn't change and you see that viewDidLoad() doesn't get called a second time. Thanks very much for taking the time to look at this for me. – Jim Aug 05 '16 at 13:12
  • Found it from your github, see new solution – BaseZen Aug 06 '16 at 00:06
  • Wow, I don't know if I ever would have caught that. Thank you. About that debug utility, I happened to grab that code before you deleted it so I could try it myself because it looks very useful. But I got some unresolved identifier errors in Util and "Use of an instance member 'log' on Util" in ObservingVC. I put it back on github in its now disabled state. I know it's more than I should ask but if you feel like it, would you take 2 min to look and tell me where I'm going wrong? I'm very grateful for your help with the above either way. – Jim Aug 06 '16 at 00:24
  • Logging function added back to answer. Somehow your func signature got garbled there too. Not sure if that was you or me. – BaseZen Aug 06 '16 at 00:32