Update previously pushed view controllers via delegation IOS6 - ios

I'm still getting the hang of IOS delegation, so I hope this question makes sense as I explain what I want to do...
What I want to do is download some JSON Data on a background thread as soon as my app first runs. The data will then be parsed and then update a global variable. Once that happens I want all previously pushed view controllers to update their content based on the data that has been downloaded and parsed.
My proposed way of solving this problem would be to have either my app delegate or my custom Navigation Controller subclass be a delegate for a custom JSON object that will parse the data. The delegate will run a protocol method that updates a global variable once the parsing is complete.
Now once this variable has been set, it will be available to any view controller that is pushed on to the navigation stack. I also want to update the view controllers that have been previously pushed so that their content can be updated.
I know I can make the top view controller an active delegate that will run a protocol but what about the previously pushed view controllers? Is this even possible or is there another way to make previously pushed view controller update their content?

In this case you don't really want to use a delegate. You want several objects to listen to a specific event, so use NSNotificationCenter instead.
When your parser finished parsing the JSON do the following:
[[NSNotificationCenter defaultCenter] postNotificationName: #"FinishedDataParsing" object:self userInfo:nil;
This way you also won't need a global variable. You could either make your parsed data accessable in your parser object or use the userInfo dictionary to pass some information to the notification receiver.
Everywhere you want to do something when your parsing finishes you first have to register as an observer (you could do that in viewDidLoad):
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(dataParsed:)
name:#"FinishedDataParsing"
object:nil];
And obviously you have to implement your callback method to do what ever you want to do with your parsed data.
- (void)dataParsed:(NSNotification *)notification {
// Do this to access the user info.
NSDictionary *userInfo = notification.userInfo;
// Or access your data parser object.
DataParser *parser = (DataParser *)notification.object;
}
Also you should deregister as observer when you no longer need to get notified (e.g. in dealloc)
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}

Related

Reload UITableView from a NSObject

I have a question concerning iOS. I couldn't find an answer to my question online but maybe one of you know the answer.
Im working on an app which pulls data from an online database. A NSObject Class is called by a timer in the background of the app. I want it to tell one of my view controller to reload its tableview if it finds new data. Like it is supposed to check for data not caring in which view controller im in. But when I'm in the specific one with my table view it should reload the table view if it found new data.
I've tried to create an instance of the tableviewcontroller in my NSObject and call the reload table view function front there, but that doesn't work :/
I apologise for my poor explanation, I'm not a native speaker.
Thank you very much :)
You can use two approaches:
post a NSNotification in your class that gets more data and observes
this notification in your class that the TableView is initialized.
make a delegate approach between the two classes and fire a method when you have the new data.
I would go with the first option:
a. in your data loading class:
NSNotification *notification = [NSNotification notificationWithName:#"newDataFetched" object:anyObject];
[[NSNotificationCenter defaultCenter] postNotification:notification];
b. in your listener class:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(aMethodToReloadTheTableView:)
name:#"newDataFetched"
object:nil];

What is the best way of making more than one object a delegate of another object?

I'm creating an iOS app with 4 tabs (4 view controllers) that gets CoreLocation updates and displays the location along with some other data in various representations (table view, map view, etc)
I only want to use one CoreLocationManager, so I can only have one delegate, but 4 view controllers need to know about the updates so that the visible view can be updated
What is the best way to let my view controllers know that there has been a location update?
The simplest is to post a notification rather than using a delegate. Delegates are 1:1 where as notifications are 1:many. Problem is you still need at least one delegate which will post the notifications.
If you want, you can create an NSProxy object which you can set as the location manager delegate and which internally holds a list of other delegates and forwards all of the received method calls to all of the internally managed delegates.
The BEST method is probably to simply use the notification center. Rather than a #protocol, put a NSString * const you're using for the notification:
NSString * const kMyNotificationString #"MyNotificationString"
Now, when the would-be delegates need to respond, it's as simple as:
[[NSNotificationCenter defaultCenter] postNotificationName:kMyNotificationString
object:nil];
Any object that needs to respond to this notification can #import this header file (so it gets the NSString * const-ed notification name and then and start listening to the notification as such:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMethod)
name:kMyNotificationString
object:nil];
Just don't forget to remove observers when (or before) you dealloc.
Alternatively, there's this approach... but it's not really that great, and I'd actually recommend against it.
Rather than a single delegate property, you could have a NSArray property that handles the "delegates" (make sure you have an "addDelegate" method that only adds weak references to the array). Then, when you need all of these objects to respond to a change:
for (id<MyProtocol> object in self.arrayOfDelegates) {
if ([object respondsToSelector:#selector(myMethod)]) {
[object myMethod];
}
}
The easiest way is implement one delegate that forwards messages as notifications, this way any number of objects can subscribe as observers for those notifications.

Refreshing an Arbitrarily Deep UITableView

I am wondering if there is a good, general paradigm for setting up the refresh action in an arbitrarily deep UITableView. I'm speaking specifically of the refreshControl property of UITableViewController.
One layer is pretty easy and obvious since you just refresh your model and then call [self.tableView reloadData], but even two layers things start conflicting with my notions of good OO programming.
So if the user pulls the table view down to refresh from an arbitrarily deep table, it strikes me that it does absolutely no good to just reload the data from the parent view (which doing, it seems to me, isn't a good OO setup since a child controller isn't supposed to be able to talk directly to the parent). The refresh call needs to be passed back up through the UITableViewControllers until either the top, or some point that would conceivably be the deepest point where the model might have changed, and then push the new, refreshed, data back down through the child UITableViewControllers until it reaches the controller which originated the refresh (checking that that path even still exists at each jump).
The only way I'm coming up with doing this is to have a property in each controller called parentView which is set during prepareForSegue and a method called something like dataForChildView.
-(IBAction)refreshTableView {
[self.refreshControl beginRefreshing];
//self.parentView would be nil at the topmost TVC
if (self.parentView) {
[self.parentView refreshTableView];
self.data = [self.parentView dataForChildView];
}
//refresh data
[self.tableView reloadData];
[self.refreshControl endRefreshing];
}
But this seems to me to violate the basic rules of nested controllers, for the reasons I state above.
I haven't yet learned about NSNotifications, but this strikes me as a possible approach. However, since I'm finding nothing online about this question, I wanted to ask before I commit a bunch of time to figuring out if NSNotifications will let me do this. Or maybe I'm missing some even simpler approach.
Edit: the assignment that this is for is set up such that only the topmost UITableViewController has access to data from the model; all lower controllers are passed their data in prepareForSegue. I am starting to think that perhaps this is the problem; there needs to be a model that can be accessed from any level down in the UITableView. Would that be correct?
You could for example refresh your model (which can be passed along to the view controllers) and reload the table views on viewWillAppear.
Also the way with notifications you mentioned will work.
Register for notifications in your viewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(selectorThatReloadsTheTableView:)
name:#"NameOfYourNotification"
object:nil];
Don't forget to remote your view controller from the notification center by calling
[[NSNotificationCenter defaultCenter] removeObserver:self];
Now all you need to do is post the notification once the user pulls-to-refresh:
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification"
object:nil];

NSNotification not sent/received

I've been trying to figure this out for a whole day now and I'm starting to become desperate...
So here is my problem:
I have a table view controller showing a list of files. The user can download new files to this list. I handle the downloads asynchronously in a separate class. When a new file has been downloaded I want to notify the table view controller, so it can update the list. The delegate pattern doesn't really fit here, because multiple instances have to be notified, so I want to use NSNotificationCenter.
In the view controller's viewDidAppear: method I register as an observer:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(downloadComplete:)
name:kDownloadCompleteNotification
object:nil];
This is called definitely before posting the notification, because the user has to tap the add button on the view controller to start a new download. When the download is done I post the notification (in the DownloadManager class):
[[NSNotificationCenter defaultCenter]
postNotificationName:kDownloadCompleteNotification
object:self];
However, my notification handler method in the view controller is never called. I added breakpoints and it's just not called. I also tried to set the object to nil. No difference.
- (void)downloadComplete:(NSNotification *)notification {
NSLog(#"Inserting new files into table view.");
}
Both, registering and posting the notification is done on the main thread (I don't think that matters anyway). The view controller is not released or anything. I didn't make any typo as far as I can tell. The name strings are the same constant.
If set the name argument to nil when registering as an observer, I receive all kind of (system) notifications, but not my own ones. However if I post a test notification right after registering (in viewDidAppear) it works.
I don't know what else I should check... Any hint is very appreciated. Thanks!

ios superview and subview

I have a superview and I add a subview to make a selection. In the superview (main view) I do the following:
[self.view addSubview:cityViewController.view];
In the cityView, when I have done what I need to do, I just do
self.view removeFromSuperView.
The question is, from within the superview, how can I tell when the subview has removed itself.
There's a few ways, but honestly since the current view controller (let's call it main) is just adding the cityViewController's view, keep the handling of adding/removing the views to the current view controller, and just have the main controller call [cityViewController.view removeFromSuperView]
This way you can execute whatever code you want when it receives this notification (be it a method that fires or a UINotification).
-- edit for sample UINotification code --
main.m
...
//Define cityViewController as an iVar and alloc/init it
[[UINotificationCenter defaultCenter] addObserver:self selector:#selector(didFinishView:) name:#"DidFinishView" object:nil];
[self.view addSubview:cityViewController.view];
...
-(void) didFinishView:(NSNotification *)notification {
[cityViewController.view removeFromSuperView];
}
CityViewController.m
-(IBAction) doneButtonClick:(id) sender {
[[NSNotificationCenter defaultCenter] postNotificationName:#"DidFinishView" object:nil];
}
The quick answer is your view should not be removing itself. It's better practice for a view to communicate user interactions to a relevant controller through an interobject communication mechanism. The most common methods are direct messaging, protocols and notifications. The iOS framework uses all of these and there are great docs explaining them. Here's a brief summary:
Direct messaging. Use this when an object needs to communicate with a specific object of a known type. For example, if MyView is always contained in MyViewController and needs to send messages to it you can add a property to the MyView class that keeps a pointer to the specific MyViewController object. You can then send a message from myView to it's myViewController via [myView.myViewController userDidTapSaveButton] or whatever.
Protocols. A protocol defines a contract between objects that don't know anything about each other other than that they abide by the contract. For example, UITableView knows that it's delegate conforms to the UITableViewDelegate protocol and it can send the required protocol messages to it's delegate. Any object can conform to the UITableViewDelegate protocol.
Notifications. Notifications allows an object to post notifications through a central mechanism (NSNotificationCenter) that other objects can observe and respond to. Notifications are useful when the object posting the notification doesn't know or care what objects are observing it's notifications.
I'd read the relevant docs and other Q&A on SO about these methods. I'd also study up a bit on the MVC (Model/View/Controller) design pattern so you get more comfortable knowing where to put app logic. Generally, a view should only be responsible for it's display (based on properties set by it's controller), observing/responding to user actions, and notifying it's controller for relevant actions.

Resources