I have a function in my AppDelegate which is similar to, DidReceiveRemoteNotification, and upon receiving a message appends a message to an array and then attempts to reload a tableview.
My app consists of just one Viewcontroller and one AppDelegate.
My code looks like this:
var myCustomViewController: ViewController = ViewController(nibName: nil, bundle: nil)
println("******didReceiveMessage*****")
myCustomViewController.messages.append(message.data.message as! String)
println(myCustomViewController.messages)
myCustomViewController.MessageTableView.reloadData()
whenever I try to call a reload function my app crashes however. Is there a way to reload my tableView from my appDelegate function?
Your view isn't loaded yet, so the table view doesn't exist yet, hence it is nil.
It isn't clear whether you're using the correct view controller, because you're creating a new one. Either call view on it to create the view (and subviews) or change the reference to the real existing view controller.
Most likely you should be accessing the root view controller of the app delegates window.
Related
Throughout my app I use a navigation controller to push and pop my view controllers. When I pop from one ViewController, I check to see if I can reload the data in the previous one with this:
_ = self.navigationController?.popViewController(animated: true)
if let previousViewController = self.navigationController?.viewControllers.last as? AnimalsVC {
previousViewController.tableView.reloadData()
}
This works every time, but now I need to do the same with another view, but it's not apart of the navigation controller because I modally present (instead of pushing it to the navigation controller). Now there's no way I can access the previous ViewController like before and I can not figure out how to access the previous ViewController.
How can I access the previous ViewController to reload the tableview like the example code without accessing the navigation controller?
I know this is possible with notifications, but I prefer not to use that method if possible!
First of all, It's not necessary to access the previous ViewController to reload tableview(or any other func)
I recommend you to use Delegate to achieve the same feature.
Holding a reference to the previous viewController in the way you mentioned will make your app very hard to maintain when your app gets more complicated.
You can call tableview.reloadData() in viewWillAppear method in the controller that you present modally
I'm making an app where you jump between two Views and I don't want two generate them new every time I show one of them. So I stored ViewA as a reference in ViewB, that I can present ViewA later again.
In ViewA in the prepare func
viewB.root = self
Now, when I try to access the ViewA, I get a EXC_BAD_ACCESS Error. I figured out that this is because the ViewA unloads and gets deleted, so my reference is like nil.
Any1 has a suggestion?
Use segue instead of using references of one controller in another.
Study this link and you will know more about navigating between different View controllers.
Navigation controller and Segue
You can cache viewController
let cache = NSCache()
// get your viewController from cache
let controller = cache.objectForKey(object) as? YourViewController
// store viewController into cache
cache.setObject(controller, forKey: object)
I have a class that is a GCDAsyncUdpSocketDelegate. Now I'm getting some data thru UDP (in a background thread) and need to display that data with an IBOutlet in my ViewController. But ViewController is just a class. Where is its object instance defined? What is its name? What do I do to access it, or one of its properties, methods? I've seen this UIApplication.sharedApplication().keyWindow?.rootViewController method but this doesn't guarantee the exact ViewController I want to access, I guess.
I don't know the exact structure of your app, but you could try something like this:
let rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
if let customViewController = rootViewController as? CustomViewController {
customViewController.label.text = theData
}
else{
let customViewController = storyoboard!.instantiateViewControllerWithIdentifier("CustomViewControllerID")
// (This assumes CustomViewController is defined in the same
// storyboard as the view controller running this code. Otherwise,
// you need to get a reference to the storyboard first).
rootViewController.presentViewController(customViewController, animated:true)
customViewController.label.text = theData
}
EDIT: If the object that receives your asynchronous server data is dettached from your navigation (good call, by the way), you can have it:
Store the data somewhere it will persist even if said object is deallocated
(or make said object a singleton, if applicable).
Post a system notification (using NSNotificationCenter) once the data is available, and have your main ViewController "listen to it" (-addObserver:selector:name:object). When the notification is posted and the notification handler is called, your view controller can retrieve the data from its (persistent) location (file, or property of the singleton mentioned above if you chose that route).
Finally, for the case when the data is already available by the time your ViewController is instantiated, check for data availability and retrieve it if present in e.g. your main view controller's viewDidLoad().
There are many ways you can achieve this,
1. Use Protocols or Delegates
You said "I have a class that is a GCDAsyncUdpSocketDelegate." Write a protocol implementation in that class and make your view controller subscribe to that delegate, when you get delegate call back for your GCDAsyncUdpSocketDelegate do neccessary check and fire your custom delegate to the view controller, pass the data as an argument to the delegate method. In the view controller get the data and update the IBOutlet.
2. Use NSNotificationCenter (dirty way of doing things, gotta be careful though)
In your view controller class add an observer for the notification you are going to post like,
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateUI:", name: "didRecieveDataFromUDP", object: nil)
Add a method in your VC
func updateUI(notifObject:NSNotification){
let responseString = notifObject.object
self.yourOutlet.string = responseString
}
and also remove the notification observer when you are done with the VC.
In the GCDAsyncUdpSocketDelegate delegate class when you get the call back with results fire this notification,
NSNotificationCenter.defaultCenter().postNotificationName("didRecieveDataFromUDP"", object: theResposenObjectYouNeedToSend)
3. Use sharedInstances or mis-use the AppDelegate (not recommended)
Create shared instances or singleton classes that will live through the app lifecycle and use them to store/retrieve data from any class or any thread.
Hope this helps, I wish you choose the first or second way.
Hi all I am doing a course in Udemy, and the code calls for placing code in the viewDidLoad function as shown below:
override func viewDidLoad() {
super.viewDidLoad()
placesArray.append(["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"])
}
The array append should only run once, however, when I segue to another viewController and come back, it runs the code to append again. So I now have an array with 2 rows, both of which are Taj Mahal.
I thought that the viewDidLoad function only runs code once?
Is there a way around this?
Thanks.
Addendum:
I am using Swift, so I don't see any alloc and init while creating and launching the viewController. And weird as it sounds, the video tutorial has it working in the viewDidLoad and the trainer is using the storyboard to segue from the initial table view controller to a map view on a view controller and just has a back button on the map view that segue's back to the table view controller via the storyboard as well. - Could be because I have the latest version of the Swift language and the trainer was using an earlier version, (cause I noticed some slight differences in coding earlier) but you never know. Either way whenever he touches the back button it does not run the append code anymore.
I am trying to get in contact with the trainer as some of the suggestions here, though they are good don't seem to work.
I will put the solution in here once I get in contact with the trainer.
The viewDidLoad method is called when your view controller's view finishes loading. The view will load when a view controller's view property is nil and something attempts to access it.
UIViewController *myVC = [[UIViewController alloc] init];
UIView *aView = myVC.view; // this loads myVC's view; viewDidLoad is called when it completes loading.
If the view has unloaded (usually due to memory limitations), it will be called when the view property is next accessed.
Manipulation of data sets should generally not be done within view methods. Consider moving this to the init of the view controller (or to a different "datasource" class).
I suppose you are trying to do data initialisation in viewDidLoad. If there is no other operation on placesArray before viewDidLoad, then instead of append, what about setting the placesArray directly:
placesArray = ["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"]
Then even if your view is unloaded for some reasons. Taj Mahal will still be added once only.
viewDidLoad is called whenever the view controller's view property is set. When does this happen? It depends on how the view controller is contained:
UINavigationController
- View Controller views are loaded as they are added to the navigation stack and "unloaded" (although the viewDidUnload method is deprecated) as they are removed.
UITabBarController
- View Controller views are loaded as they are added to the tab bar regardless of whether they are on screen or not. They stay loaded as you change from tab to tab.
Depending on your needs and use case, you can create your own view controller container that does what you need. Checkout the Apple docs on the proper way to do this:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
I have a custom segue type (overriding init and perform methods of UIStoryboardSegue) and in init method I instantiate the destination view controller(VC). In prepareForSegue method of source VC I call a method of the destination VC that tries to reload the tableView of the destination VC. The problem is that the table view is not always initialized and I SOMETIMES get a nil de-reference error when I call the reloaddata of the tableview.
The question is that how can I wait till the VC is fully initialized and do not get this error?
I am using swift and would appreciate if you write any sample code for the answer in swift.
just make a call on the viewController's view to force its load.
[viewController view]; //will force a loadView if necessary
///then do what you're trying to do..
I think that the best approach in this case is to add a flag property in the destination VC, something like:
var forceReload: Bool
that you set from prepareForSegue in the source VC. This way, you can choose where to actually perform the data reload from the destination VC (for example, in viewDidLoad or viewWillAppear) by simply checking the value of that flag - of course if the flag is true, don't remember to reset it.
If you also need to pass data from the source to the destination VC, use one or more properties declared in the destination and set from the source.