sceneDidLoad being called twice? - swift

Using Xcode 8, swift 3 and I create a iOS application using the game template with entities enabled. I notice I was seeing double node count for some initial sprites even though I only used addChild once.
I added
override func sceneDidLoad() {
print(#function) ... }
to the code and no idea why this is being called twice.
log file...
2016-09-20 10:21:31.482 MMDecon1[3295:791435] SKUtil.m: MGGetBoolAnswer is not available in the simulator.
sceneDidLoad()
sceneDidLoad()
I added
override func didMove(to view: SKView) {..}
and put my initialisation code in here as a temporary fix.
Does any one know why sceneDidLoad() is being fired twice with the default game app code using entities?

Normally, sceneDidLoad is only called one time. However, if a memory warning is sent then UIViewController releases its scene and sets it to nil if the view controller isn't visible. The next time that the scene appears the view controller will reload the scene and call sceneDidLoad again.
You have to assume that sceneDidLoad can be called multiple times.
Implement didReceiveMemoryWarning and log or set a breakpoint to see what is happening.

Related

Swift: View position resets when app is minimized, then brough back to foreground

I have a UIView that starts off in a random position (set up through storyboard), then finds its actual position when viewDidLoad() is called, depending on the user preferences stored in UserDefaults.
The problem is when the app is minimized, and then brought back to the foreground, the position of that UIView resets to the initial random position.
How could I prevent the app from releasing that position determined at runtime? Or do I need to re-calculate that position through a function that is triggered when the app is brought back from being inactive?
I don't think there is much use to include code for this, but the position of the UIView (named 'contextUnderscoreLine') is set in viewDidLoad() with contextUnderscoreLine.center = savedCGPoint.
I think I found a solution for this, in case someone else was going through the same issue:
Add an observer in viewDidLoad() to detect when the application is 'active'. From documentation, my understanding is that this will get called once when the app is launched, and then every time the app comes back from being in the background. So the initial formatting of the problematic UIView can be done there instead of in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc func applicationDidBecomeActive(notification: NSNotification) {
// Application is back in the foreground
//determine the position of the UIView here, instead of in viewDidLoad()
}
Edit
For my specific case it was better to use UIApplication.willEnterForegroundNotification instead of UIApplication.didBecomeActiveNotification. That way the changes in UIView are done before the Application is back in the foreground, and therefore you avoid the noticeable UIView jitters as the changes are made in front of the user.

deinit not called on boilerplate macOS app

I've created new app from macOS Cocoa App template. The only change I've made is added deinit in NSViewController. So now it looks like this(complete code):
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func doSomething()
{
var a = 10
a = a + 1
print(a)
}
deinit {
doSomething()
print("deinit called")
}
}
Why I don't see deinit call? I've searched number of questions here, but couldn't find answer, as I don't have any retain cycle.
As Tobi says in his answer, deinit gets called right before an object is deallocated.
An object gets deallocated when there are no longer any strong references to it. (Nobody owns the object any more.)
In order to answer your specific question you need to look at how your view controller is created an who owns it.
I haven't done much Mac development in a while, so I'm kinda rusty on view controller lifecycle, but here's my recollection of how it works:
If you only have one app window, it is the view controller's owner, and the window never gets closed, that means the view controller will never get deallocated.
If you quit the app I don't think the system tears down your window hierarchy before terminating the app (unless you're app is a document-based app, in which case the app will be told to close all of it's document windows before quitting.)
A deinitializer is called immediately before a class instance is
deallocated.
Altho your question is not clear to me.
But the usual reason for failure to trigger deinit when expected is that you have a retain cycle that prevents your view controller from going out of existence.
Sometimes the reason is that your expectation that the view controller would be destroyed under the circumstances is incorrect.
But assuming it is correct, a retain cycle is the reason.
another suggestion is to use Hierarchies Debug.
answer those questions for your self.
is this the root UIViewController ?
is it dismissed properly ?

Swift: SKSpriteKit, using Storyboards, UIViewController and UIButton to set in game parameters?

Context
The default code that comes with a new SpriteKit game has a storyboard such that - following the launch screen - all there is, is a GameViewController which calls forth the GameScene. However, this may be less ideal for many games. For example, one may wish to have the user select difficulty from a main menu, and then go to the GameScene - outlined below:
Notably, the middle view controller is a custom class MyUIViewController so that the UIButtons "easy" and "hard" can have the following IBActions:
#IBAction func setGameDifficultyToEasy(sender: AnyObject) {
gameDifficulty = "easy"
print("Game difficulty set to \(gameDifficulty)")
}
#IBAction func setGameDifficultyToHard(sender: AnyObject) {
gameDifficulty = "hard"
print("Game difficulty set to \(gameDifficulty)")
}
where gameDifficulty is a global variable, that the GameScene utilizes to determine aspects of the game play.
In the M.W.E. setting gameDifficulty to "hard" causes there to be three sprites on the screen, whereas setting gameDifficulty to "easy" puts forth only two.
Easy
Hard
Question
In the following gif, one sees that:
gameDifficuly is initialized as "hard"
the UIButton "Easy" was selected.
This can be seen be the printout statements.
Interestingly, although the UIButton was pressed first then the GameViewController was called, the changing of the parameter gameDifficulty was not set until after the GameScene was rendered.
**How can I get the UIButtons to set the parameter gameDifficulty prior to GameScene being called?
Minimum Working Example
Stack Overflow SKSpriteKit
Note
My answer here shows that this is clearly possible, but by relegating everything to SKViews rather than using a storyboard. So if possible, please keep answers related to using storyboards.

Refresh Today Widget every time opened

I thought that Today View every time when i open it it calls "viewWillAppear" but its not. When i change something in my app, and then I slide down for Today View it sometimes refresh the view and sometimes not.
I do all logic in viewWillAppear (fetch data from coreData and put that data to labels), but its not called everytime.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
fetchContent()
setLabels()
setContentHeight()
tableView.reloadData()
print("view will appear")
}
how to call fetchContent and setLabels every time when user opens Today Extensions?
For this matter you should be using NSWidgetProvinding's widgetPerformUpdateWithCompletionHandler.
Steps:
1.- Make sure that your UIViewController implements NCWidgetProviding
class MainViewController: UIViewController, NCWidgetProviding
2.- Add the following function:
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.NewData)
}
3.- In your case you will be using .NewData.
Just make sure that you retrieve needed data and refresh your views (putting every data in place, filling labels, graphs, etc).
Nevermind the fact that your view is not visible during the call to this function, iOS will fill the view and take a snapshot of it.
Then that's what it shows while you are opening notification center and until you gain control again of your app.
So, in your case would be something like this:
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
fetchContent()
setLabels()
setContentHeight()
tableView.reloadData()
completionHandler(NCUpdateResult.NewData)
}
Swift 2.1 && Xcode 7.2
It looks like some bug appears when you many time recompile this today extension. solution is to remove from notification center and add it again. then it refresh fine everytime opened

Start the animation of something when app starts

I am trying to learn Swift. I currently have a button that plays an animation but I want to have the animation start immediately after the app loads. How would I go about doing that?
You need to put your animation on the viewDidApear method, while will be called after your viewDidLoad method.
For example
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
UIView.animateWithDuration(2, animations: {
//do what ever here
})
}
Your question is not very specific so I'm not sure if this answers your question.
When your iOS application starts, there is usually a View Controller that gets rendered on the screen as your main entry point. This can be changed in in your AppDelegate.swift
Now, let's assume that in your application, a view controller named WelcomeViewController is the first page and you have your button along with the rest of your logic in it. Every view controller has certain functions that you can override and use. For example:
viewDidLoad
viewDidAppear
viewWillAppear
viewWillDisappear
the names are self-explanatory so I'm sure you can guess what they do. In your case, just override viewDidAppear and start your animation in there.
override func viewDidAppear(animated: Bool) {
...
}