Get download progress from different ViewController - iphone

I have a VideoListController. It has list of videos with download button. When I press the download button, the control is transferred to DetailViewController. In DetailViewController, I am using AFNetworking to download file.
If I go back to VideoListController from DetailViewController. How can I know progress of download or when download gets completed from VideoListController. I want to know this because based upon that I will reload the list to show play button instead of download.

I think that the best solution for your problem would be to create a custom delegate protocol that the DetailViewController and the VideoListController can use to communicate with each other. Check this post for additional information How to use custom delegates in Objective-C
In a nutshell the strategy is the following:
1. The DetailViewController defines a delegate protocol that it uses to pass events to its delegate
2. The VideoListController becomes the delegate to that it knows whenever an upload has progressed or been completed
3. The VideoListController keeps track of which DetailViewControllers have completed the download
Here is come code:
DetailViewController.h:
#class DetailViewController;
#protocol Delegate <NSObject>
- (void) detailViewController: (DetailViewController *) theDetailViewController didFinishDownloadingVideoWithResults:(BOOL)successful;
#end
#property (nonatomic, weak) id<DetailViewController> delegate;
DetailViewController.m:
Whenever a download is complete do the following:
if ([[self delegate] respondsToSelector:#selector(detailViewController:didFinishDownloadingVideoWithResults:)]){
[[self delegate] detailViewController:self didFinishDownloadingVideoWithResults:YES];
}
Now, in the VideoListController.m make sure you establish yourself as the delegate of the DetailViewController.
[theDetailViewController setDelegate:self];
And implement the delegate method. You can for instance have a dictionary that defines which DetailViewControllers have completed the download:
- (void) detailViewController: (DetailViewController *) theDetailViewController didFinishDownloadingVideoWithResults:(BOOL)successful{
detailViewControllersDownloadInformation[theDetailViewController] = #(successful);
}
Now, whenever you need to check if a DetailViewController did indeed complete a download, all you have to do is check that dictionary
if (detailViewControllersDownloadInformation[theDetailViewController] && detailViewControllersDownloadInformation[theDetailViewController] == #(YES)){
// Did download a video
}
Keep in mind that the solution I provide will only let you know if the download has been completed. If you also want to keep track of the progress you need to pass that as an additional parameter in the delegate. We are also assuming that you keep all of the DetailViewControllers in memory. If you release and reuse them you will need to keep track of which element was downloaded in a different data structure.

I got it working using NSNotificationCenter.
In viewDidLoad of DetailViewController, I added this
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieDownloadDidFinish)
name:#"MovieDownloadDidFinishNotification"
object:nil];
When download gets complete. I call this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"MovieDownloadDidFinishNotification" object:self];
I remove the observer from DetailViewController when when backbutton in navigation controller is clicked
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"MovieDownloadDidFinishNotification" object:nil];
And added method in DetailViewController that is called when download gets completed.
-(void) movieDownloadDidFinish {
NSLog(#"MovieDownloadDidFinish on DetailViewController");
}
Now in viewDidAppear of VideoListController, I added the observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieDownloadDidFinish)
name:#"MovieDownloadDidFinishNotification"
object:nil];
And in viewDidDisappear Of VideoListController, I remove the observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"MovieDownloadDidFinishNotification" object:nil];
And added method in VideoListController that is called when download gets completed.
-(void) movieDownloadDidFinish {
NSLog(#"MovieDownloadDidFinish On VideoListController");
}
In this way, when DetailViewController is visible, the method movieDownloadDidFinish of DetailViewController is called and similarly movieDownloadDidFinish of VideoListController is called when VideoListController is visible.

Related

How to reload another view controller's tableview data

Right now, in FirstviewController, I got a button, when i clicked it, I get the value back using delegate. Right now, I want to send this value to SecondViewcontroller and reload it's tableview data. How to do that? How about use nsnotificationcenter, but i have tried, it's not working. I post the notification in the delegate that implemented in Firstviewcontroller. the code like this:
FirstviewController.m
// delegate that get selected cat
- (void)didSelectSubCat:(SubCat *)cat;
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"DidSelectCat" object:self userInfo:#{#"catId": cat.catId}];
}
SecondViewcontroller.m
- (void)awakeFromNib
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(selectedCat:) name:#"DidSelectCat" object:nil];
}
- (void)selectedCat:(NSNotification *)notif
{
NSLog(#"userinfo: %#", [notif userInfo]);
}
In SecondViewCOntroller create protocol with a methode
#protocol TeamListViewControllerDelegate <NSObject>
-(void)SecondViewController:(SecondViewController*)teamListViewController data:(NSString*)data
#end
declare delegate
#property(nonatomic,assign) id<TeamListViewControllerDelegate> delegate;
In firstViewcontroller follow that protocol and implement that method and inside that method refresh table.
I hope this will help.

Call [tableView reloadData]; on a viewController from a modalViewController

I have a modalViewController that comes up over the top of a viewController with a tableView. When the user clicks a button on the modalViewController I want to reload the tableView within the viewController with this:
[tableView1 reloadData];
I do not want to put the reload in the viewDidAppear or viewWillAppear methods as they get called when i do not need the tableView to reload (i.e. when the user clicks the back button to return to the tableView).
Is there a way to do this?
Try
1) write one method which reloads the table data.
2) Call it on the back button clicked.
This is the classic delegate pattern problem, in your modal view controller you need a delegate reference to the current view controller presenting it
//Modal
#protocol ModalVCDelegate
- (void)tappedBackButton;
#end
#class ModalVC: UIViewController
#property id<ModalVCDelegate> delegate;
#end
#implementation
- (void)backButtonTapped:(id)sender
{
if (self.delegate)
[self.delegate tappedBackButton];
}
#end
Now, in your presenting VC, just process this delegate message
//Parent VC
- (void)showModal
{
ModalVC *vc = [ModalVC new];
vc.delegate = self;
//push
}
- (void)tappedBackButton
{
[self.tableView reloadData];
//close modal
}
You can use delegate . If find it more harder then alternative is to use NSNotificationCenter. You can see accepted answer for Refreshing TableView. This is really very short, easy and understandable way.
using Notification like bellow Method:-
Create NSNotificationCenter at yourViewController's ViewdidLoad Mehod
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(ReloadDataFunction:)
name:#"refresh"
object:nil];
[super viewDidLoad];
}
-(void)ReloadDataFunction:(NSNotification *)notification {
[yourTableView reloadData];
}
Now you can Call this Notification from your modelViewController BackButton or else you want from calling this Refresh notification like putting this line of code:-
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh" object:self];
NOTE: postNotificationName:#"refresh" this is a key of particular Notification
Try to use this one
Make a Button and click on this button and than you can reload your data.
This button make custom and use it on background.
- (IBAction)reloadData:(id)sender
{
[tblView reloadData];
}
You can use NSNotification to refresh table on ViewController.
Inside viewController :
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
Write code in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadMainTable:)
name:#"ReloadTable"
object:nil];
- (void) reloadMainTable:(NSNotification *) notification
{
[tableView reload];
}
Inside ModelViewController:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ReloadTable"
object:nil];
Here you can also send custom object instead of nil parameter. But be care full about removal of NSNotification observer.

Reload UITableView from another tab

I am having trouble trying to reload UITableView cell data which are being loaded from an XML source.
Here is the scenario. App contains tabs, in one of them there is a tableview which gets it's data from an XML file and works just ok, but the thing is when I want to change the feed category and change the XML from another tab I can refresh the current tableview.
For switching between tabs I use
self.tabBarController.selectedIndex = 1;
and pass the category feed to the other tab which I want to load
xmlParser = [[XMLParser alloc] loadXMLByURL:categories];
and it still loads the same old feed, not the new one which has been passed. I checked with NSLog and the feed value passes properly but it just wont load after switching.
I also tried to [self.tableView reloadData]; from both current tab and the category tab and it didn't work either.
You can use NSNotifications to send a notification from your other tab and have a oberver in your tableview that responds to that notification.
Example
(Tab calling the reload of the tableview) put this code whenever you want to reload the data, so when a button is pushed or a download is finished etc.
NSNotification * notification = [NSNotification notificationWithName:#"updateTable" object:nil];
[[NSNotificationCenter defaultCenter] postNotification:notification];
In the UITableViewController / the class with the UITableView, do the following.
in your viewDidLoad add:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateTableView) name:#"backtolist" object:nil];
Then add the function updateTableView
- (void) updateTableView: (NSNotification*) note
{
//Do whatever needs to be done to load new data before reloading the tableview
[_tableView reloadData];
}
Hope this helps
Ophychius was correct in his suggestion to use Notifications. I'm assuming you have all of the data sources for your table view updating when the XML is finished loading. This also assumes you're using dynamic cells. In the class that loads the XML, post a Notification when the new XML is finished loading.
[[NSNotificationCenter defaultCenter] postNotificationName:#"XMLLoaded" object:nil];
In the table view class, register as an observer for the Notification you posted from the XML class.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadTable:) name:#"XMLLoaded" object:nil];
As you can see, this calls a selector when this notification is received. Either call your method where you build the table, or create another simple method to call reloadData from.
-(void)reloadTable:(NSNotification *)notif
{
NSLog(#"In ReloadTable method. Recieved notification: %#", notif);
[self.tableView reloadData];
}
Finally (as Leonardo pointed out below), in your viewDidUnload (or dealloc for ios6) method, remove the class as an observer of that notification.
- (void)viewDidUnload
{
[super viewDidUnload];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
I am just guessing, without having seen the rest of the code.
I suppose your table view has an NSArray datasource, did you make sure that your array datasource is updated too ? Does your xml parser, or controller, transfer those data to the NSArray ?
Because if you call reloadData it is just going to refetch the same array. And if it is not updated, you would get old data.

Call Selector method from another Class - NSNotificationCenter

I would like to know how I can call the selector which is in another class when notification is posted.I am on tabbarcontroller.
The FirstViewController, SecondViewController are tab bar items
Inside `FirstViewController` I have the following
-(void)viewdidload
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:kProductPurchasedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector: #selector(productPurchaseFailed:) name:kProductPurchaseFailedNotification object: nil];
}
- (void)productPurchased:(NSNotification *)notification {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
NSString *productIdentifier = (NSString *) notification.object;
NSLog(#"Purchased: %#", productIdentifier);
[appDelegate.myDownloadablePoemsArray addObject:productIdentifier];
[self.tabBarController setSelectedIndex:3];
}
- (void)productPurchaseFailed:(NSNotification *)notification {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
SKPaymentTransaction * transaction = (SKPaymentTransaction *) notification.object;
if (transaction.error.code != SKErrorPaymentCancelled) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Error!"
message:transaction.error.localizedDescription
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil] autorelease];
[alert show];
}
}
The above code is working fine. Now what the issue is, I want to call the same selector method from my another view say for example I have a view controller named SecondViewController, in that I am adding the same notification observer.
but the selector method is not called in the FirstViewController.
Inside SecondViewController I have the following
-(void)viewdidload
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(productPurchased:) name:kProductPurchasedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector: #selector(productPurchaseFailed:) name:kProductPurchaseFailedNotification object: nil];
}
But I want to call the selecor methods from FirstViewController;
Please let me know , is that possible ? And how can I do that?
Thanks a lot
in the SecondViewController change the self as observer to the pointer of the FirstViewController, because the instance of FirsViewController has the methods.
inside SecondViewController.m you must use these lines:
- (void)viewdidload {
[[NSNotificationCenter defaultCenter] addObserver:firstViewController selector:#selector(productPurchased:) name:kProductPurchasedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:firstViewController selector: #selector(productPurchaseFailed:) name:kProductPurchaseFailedNotification object: nil];
}
BUT! AND THIS IS THE POINT.
if the FirstViewController is already a valid and loaded view controller in the memory with the methods as you've mentioned above, and it is an observer already for these notifications in the NSNotificatioCenter, you don't need to add again it to the NSNotificationCenter because the FirstViewController can receive and will receive the desired notification still. (it is just not shown, because an other view controller covers it.)
if the FirstViewController is not exists yet when the SecondViewController is, you cannot reach any instance method called from an another class because the FirstViewController was not instantiated before, and you cannot add it to the NSNotificationCenter as well.
CONCLUSION
it would be better to isolate the purchase callbacks into a third class what you can use for every independent view controller, according to the spirit of the OOP and MVC.
If your view controllers are the roots of tab-bar controllers, once they are loaded the first time, they stay around unless manually replaced.
Thus, when you install the notification handler in the first controller, unless you remove the notification handler, it will still get them, even when the second controller is onscreen.
Now, it may get unloaded due to memory pressure, or by the custom tab-bar-controller code. However, it's highly unusual for a tab-bar controller to deallocate one of its view controllers, so your installed notification handlers will be around until you cancel them.
In fact, if both view controllers register for the notifications, then they will both get them.
You are registering in viewDidLoad so the first one will get registered immediately, as it will be loaded and displayed as the initial controller. It will continue to receive those notifications.
When the second one loads, it will also register. Now both view controllers are receiving the notifications. When you go back to the first view controller, they will both still be getting the notifications.

How can my main view controller know when the web service call finished

Hi I have an web service based app that will interchange data with our server. Since I have a dedicated class doing my work, the main view controller will actually call the worker every time. The worker itself knows when the connection finished since it is a NSURLConnectionDelegate. However I need to inform the main view controller whenever the work is done. The worker is a delegate of main view controller so it knows when it need to start working. Please help me.
You should do the other way around.
First declare your worker object as a member of your main view controller(and of course, make it a property), then from you main view controller, you can just call [self.worker sendRequstToServer] to send the request.
Second, within your main view controller, wherever you initialize your work object, don't forget to put self = worker.delegate.
Third, declare a delegate method in your worker class, something like -(void)webServiceCallFinished(), and call [self.delegate webServiceCallFinished] in your worker's -connectionDidFinishLoading() (you might wanna do in -parserDidEndDocument() if you are using NSXMLParer to parse xml)
Last, you wanna declare your main view controller as the delegate of your worker class and implement -webServiceCallFinished().
Hope this helps.
You can do it in two ways:
Approach #1:
User Local notifications.
In the mainclass add an observer into LocalNotification Center in following way.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(jobDone) name:#"WORKERJOBOVER" object:nil];
And in the worker class when the job is done, post nofitication to fire the selector:
[[NSNotificationCenter defaultCenter] postNotificationName:#"WORKERJOBOVER" object:nil];
Approach #2:
You can create a protocol of your worker class and add a method in the protocol which you can call on the delegate when your job is done in worker.
WorkerClass.h
//WorkerClass.h
#protocol WorkerDelegate
#interface WorkerClass: NSObject
#property (nonatomic, assign) id<WorkerDelegate> delegate
- (void)JobInProcess;
#end
#protocol WorkerDelegate
- (void)MyJobIsDone;
#end
WorkerClass.m
//WorkerClass.m
#implementation WorkerClass
#synthesize delegate = _delegate;
- (void)JobInProcess
{
//When job over this will fire callback method in main class
[self.delegate MyJobIsDone];
}
MainClass.h
//MainClass.h
#import WorkerClass.h
#interface MainClass: NSObject <WorkerDelegate>
#end
MainClass.m
//MainClass.m
#implementation MainClass
- (void)MyJobIsDone
{
//Do whatever you like
}
#end
In viewDidLoad: of your main view controller, set it as a notification observer with [NSNotificationCenter defaultCenter]:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(webServiceContacted:) name:#"webServiceConnectionSuccessful" object:nil];
Then when your data was successfully interchanged with the server, post a notification within the network completion block:
[[NSNotificationCenter defaultCenter] postNotificationName:#"webServiceConnectionSuccessful" object:nil];
And webServiceContacted: could look like this:
-(void)webServiceContacted:(NSNotification *)note
{
//do stuff here
}
I would fire a notification from the background worker that you can listen for from your main view controller
In your View add the following when you want to listen.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(operationComplete:) name:#"operationComplete" object:nil];
In your background View notify that the event completed.
[[NSNotificationCenter defaultCenter] postNotificationName:#"operationComplete" object:nil];
Finally don't forget to remove the observer
[[NSNotificationCenter defaultCenter] removeObserver:self];