I'm making this small game with swift and the game is set up using the viewDidLoad. Each day the game is suppose to be slightly different, but that depends on that the viewDidLoad runs. So let's say a user plays at day 1 and keeps the game active to day 2, the game won't function on day 2 since the viewDidLoad wasn't run.
For the game to function properly I need to run the VieDidLoad each time "applicationDidBecomeActive", to make sure it runs as it should.
I've tried to do like this:
func applicationDidBecomeActive(application: UIApplication)
{
myViewController?.viewDidLoad()
}
I also tried to wrap my code that is inside viewDidLoad like this and run it in applicationDidBecomeActive. But that doesn't do it.
override func viewDidLoad()
{
func refreshView()
{
//all the code goes here
}
}
Any idea what I could do?
Set your view controller to listen applicationDidBecomeActive notifications and run the configuration method when it receives it
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "refreshView:",
name: UIApplicationDidBecomeActiveNotification,
object: nil)
viewDidLoad method not meant to load many times, this will be called only once per lifecycle of viewController when view first created an loads into memory, and this is not right approach call this again and again.
What you want is possible without loading viewDidLoad again and again, move your refreshView method out of viewDidLoad and call it when required, you can call it once form viewDidLoad and after that you can call it again when required.
You can respond to application notification methods like
UIApplicationWillEnterForegroundNotification or in
UIApplicationDidBecomeActiveNotification and then figure out do you want to refresh view or not, if you want to refresh view just call your refresView method.
Your code should look like this
override func viewDidLoad()
{
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "OnAppBecameActive", name: UIApplicationDidBecomeActiveNotification, object: nil)
refreshView()
}
func refreshView()
{
//all the code goes here
}
And add this method as well
func OnAppBecameActive()
{
// Add logic here to check if need to call refresh view method
if needToRefreshView
{
refreshView()
}
}
I have the same probelm but i solve it with call
self.viewDidLoad()
On my Func I wanna to call my view did load again from it.
Related
hope someone can help me with my issue. I'm quite a beginner programmer and this is my first serious app im trying to build so please be understanding and feel free to point out if i didn't provide enough information or code.
I'm trying to pass data using Notification Controller between View Controllers. Shortly this is how it suppose to work:
After pressing edit button on the MainVC it should pass notification and go to EditVC, where that data should be received and used later on that view.
Problem i stumbled into is that seems EditVC doesn't receive data on first load, but if i go back and press the button again it works flawlessly.
Any suggestion why it doesn't load first time?
I tried to move observer from viewDidLoad to viewDidAppear and notification post to different moment (worth mentioning: for now it gets posted after i long press the cell, what long press achieve is showing additional buttons to edit or delete, i chose this moment because i want to use that notification to pass all data from active cell and will use the same notification in other places too, this specific segue is performing after pressing edit button)like moving it to editButtonPressed function or prepare for segue function, but effect is the same. What am i missing here?
Here are snippets of my code:
Edit VC
class EditVC: UITableViewController {
override func viewDidLoad() {
startObserving()
}
func startObserving() {
NotificationCenter.default.addObserver(
self,
selector: #selector(editPressedName(notification:)),
name: .editPressedName,
object: nil
)
}
#objc func editPressedName(notification: NSNotification) {
print("\(notification.userInfo) received")
MainVC Notification Post
let activeCellName:[String: Data] = ["name":item!]
print(activeCellName)
NotificationCenter.default.post(name: .editPressedName, object: nil, userInfo: activeCellName)
When you press the Edit button for the first time, the EditVC it not listening for the notification, as it's ViewDidLoad function was not yet called and the observer was not yet set. You can check this by adding a brakepoint to you startObserving function and to the Notification post in your first ViewController, and you will see that the notification fires first, and only then the observer is set in your EditVC.
To make it work you have to make sure that the startObserving is called before the notification was fired. If you are creating the EditVC programatically you can move the startObserving function into the init:
class EditVC: UITableViewController {
override init(style: UITableView.Style) {
super.init(style: style)
startObserving()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
If you are using storyBoard, you can do something like this
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? EditVC {
destination.startObserving()
// And now you can post the notification
}
}
What is the iOS equivalent for onRestart() used on Android?
onRestart() is called when current activity is being re-displayed to the user (the user has navigated back to it).
I believe you need viewWillAppear method:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//your code here
}
It is called every time right before view is going to be shown. So it will be called when view is shown for the first time as well.
If you want to avoid running your code for the first time viewWillAppear is called, you will have to add a flag property and check if it has been set previously.
If you're trying to capture whenever the scene in question comes into view, there are two cases you might be concerned about:
If you transition to this scene (or dismissing/popping back to this scene) from within the app. In that case, use viewWillAppear:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
update() // your routine for updating what is displayed to the user
}
If your app is running and you press the "home" button (or go to another app), and then later return to the your app (before it is terminated), viewDidAppear is not called. To detect that scenario, you can observe .UIApplicationDidBecomeActive:
private var observer: NSObjectProtocol?
override func viewDidLoad() {
super.viewDidLoad()
observer = NotificationCenter.default.addObserver(forName: .UIApplicationDidBecomeActive, object: nil, queue: nil) { [weak self] notification in
self?.update()
}
}
deinit {
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}
I have some code I call that changes the language in the viewWillAppear section of a viewcontroller inside a navigation controller.
When I hit the back button the language change doesn't take place even though I have code for it to in the viewWillAppear. The only time it switches is when I hit back all the way to the original screen and then start moving forward it changes. Is there any way to have the function in the viewWillAppear work?
Here is my code, I'm using a language changing pod:
//MARK: Language change
//used to change language text for imediate screens
func setText(){
locationsLabel.text = "Locations".localized()
languageLabel.text = "Languages".localized()
termsOfUseLabel.text = "Terms of Use".localized()
privacyPolicyLabel.text = "Privacy Policy".localized()
pushNotificationsLabel.text = "Push Notifications".localized()
contactUsLabel.text = "Contact Us".localized()
}
// Changes text to current language
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setText", name: LCLLanguageChangeNotification, object: nil)
}
// Remove the LCLLanguageChangeNotification on viewWillDisappear
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
The viewWillAppear method is only adding a notification observer. The observer is removed in viewWillDisappear. This means that setText will only be called if the LCLLanguageChangeNotification notification is sent while the view is visible.
The update stops as soon as the view goes off-screen due to the navigation behaviour.
To ensure that the text is updated, you also need to call setText inside viewWillAppear:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
setText()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setText", name: LCLLanguageChangeNotification, object: nil)
}
Implement navigationcontroller delegate methods
navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
I have created this little class that solves this problem.
Just set it as a delegate of your navigation controller, and implement simple one or two methods in your view controller - that will get called when the view is about to be shown or has been shown via NavigationController
Here's the GIST showing the code
I'm working on an app that fetch data from a website. When the user hit the home button then open the app again (from background), I want to reload the data again to the viewController.
I tried the following code:
in app delegate:
class AppDelegate: UIResponder, UIApplicationDelegate {
var myViewController: ViewController?
---------
var myViewController: rootViewController?
func applicationDidEnterBackground(application: UIApplication) {
print("Goodbye world") //.... then whatever code after pressing the home button
}
func applicationWillEnterForeground(application: UIApplication) {
print("Hello World")
myViewController.ObtianData() // which is pretty much the func in my app that fetch data from the web and display it in tableView
}
Then in the ViewController under ViewDidLoad
override func viewDidLoad() {
// I added the print to log here to check if the viewDidLoad function is being called but apparently it is not.
print ("Hello again from ViewController")
let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate! as! AppDelegate
appDelegate.myViewController? = self
}
Any suggestions?
The root cause of the problem that you are having is that the data that you load is attached to the view controller that displays it. This goes against the MVC principle, which suggests that the model needs to be separated from the controller.
You should reorganize the classes in such a way that ObtainData is split between the model and the controller:
The model goes out and obtains the data,
The controller decides what to do with the data.
Make a class called Model (or pick some other name with Model in it) and store the data for your table in it. Make a single instance of that class statically accessible from everywhere through Model.instance (i.e. implement a Singleton in Swift).
Change your view controller to rely on Model.instance for its data, rather than storing it internally.
That is all you need to do to separate the pieces of your app. Now your problem can be solved in exactly two lines of code - applicationWillEnterForeground should call Model.instance.obtainData, and your controller's viewWillAppear should call reloadData on its tableView.
You should use NSNotificationCenter event UIApplicationDidBecomeActiveNotification, it was made especially for that.
(You don't need to use AppDelegate)
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(applicationDidBecomeActive),
name: UIApplicationDidBecomeActiveNotification,
object: nil)
}
func obtianData() {
// do something
}
Note that swift standard require function name to start with a lower case.
add Notification with UIApplicationDidEnterBackgroundNotification and UIApplicationDidEnterBackgroundNotification
In my app I have a background task running that alerts the user when a certain event happens. What has to happen next depends on which view is currently on top. How can I determine this? I thought about writing some indicator into a plist file but that seems a bit clunky especially if there's a system way of doing it.
that's how it can be done in objective c
if([self.navigationController.topViewController isKindOfClass:[MYController Class]])
//then do something
I'm sure you cab find out the swift version easily
I can think of two possible ways:
First, you could use NSUserDefaults to set a Bool or String indicator. Assuming you're using Swift, try:
NSUserDefaults.standardUserDefaults().setObject("FirstViewOnTop", forKey:"WhatViewIsOnTop?")
NSUserDefaults.standardUserDefaults().synchronize()
When you want to retrieve the value, use:
let indicatorString = NSUserDefaults.standardUserDefaults().objectForKey("WhatViewIsOnTop?")
You could use an if/else statement from there.
The way I would go about doing it, though, is to subclass UIView and add a z property and add it to the init(). Then you can access it much more elegantly.
This sounds like the perfect opportunity to use NSNotificationCenter. Have the background task post a notification that your view controllers will listen for. When the active viewController gets the notification, it can handle it in the way that is appropriate for that viewController.
Send the Notification from the main queue since your task is running in the background:
dispatch_async(dispatch_get_main_queue()) {
NSNotificationCenter.defaultCenter().postNotificationName("SomethingWonderfulHappened", object: nil)
}
Receive the Notification: For each of your viewControllers that might want to act on the notification, override viewDidAppear to start listening for notifications, and override viewDidDisappear to stop listening for notifications:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleNotification:", name:"SomethingWonderfulHappened", object: nil)
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Notification Handler: Implement one of these for each of your viewControllers. Have each one do what is appropriate for that viewController.
func handleNotification(notification: NSNotification) {
//Action to take when SomethingWonderfulHappened
}