Swift iOS Which View Is On Top - ios

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
}

Related

View Controller must refresh itself when coming to front

I have an app that I am developing using Swift 4.0. I have a View Controller on which I am showing some useful information and user can interact with them. Lets call this as BaseViewController.
What I am doing:
The BaseViewController is starting different other ViewControllers, and than user can dismiss those viewControllers and come back to BaseViewController.
What I want:
Now I want that whenever user comes back to BaseViewController it gets itself updated. I know it can be done using Protocols, but I just want a simple way. Like in Android there is onResume method to perform updates whenever Activity comes into active state.
I think there is no need to share code as starting other viewController from one viewcontroller is pretty simple. I just wanted to know the better approach. Thanks in advance
UPDATE: THIS IS HOW I AM CALLING NEXT CONTROLLER OVER BASE CONTROLLER
let dialogRegisterForEventVC = UIStoryboard(name: "Main",bundle: nil).instantiateViewController(withIdentifier: "idDialogRegisterForEventVC") as! DialogRegisterForEventVC
dialogRegisterForEventVC.modalPresentationStyle = .overCurrentContext
dialogRegisterForEventVC.modalTransitionStyle = .crossDissolve
dialogRegisterForEventVC.isUserLogin = isLogin
self.present(dialogRegisterForEventVC, animated: true) {
}
You can always go with viewWillAppear Method
override func viewWillAppear(_ animated: Bool) {
print("This is called when coming back to Base View Controller")
}
Use your refresh code here. This is called when you pop your viewController
There different cases and you need to handle them for example
If you presenting another viewController on top of this you should use
override func viewWillAppear(_ animated: Bool)
If you need to refresh the view if the app comes in foreground state you need such code :
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func willResignActive(_ notification: Notification) {
refreshMyView()
}
Also, you can create delegate that your viewController can call to update this view controller. And one more option is to make your view controller observer of custom notification in Notification center and to push this notification once when you need to refresh the view controller.
If you using the notification center just be sure that your UI changes are on the main thread.

iOS equivalent for onRestart()

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)
}
}

viewWillAppear not called when going back on a UINavigationController

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

How to reload a function if you hit home and then open the app again?

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

A way to reload the ViewDidLoad on each run swift

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.

Resources