9

I'm new to swift and ios programming in general. I'm trying to display a modal view when my app first loads which it does. The problem I'm running into is that my modal keeps appearing over and over and over. Not sure where I'm going wrong.

BONUS QUESTION: Ultimately I'd like this to only happen the first time the user opens the app.

class ViewController: UIViewController {

    var introModalDidDisplay = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        showIntroModal()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func showIntroModal() {
        if (!introModalDidDisplay) {
            println(introModalDidDisplay)
            introModalDidDisplay = true
            let intro = self.storyboard?.instantiateViewControllerWithIdentifier("introModal") as IntroModalViewController
            intro.modalPresentationStyle = UIModalPresentationStyle.FormSheet
            self.presentViewController(intro, animated: true, completion: nil)
        }
    }
}
timT
  • 287
  • 1
  • 4
  • 15

3 Answers3

4

Found it. My "intro" class was extending ViewController rather than UIViewController...apparently that's bad. Thanks for the help! Sorry for the wild goose chase.

timT
  • 287
  • 1
  • 4
  • 15
0

When you close the modal view you show your ViewController view again, firing viewDidAppear once more and entering an infinite loop of showing your modal view, since the first view is always "appearing"

I'd suggest doing this in viewDidLoad, as the view is supposed to load only once. Try and experiment with these events and see when they are fired.

As for firing only once I'd suggest setting a flag in localStorage (plist) indicating whether it's the first time the user opens the app or not. For example set a flag in the first view's viewDidLoad and if that flag is false show your modal view and set the flag to true.

Here's a question about writing in plists in Swift: Save Data to .plist File in Swift

Community
  • 1
  • 1
martskins
  • 2,920
  • 4
  • 26
  • 52
  • Thanks for the comment! I tried viewDidLoad first and it threw an error because (I think) the view hadn't finished drawing so I couldn't add my modal to it. The comments I found when looking for a fix to that suggested moving things to viewDidAppear. – timT Oct 15 '14 at 18:43
  • Using the method I suggested of using a flag in localStorage would solve your problem just fine as it would only fire once. – martskins Oct 15 '14 at 18:49
0

A couple of observations:

  1. Are you saying that you're seeing this appear again and again while you're using the app? That would suggest that you have multiple instances of this view controller instantiated. For example, you might be doing a segue back to this view controller (which will create new instance) rather than unwinding/popping/dismissing back to it (which will return to the previous instance).

    I'd suggest you have a breakpoint or logging statement in viewDidLoad and confirm that you see this once and only once. If you're seeing it multiple times, that means that you have some circular reference amongst your storyboard scenes (and, BTW, you are abandoning memory, a type of leak).

  2. To handle this only presenting itself once between uses of the app, you need to save this introModalDidDisplay in some form of persistent storage. Often NSUserDefaults is used for this. For example, define introModalDidDisplay to look up the status in the standard user defaults:

    var introModalDidDisplay = NSUserDefaults.standardUserDefaults().boolForKey("introModalDidDisplay")
    

    Then your showIntroModal can update this setting in the user defaults:

    func showIntroModal() {
        if !introModalDidDisplay {
            introModalDidDisplay = true
            NSUserDefaults.standardUserDefaults().setBool(true, forKey: "introModalDidDisplay")
            NSUserDefaults.standardUserDefaults().synchronize()
    
            let intro = self.storyboard?.instantiateViewControllerWithIdentifier("introModal") as IntroModalViewController
            intro.modalPresentationStyle = UIModalPresentationStyle.FormSheet
            self.presentViewController(intro, animated: true, completion: nil)
        }
    }
    

    Clearly, you can use whatever persistent storage technique you want (plist, archive, user defaults, Core Data, SQLite), but the idea is the same: Retrieve the status from persistent storage and once the intro screen has been presented, update that persistent storage accordingly.

    By the way, by looking this up in persistent storage, we also fix the symptom of the problem I discussed in point #1. But you really want to fix the root cause of that first point, too, because otherwise you'll be leaking memory (if, of course, you're really instantiating multiple copies of the ViewController class).


By the way, looking ahead to the future, rather than storing just a boolean, I might suggest storing a version number identifier, too. That way, when you release version 2.0 of the app, you'll be able to decide whether the v1.0 users might see the updated intro screen again (or perhaps a custom one that focuses on what's new).

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • You're right, viewDidLoad is getting fired multiple times, but I'm not sure why. I don't have any segues in place. It seems to have something to do with this line: `self.presentViewController(intro, animated: true, completion: nil)` – timT Oct 16 '14 at 02:29
  • I would have guessed the problem was elsewhere. You must have segues or other occurrences of `presentViewController` that are presenting new instances of this view controller. It should be easy to diagnose: add breakpoint or log in `viewDidLoad` and then run the app and identify what sequence of actions results in new instance to be generated. Once you figure out where the new instance is being generated, it should be easy to look at the code and figure out why/how. – Rob Oct 16 '14 at 02:45
  • BTW, I assume the "intro" scene will return back to the main `ViewController` by calling `dismissViewControllerAnimated`, right? – Rob Oct 16 '14 at 03:03
  • Right, I have this function in "intro" that gets called on button press. `@IBAction func closePressed(sender: UIButton) { self.dismissViewControllerAnimated(true, completion: nil) }` – timT Oct 16 '14 at 03:06
  • It's gotta be something stupid. I really don't have much other code in this thing. If I comment out that `self.presentViewController(intro, animated: true, completion: nil)` line it only fires once. – timT Oct 16 '14 at 03:10
  • `viewDidAppear` will be called every time you come back, but `viewDidLoad` would only be called when a new instance is instantiated. At this point, I don't think I can advise anything else besides your zipping up the project and uploading it somewhere where we can take a look at it. – Rob Oct 16 '14 at 04:03