ViewController Scene gets cleared when transitioning between scenes - ios

Is the UIScene discarded when switching views? Whenever I try to switch views, the nodes that the User added (SkShapenodes by drawing) and then come back to the UIScene with the drawing, the SkShapenodes are gone. Any help? Is this normal? How can i prevent this?

Related

iOS Show UIViewController as Popup from Anywhere

Basic question:
Is there a reliably way to trigger showing modal UIViewControllers at any point in the app's lifetime (including from different threads)?
My current approach is to call presentViewController on the showing ViewController (found through window.rootViewController + hierarchy traversing but that's unimportant). This generally works, but is sometimes ignored due to things like a navigation action/animation taking place.
E.g. a background thread signals for a popup to be shown, and presentViewController is called on a ViewController in the process of being dismissed.
I've tried a few work arounds such as repeating the signal if the ViewController isn't shown (which led to some instances of it being show multiple times), but it's ended up being a game of whackamole.
An ideal solution would also allow navigation to take place underneath the popup, but the primary issue right now is just reliability.
edit
To be clear, I’m a seasoned developer. The threading is being handled properly, the instance and type management is working. My problem is trying to manage all
the corner cases, not the basics of how to do it.
If you need mechanism for thread safe showing different VCs in multithread environment, you can make some object which is responsible for presenting/dismissing controllers. And make some queue on presenting/dismissing. So when your signal occurs, your operation on presenting will be next after dismissing current VC in queue

ARKit: pausing session does not stop the scene to be called

I'm using ARKit with SpriteKit. My AR feature is not a core feature in my app, users may optionally navigate to a viewController with an ARSKView where I configure and set an SKScene in the ARsession.
I pause the session when the user navigates back:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
then the viewController is pop from the navigationController where it was pushed. But I'm seeing that the func update(_ currentTime: TimeInterval) in my SKScene subclass keeps being called... which is the appropriate way to also stop that? I thought that pausing the ARSession would also stop the scene.
EDIT: when I navigate back from the view controller that holds the ARSKView and it is deinitialized, I see that the SKScene is still in memory and a new one is created every time I navigate again to the AR related view controller. So, if I navigate there N times, I see I have N SKScenes in memory. How could I appropiately remove the scene when navigating back? The scene property of the ARsession is read only.
that sounds like a memory retain issue. Make sure that any node in you SKScene subclass is properly removed and deinitialized.
Set your scene delegate to nil, set any node or array of nodes referenced in your VC to nil.
I ran a simple test with 2 VC embedded in a navigation controller, the first one has a view with a start button, the second one is the ARVC.
I run the session, set my custom scene delegate to ARVC, when I hit the back button everything stops by itself and my ARVC is being popped. Nothing in memory.
So make sure that you manually remove all your nodes and their animations, constraints, actions and anything that relates to you custom SKScene subclass.
Also don't be afraid to call sceneView.presentScene(nil) if that can help.
I couldn't find any official documentation about this, apparently there is not proper method to stop a session yet. What I found that was similar to your scenario is this work around.
It suggests you create ARSKView as disposable and before leaving your controller, pause the session, remove the view from superview and assign it to nil.

Heavy SceneKit scene in UINavigationViewController takes too long to dealloc

My app uses standard UINavigationViewController.
At some point, user has navigated to a ViewController which shows a SceneKit Scene.
This scene is very heavy in memory, since it has around 6000 geometries and each geometry has its own Material (this needs to be this way).
When user clicks the back button at the top bar in order to return to the previous viewController, the app correctly pops the current View Controller and shows the one below.
However, for around 4 seconds, the UI of the new ViewController shown freezes.
I have used Instruments Time Profiler, and I can see that big part of those 4 seconds are taken by these SceneKit methods:
-[NSConcreteMapTable countByEnumeratingWithState:objects:count:]
-[SCNMetalResourceManager _geometryWillDie:]
-[SCNMetalResourceManager _materialWillDie:]
It makes sense because there are so many geometries and materials.
How can I solve this situation, so that UI is not blocked while the heavy Scene is being released, either from a SceneKit perspective (making deallocation quicker) or from a UINavigationViewController perspective (maybe forcing deallocation of the Scene to happen in a separate thread?).
My understanding is that SceneKit uses it's own background thread to do work, so it shouldn't be blocking the main thread.
It is possible that the blocking of the main thread has something to do with UINavigationController animating the content away. I've had issues when trying to animate SCNViews/SCNScenes and it looks like SceneKit and CoreAnimation do not collaborate very well.
I don't have the full context here, but I can suggest couple of things for you to test:
You can try setting the scene propery of the SCNView with the heavy scene scnView.scene = [SCNScene scene]; to a new empty scene just before the View Controller with the scene is popped off, so that the heavy scene is not involved in the animation, and SceneKit properly deallocates it in it's background threat.
You can try keeping a strong reference to the heavy scene in the View Controller that prepares the problematic View Controller with the heavy scene.
Then when the latter is popped off and you return to the one below it, the popped off View Controller and all of its content should have deallocated, except the heavy scene and you'll have a reference to it.
Here you can try setting to nil (after the animation has completed) and possibly it will deallocate properly on SceneKit background thread.
If not, you can first remove all heavyScene.rootNode child nodes, actions, etc and then deallocate.
If this still does not work, you can first try using a recursive function to traverse all your nodes one by one and nil their geometry/material first and then deallocate the scene itself.
Using the rendering loop -renderer:updateAtTime: method to remove the nodes over several frames (less than a second). Then you can be sure that SceneKit will deal with them the way it is designed to.
You will have to detect when the user presses UINavigationController "Back button" and the VC with the heavy scene is being popped off. Then over, say 20 frames (1/3 of a second at 60fps), remove 5% of nodes at each frame, so there will still be a short delay but 1/3 of a second and hopefully unnoticeable.
The detection of the VC being popped off can happen by overriding its didMoveToParentViewController: method and checking if nil is passed as the parent parameter.
E.g.
- (void)didMoveToParentViewController:(UIViewController *)parent {
if (!parent) {
// work that should be done when VC is being removed from parent
// maybe set a counter in the VC being popped off to
// count down from 20 to 0, -1 at each -update call
}
}

Nil out a presentedViewController when UIApplication becomes active again

I have a presentedViewController on top of the root view controller when the app gets dismissed. (e.g. User navigates to another app or goes back to the home screen.)
I would like to nil it out when the user reactivates the app without it being visible to them. Calling -dismissViewControllerAnimated: is not an option because it only works if the view controller is visible, and I'd like to do it sooner and specifically only in application:openURL:sourceApplication:annotation: and otherwise let the user continue their workflow in the modal view.
Are there any tricks I can use?
According to your comment, the only actual issue is what happens when the app receives application:openURL:sourceApplication:annotation: and is thus brought to the front.
In that case, simply adjust your interface in application:openURL:sourceApplication:annotation: - that is part of its purpose, to allow you to do that. You will be given some time before the "snapshot" is torn away, so if the adjustment involved is to dismiss an existing modal view, dismiss it without animation and there you are.
The user will still see the modal view for a moment, but not really - what the user is seeing is not your interface but the "snapshot" that was created by the system when the app when into the background. The "snapshot" is removed to reveal the actual interface (with a nice crossfade effect in iOS 7).
If the "snapshot" itself is problematic there are ways of preventing it from being taken, but it doesn't sound to me like this is that kind of situation.
(By the way, this raises the question of how the presented view is to know that it must be dismissed. The initial main action is in your app delegate, which is probably some conceptual distance away from the presented view. This would be an appropriate situation in which to use an NSNotification to communicate across this distance.)

Clearing UIWindow cocos2d scene

I recently made a menu screen to my cocos2d app where I have attached two pickerviews to the UIWindow. Everything works good, but when I go to select which "game" I want to play and the scene changes, the pickerviews remain. How can I remove them?
Thanks!
More info:
The button is a CCMenuItem (if that matters)
Edit: And this is how Im changing the scene once a button is pressed.
[[CCDirector sharedDirector] replaceScene:menuScene];
If a CCNode object (like CCMenuItem) remains on screen after changing scenes, you have a memory leak.
You're saying that you attach the views (CCMenuItem?) to the UIWindow. I can't imagine how that would work since CCNode objects are not UIView objects.
One of those two issues is likely to be your problem, but hard to give you a concrete fix without seeing the relevant code sections.
I figured it out, all I did was give each pickerview a unique tag, and then when my button is pressed, I used
[UIView removeFromSuperview: tag];
for each picker view.

Resources