Currently I have 3 swift files: ManualViewController, AutoViewController, Main
ManualViewController is the UIViewController with a table view.
AutoViewController is a UIViewController with a few buttons.
Main is just a swift file with all the data for table view.
ManualViewController and AutoViewController are controlled using TabBarController.
When I run the app the initial contents found in Main.swift is loaded onto the table view. When I go to the next view i.e AutoViewController and click on a button to change data in Main.swift, the data changes. The problem is when I switch back to ManualViewController the table still contains the old data and not the updated one.
I also tried this:
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(false)
self.tableView.reloadData()
}
It still din't work.
You can use NSNotificationCenter for update your tableView from another view.
Your -addObserver: declaration has to be like this in your tableView Controller in viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshTable:", name: "refresh", object: nil)
}
And your function for this addObserver will like:
func refreshTable(notification: NSNotification) {
println("Received Notification")
self.tableView.reloadData()
}
Now you can post notification like this when you navigate to your tableView controller:
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil, userInfo: nil)
Check THIS sample project for more Info.
Hope it will help you.
Under normal circumstances, the way to update data in a UITableView is
self.tableView.reloadData()
Related
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.
I want when user call app from background to reaload one function from viewcontroller?
Elaborating on nishith's answer:
Add the following code to your view controller you want to refresh
override func viewWillAppear() {
super.viewWillAppear()
......
......
NotificationCenter.default.addObserver(self, selector:#selector(YourViewController.methodToRefresh), name: UIApplication.willEnterForegroundNotification, object: UIApplication.shared)
......
......
}
Always remember to cleanup when the view disappears in:
override func viewWillDisappear(animated: Bool) {
NotificationCenter.default.removeObserver(self)
}
You can register your controller for these notifications and reload your controller accordingly.
UIApplicationDidEnterBackgroundNotification
UIApplicationWillEnterForegroundNotification
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 a ViewController, I'm trying to reload data in a TableView in another ViewController like so:
(self.presentedViewController as! tableViewController).table.reloadData()
Where tableViewController is the class in the TableView's controller (It's not upper camel case, I know) and table is the TableView. Well, doing this yields "fatal error: unexpectedly found nil while unwrapping an Optional value" and I guess that makes sense since the "presentedViewController" hasn't been loaded yet.
I also tried this:
(self.navigationController!.viewControllers[self.navigationController!.viewControllers.count - 2] as! tableViewController).table.reloadData()
which yielded the same result (I made sure the tableViewController was under the current ViewController on the navigationController stack). I'm baffled about what I should do...I feel like it should be easier to refer to properties in different view controllers. I may be a little vague; if I am, tell me what you need to know!
Xcode 9 • Swift 4
Create a Notification Name:
extension Notification.Name {
static let reload = Notification.Name("reload")
}
You can post a notification
NotificationCenter.default.post(name: .reload, object: nil)
And add an observer at the view controller that you want to reload the data:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(reloadTableData), name: .reload, object: nil)
}
#objc func reloadTableData(_ notification: Notification) {
tableView.reloadData()
}