Hi I made a game using SpriteKit and wanted to add a leaderboard using a UITableView. My segues are working perfectly from my GameViewController to the TableViewController and back. However I can only do this once. Once I am back to my GameViewController, tapping the same button wont take me to the TableViewController for the second time. Why is this happening?
I set it up so when a certain SKSpriteNode is tapped it segues to the the TableViewController. Currently I am using this line of code to segue from the GameViewController to the TableViewController (This code is within GameScene.swift)
self.view!.window!.rootViewController!.performSegueWithIdentifier("showScores", sender: self)
I then setup a Back button within a navigation bar and created a segue back to the GameViewController (by dragging the BarButtonItem to the GameViewController and selecting presentModally). Some help would be appreciated. Thanks!
I think you are segueing to the leaderboard, segueing back, but in the process adding the main game view to the stack rather than removing the leaderboard, so when you try to segue again, it is not the visible game scene that receives the call.
Try using this rather than a segue in your leaderboard code (where you ask to segue back to the game scene):
self.dismissViewControllerAnimated(true, completion: nil)
If that doesn't work use a delegate to ask the game scene to dismiss the leaderboard.
Edit: I was rambling and maxed out my word count for the comment, so thought I'd add my explanation here!
Think of the stack as a pile of paper, where each piece of paper is a viewController, the topmost piece being the one that's visible (naturally). When you segue or present a viewController modally you are adding a viewController to the stack or 'adding a piece of paper to your pile'. It is generally then a bad idea to segue to and from like you were doing because you are adding more and more 'pieces of paper' to the stack. You tend to get memory problems and/or irritating bugs :P. Instead what is considered best practice for programmatically going back is to do one of two things: if you are using a navigationController you should call popViewControllerAnimated, or if you aren't, like in this case, you should use dismissViewControllerAnimated. Both these functions take the topmost 'piece of paper' off the stack entirely. Fixing that problem of continually adding the viewControllers to the stack. If any of that was confusing (I'm writing this on my phone so could be!) just send me a comment or a message and I'll try to explain better! :)
Related
I am running into a problem here. I am presenting views with performSegueWithIdentifier. Everything goes smoothly. The problem is that as a test, I only have 2 viewControllers with some data in them and I have two buttons that call a segue back to the other VC. If I keep performingSegues, you can clearly see that the memory usage goes up every two segues by around 0.4Mb. This tells me that the Views are not being deleted/removed from the view stack and are just using memory. I would like to know the correct way of getting rid of the view that presents the other view by using performSegueWithIdentifier (of course, after it finished presenting the view, else it will not work I guess).
Could you point me in the right direction? I found some objective-c code that tried to do this but it was very extensive and I don't know much about objective-C, so it was a little hard for me to understand.
Thank you in advance for your help!
Cheers!
Edit:
I am doing a "manual segue". By this I mean I have two view controllers standing on their own. They are not embedded in any navigationVCs or something like that. I am using an "Adaptive Segue" of type "Show".
If your goal is to always keep one VC, and since you already have a manual segue, what you can do is, after presenting the next VC you can pop the existing VC from the root and assign the destination VC as your root VC like so
class ManualSegue: UIStoryboardSegue {
override func perform() {
sourceViewController.presentViewController(destinationViewController, animated: true) {
self.sourceViewController.navigationController?.popToRootViewControllerAnimated(false)
UIApplication.sharedApplication().delegate?.window??.rootViewController = self.destinationViewController
}
}
}
When you do normal segues, you are piling up new view controllers on top of the existing ones without dismissing them. That's why your memory usage keeps going up.
You need to provide more information before we can give you specific guidance. Are you doing modal segues or pushing onto a navigation stack?
If you're pushing, you want to pop to the view controller you came from. Think in terms of a stack of plates. When you push, you add plates to the stack. When you pop, you take plates off and reveal the plates (view controllers) underneath. Do a search in the Xcode help system on "PopTo" to find the methods that let you either pop back to a specific view controller or back to the root view controller of your navigation controller.
Modal view controllers stack on top of each other in a similar fashion, but without the origination of a navigation controller. If you've pushed a series of one or more modals and you send the dismissViewControllerAnimated:completion: method to the view controller you want get back to it will dismiss the modals that are on top of it.
You can also look at using unwind segues. Unwind segues let you go back where you came. Do a search in the Xcode help system on "Using unwind segues" for more information. Most of the tutorials on unwind segues are written in Objective-C but you should be able to find some in Swift if you look. Explaining unwind segues in enough detail to be useful is more than I can cover in this answer.
I am building an application in Xcode 6.2, for iOS 8.1. I have a UIViewController (LevelViewController) which contains a UICollectionView whose cells each represent a level in my game (each one has a label w/ a number). Once one of the cells is selected I perform a show segue to an SKScene which loads all the data for that particular level.I also have a "menu" button which performs a show segue back to the main menu.
Functionally this all works, however I am having serious memory problems after performing both segues. After peeking in instruments it appears that when I segue out of the LevelViewController that all of the UILabel that I added for each individual UICollectionViewCell remain in memory, along with everything else contained in the cells. There should only be 192 labels (for 192 levels) but after performing this segue several times they add up to around 1000 in instruments.
Obviously these are not being deallocated in memory, It's my understanding that swift should take care of that, so i'm not sure what the problem is. I should also note that the UICollectionView was added programmatically, and no IBOutlets are used.
So how exactly can I get rid of those labels, and really, the UICollectionView itself when I segue away from the LevelViewController. Im seriously confused about this and it's ruining my St. Patricks Day. So for the love of all things Irish please help a lad out :)
Note: methods I have tried
self.collectionView.removeFromSuperView()
self.collectionView = nil
self.collectionView.deleteItemsAtIndexPaths(path)
It's hard to tell without seeing the code you're using to perform these segues. But I'm guessing that the problem has to do with how you are segueing between the view controllers.
I also have a "menu" button which performs a show segue back to the main menu.
If I had to guess based on your language above, instead of popping the Level ViewController (I'm assuming it is embedded in a UINavigationController), you are trying to segue back to the main menu using performSegueWithIdentifier: This will actually create a new instance of the view controller and push it onto the navigation stack (and retain the existing instance of it leading to your memory woes).
If that's indeed your problem, the solution is pretty simple: When your menu button is tapped, you should be calling popViewControllerAnimated or dismissViewControllerAnimated:completion:.
A simple little thing worth doing while you get accustomed to iOS segueing is to add de-initializers to all your view controllers as follows so as to get ongoing debug messages that objects are being deallocated as expected:
deinit {
debugPrintln("Name_of_view_controlled deinitialized...")
}
Happy St. Paddy's Day!
I created a storyboard for my app which contains the following:
Initial view controller on my storyboard is a Tab Bar Controller (let's call it myTabCtrlr)
myTabCtrlr has forward segues pointing to several other controllers:
a. First segue points to a custom UIViewController (let's call it vc1) on which I create an interactive UIView (let's call it popview1) which is initially hidden. There's a button (let's call it showPopView1) on vc1's view which when clicked would show popview1
b. Second segue points to a navigation controller, which embeds a view controller with 3 buttons, each pointing to an (end) controller.
c. Third segue points to another navigation controller with a similar setup as (b)
On several of these (end) controllers, there's a button similar to vc1's showPopView1 that when tapped, I'd like to switch back to vc1 and programmatically bring up popview1, which I'm doing as follows (but it's not working):
myTabCtrlr.selectedIndex = 0;
//I get a handle to vc1 then
vc1.popview1.hidden = NO;
When I do that, it goes back to the first tab and shows vc1 view (which is good) but it does not show popview1. I tried many different ways to do it but no luck.
Note that if I'm actually on vc1 and I tap the showPopView1 button, then popview1 comes up normally.
Does anybody know why that is the case? This only started after I transitioned to using storyboard. thanks.
After spending hours looking at various ways to solve this problem, and focusing on reverse segues and similar methods, I was able to solve it using a totally different method.. I wanted to share this with others so nobody has to waste so much of their time (although I noticed slow response to my post. Maybe I didn't make the subject attractive enough :)..
It's a really simple solution, but quite effective. I used a Singleton pattern object. When coming from the rest of the tabs, I set a flag in the singleton that vc1 checks in its viewWillAppear method, shows popview1, and immediately resets the flag.. works like a charm!
I changed navigation in my application from using UITabBarController to u UINavigationController. I.e. former solution (1st version) was based only on the TabBarController - 4 ViewControllers (one simple TableView, one simple custom view and one MapView with many overlays). The second version is based only on the UINavigationController.
In case of TabBarController it was clear and simple, everything worked fine, especially MapView. I mean: the MapView was loaded once (with a significant number of overlays) and when I went to another view and back to the MapView the MapView was still there with its overlays already loaded and displayed (simple check: MapView`s viewDidLoad was called just once per app run, I had some debug messages there).
Now I changed navigation logic to the UINavigationController. Everything works fine for the first look - but: the viewDidLoad (for each view) is called everytime I navigate to the view. It is annoying especially in the case of the MapView - the loading of overlays is performed everytime, it takes some time and it causes app crash in some cases.
OK, my questions:
Is it some kind of "common" behavior of NavigationController?
Can I change this behavior so viewDidLoad will be called just once?
And more - How can I influence the "display sequence" of some view?
I understand the logic is probably more complicated but I appreciate any answer or hint ;)
Some related circumstances:
TabBar and Navigation controllers are not combined.
I use storyboards, segues are designed in the UIB, no manual calling like perfomSegue or prepareForSegue in my code. One button triggers segue to MapView.
I use push segues.
I also tried to use modal segues but without any change of that behavior.
any of viewDidUnload is never called during segues among the views.
No memory warning received.
No memory leaks measured both on simulator and iPhone 4.
I tried to build a very simple temporary project / app that is concerned just about the Nav. Controller and other views without ANY coding, just storyboard. It was the same behavior.
There was an issue that causes app crash when I fast and periodically tapped to navigation button and back button between one view and the MapView. In most cases the app crashed when I tapped the back button on the MapView before it was fully displayed (i.e. its overlays). It was fixed when I added a 1 sec. delay method call in the viewDidDisappeared in the MapView. It is not a fair fix, I know ;)
A UITabBarController and UINavigationController are based on fundamentally different paradigms.
The UITabBarController is intended for the UIViewController on each tab to exist independently of each other and for the user to choose which they want to view. viewDidLoad only gets called once for each UIViewController because it is intended that each tab still exists in memory even as the user switches to a different tab.
The UINavigationController is a stack of UIViewControllers where each is related to the one above and beneath itself. The top UIViewController in the stack is always the one that is visible to the user. When a UIViewController is pushed to the stack, its viewDidLoad gets called because it is being loaded into memory. When the top UIViewControllergets poped off the stack, it is unloaded from memory, and viewDidUnload gets called on the way out (viewDidUnload is deprecated in iOS6 and won't get called, but the controller will still get dumped from memory). This is why viewDidLoad gets called every time that the user pushes a particular UIViewController onto the UINavigationController stack.
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.