I have two scenes from where the user can enter information (one is actually scanning a barcode, the other enter the number manually)
From there, I want to download information from my backend.
Depending on what I get back, I want to go to one or another scene.
Here is a sample of my storyboard:
I don't know how to handle the loading part. My first idea was to have a scene with only a spinner and download the content from here. Once I get it I can choose to which scene I want to go to and it's done. But...
When the user clicks the "Back" button from the scenes on the right, he goes back to the "loading" scene instead of the last "logical" scene (the input scene).
So my question is two-fold:
Do I handle the download correctly, by using a scene just for that?
If I do it right, how do I go back to the last input scene instead of going back to the loading scene? (I found some solutions to unwind the segue, but none that use the navigation controller, which I want to use)
Thanks
Using a scene for the loading sounds good.
As you're working in a UINavigationController stack here, when you're ready to trigger the segue to the next screen you should pop the loading view controller from the stack without animation, then trigger the segue:
[self.navigationController popViewControllerAnimated:NO];
// now trigger the segue
Related
I would like to create a "deep link" into my storyboard while preserving the backstack (back button navigation).
Ex:
Given the storyboard below (entry point being the leftmost Navigation controller)
When my application is opened via a remote notification I would like to open the second tab in by tab controller AND be able to navigate back to the item list via the back button.
Please note that I am not asking about how to open the second tab, or how to create such a storyboard but specifically if there is a way to do this with storyboards or will I have to do it by code.
Thanks!
PS: I come from an Android background where one recreates the parent view controller manually or (better) inserts it into the backstack. As far as my research goes there is no such thing in ios. I'm hoping I'm wrong.
Your UINavigationController has a viewControllers property. You can create as many view controllers as you want in an NSArray and assign it to this property and that will be the back stack with the last VC in the array displayed.
The problem is that when a notification arrives, your app could be in any state at all. It could be running, with some other screen showing. It could be suspended, with some other screen showing. Or it might not be running at all, and will now have to be launched from scratch.
Thus, starting in the App Delegate routine that responds here, you will have to deal manually (in code) with the situation if you want to put your app into an appropriate state.
The Problem
I'm currently building an iPad game using SpriteKit. The gameplay is driven by sound provided by EZAudio. After running the Instrumentation tools to profile my app, I noticed that whenever the GameViewController is shown memory allocation jumps up. When I repeatedly show the screen (5+ times) it crashes the app. My project uses ARC.
Navigation
The navigation consists of 4 ViewControllers:
MenuViewController: This shows the menu
CharacterSelectionViewController: It lets you a pick a character to use
GameViewController: This lets you play a game with the player chosen
ScoreViewController: It shows you the score you achieved in the game
1 - MenuViewController
From here you can navigate to CharacterSelectionViewController via a Show (e.g. Push) segue.
2 - CharacterSelectionViewController
You can navigate to GameViewController via a Show (e.g. Push) segue. There is also a back button that goes back to MenuViewController with the following code:
[self.navigationController popViewControllerAnimated:YES];
3 - GameViewController
It first shows a 5 second countdown (using NSTimer)
The game starts with the character chosen in CharacterSelectionViewController
The game can be paused, allowing you to quit and go back to MenuViewController via a manual Show Detail (e.g. Replace) segue.
When the game ends, a manual Show (e.g. Push) segue is called that navigates to the ScoreViewController.
It's view hierarchy consists of three sets of view - one for the countdown, one for the pause menu and one for the game. These are subsequently shown/hidden. See the view hierarchy below:
4 - ScoreViewController
This allows you to quit or restart the game. When quit it pressed it performs a Show Detail (e.g. Replace) segue to MenuViewController. If restart is pressed it performs an unwind to CharacterSelectionViewController.
Responses
Please provide answers regarding:
How this kind of leak could occur
Any observations you have from the allocation and leaks screenshots
Allocation
Below, you can see the increasing allocations as I cycle through the apps screens to repeatedly show the GameViewController. I used Mark Generation to be able to show the increase in memory allocations.
Leaks
Here you can see the memory leak that occurred. This is ordered by size.
Ignore the leaks for the moment; fix the generational accretion first.
Is that generation snapshot representative of what is left after a typical snapshot? Typically, you'd want to show view controller, take snapshot, hide then show view controller, take snapshot, etc... as many times as you can without crashing (or 10 times if it doesn't crash).
Then look at generation 3 or 4 as that'll be the most stable representation of per-generation accretion.
If it is representative, it looks like you are leaking everything that the view controller would normally allocate. Ultimately, you are looking for the "root" of your object graph that is keeping everything around. Fix the reason why the root is sticking around and the rest'll likely go away.
I wrote a weblog post about this. It is a bit outdated, but the analysis workflow remains the same.
http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/
How are you unwinding from your various view controllers? I note that you mention that when the game ends you're pushing another VC onto the stack, but I presume this VC chain will at some point unwind back to your initial menu? (In essence, I wonder if you're just looping around, hence adding new VCs to the stack everytime you play a game.)
To create an un-wind segue, simply create an empty method in the destination VC (i.e.: your main menu) as such:
- (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
{
// Intentional NOP
}
(N.B.: Make sure it's also listed in the header.)
You can then call this as you would any other segue in your storyboard by dragging from the source object in question to the exit option at the top of the VC that contains the source object in the storyboard. This will present you with a list of segues to choose from. (You can verify that the segue is setup correctly by selecting the source object in the storyboard - the Connections inspector should list the unwind segue within the Triggered Segues section.)
In my iOS app, I have made it so that when I segue from the main view to a second view data is passed, and then when I go back to the main view, data is passed back to it (using a protocol). This works fine if I segue between views slowly, but once I start doing it quickly, (spamming "push" and "back" buttons) the data becomes wrong. I use the prepareForSegue,viewWillAppear and viewWillDisappear methods to transfer data.
If anyone else has had a similar problem and can point me in the right direction that would be great.
Thanks!
I was wondering what is the best way to implement a countdown screen before showing the user the game view. For a more detailed example, I want the user to see a screen that displays 3...2...1...GO ! and than the game will appear.
Currently in my application I am using a navigation controller as my main menu where you can select multiple games to choose from. When a user selects one of the game buttons this is where I want the countdown screen to appear before my game interfaces does.
Solutions that I have thought about:
1) should I implement a new view controller that i push on the navigation controller to perform the count down ( seems like a waste)
2) is there a way to blank everything on a view and show a countdown first?
Thanks in advance for your help and cooperation !
Ryan
The best way i think is as soon as user selects a game, add your 3..2..1. Go screen on the same view..as soon as u present this u can also start preparing to create your game interface(but do not present). After GO appears, remove this countdown view and present your game..
It depends on the effect you are after. If you push a view controller onto the navigation stack, you'll need to use a pop transition.
My suggestion would be to open the game view controller and put a full-screen sized overlay view on top of it with your countdown message. Have the game VC manage the countdown view. When the countdown is complete, you could fade it, shrink it to a point, do some sort of clock wipe or keyhole animation, or whatever you want, easily and simply. (Some things are obviously easier than others. Cross-fades, shrinks and the like are trivial. clock wipes and keyhole transitions are much harder and require pretty advanced Core Animation skills.
What platform will this be for? For a full screen overlay I would try UIPopoverViewController if on an iPad. Otherwise, try a view controller presented modally. I believe you can set the transparency of either type to less than 1 so the underlying view shows through. In this case it would be nice to display the selected game's opening screen during the countdown. Of course it would be dark because of the overlying view. But it would provide a glimpse into what is to come.
I have a fairly simple app thats a game for small children. There is a main screen and 5 separate levels. 3 of the 5 levels are made up of more than one VC where actions take place in the first VC in that row then code calls a modal segue to the next one in the line and so on till it reaches the end of the row and a modal segue is called linking back to the main screen. The levels that have only one VC just perform actions then segue back to the main VC.
Every segue in the app in modal.
Also every page (VC) has a home button that will segue to the main page if pressed
I set this all up in the StoryBoard and visually everything works as Id expect but when adding sound I realized that there seems to be a major problem.
If I now understand correctly (and maybe I dont) modal segues dont actually replace the current VC with the newly requested one but rather slide the newly requested one over top the original and make it the visible display.
Currently I go from main to level 1. Level 1 does some stuff and plays some sounds that repeat via a timer. If I segue back to main visually everything is fine except the sounds being played by the timers in level 1 VC continue to play and xCdoe give me the following error quite a few times
2013-01-21 22:16:07.901 TTBetaDev[678:c07] Warning: Attempt to present <MainMenuViewController: 0x7e02f40> on <BonusViewController: 0x7ecbfa0> whose view is not in the window hierarchy!
Below is a screenshot of my storyboard in case I havent explained the layout well enough.
How should this be set up to allow the navigation I would like? A what steps will I need to take to apply that to the what I already have built in the storyboards? Or will I have to re-do all my storyborad work?
I tried apples VC documentation but I couldnt understand what relates to what Im trying to do.
COuld someone please help explain this to me
You have segues going forwards AND backwards. You shouldn't do this.
e.g. Look and Main and VC 2.
You have a segue going from Main to VC 2. This means that Main will present VC 2 as a modal view controller.
When Main does this though it is still on the stack underneath VC2.
Then you have a segue from VC2 to Main. This means that VC2 will create a new Main and present it modally too. If you continue using the app you will have multiple instances of main and all the other VCs and memory consumption will rocket.
What you need to do is delete ALL the segues that go backwards. (i.e. like the one from VC2 to Main)
Then when you want to get back to main from VC2 you have to dismiss VC2.
i.e.
in Main...
//present VC2
[self performSegueWithIdentifier:#"VC2Segue" sender:nil];
//dismiss VC2
[self dismissViewController:vc2ViewControllerInstance];
or in VC2...
//dismiss VC2 from itself
[self dismissViewController:self];
The main thing though is that you can't use segues to go backwards.
TL:DR
Nothing should segue INTO Main. Any segues that go into the left hand side of main should be deleted and dealt with properly.