I am uploading data on server from my NSObject class, now i want to show MBProgressHUD when that data is uploading, I know how to show MBProgressHUD with ViewController but dont know how to show it with NSObject class.
AppDelegate* delegate = [[UIApplication sharedApplication] delegate];
[delegate.window addSubview:HUD];
...
[HUD removeFromSuperview];
There are several ways to solve this kind of problems. The most common one is using the delegation pattern, although you can use blocks, KVO, or Notifications.
You should start by creation a protocol, so you can communicate between your UIViewController and your NSObject that makes the calls. Although you don't need one to create this communication, you should use it to have a more flexible code.
Normally, I would do something like this:
#protocol CommunicationDelegate <NSObject>
#required
-(void)communicationSucceed;
-(void)communicationFailedWithError:(NSError*)anError;
Inside your NSObject, you will have a weak reference for an object that complies with the protocol CommunicationDelegate. In your .h you should have something like this:
#property(nonatomic, weak) id <CommunicationDelegate> communicationDelegate;
Just before you actually start your work with the NSObject, you should do:
myObjectThatWillDoSomething.communicationDelegate = self;
At this moment you have a valid reference between your UIViewController and your NSObject. Inside your UIViewController's .h file, add this:
#interface myViewController : UIViewController <CommunicationDelegate>
So your UIViewController complies to the CommunicationDelegate protocol.
You can now start your MBProgressHUD from your UIViewController. Once your NSObject has done his work, you can either call:
[communicationDelegate communicationSucceed];
or
[communicationDelegate communicationFailedWithError:anError]; //anError is used to describe what went wrong
Once (one of) those methods are called, you can remove your MBProgressHUD. Understand that this methods are called inside your UIViewController.
When you upload your NSObject, a view is displayed, right? So display your HUD in that view. You may need to create a delegate to notify the view when the download begin, when it ends and if there is an error.
Create a delegate protocol on your uploader object
#protocol UploaderThingyDelegate <NSObject>
-(void)stuffStarted;
-(void)stuffEnded;
#end
#interface UploaderThingy : NSObject
#property (weak) id<UploaderThingyDelegate> delegate;
Set your relevant View or ViewController as the uploaders delegate and trigger the MBProgressHUD add/remove there.
Use NSNotification Center to stop Indicator , Declare NSNotification with listening method in view controller . And post notification from Webservice file . Stop indicator in listening method of Notification center .
This link will help you
These Below Code Works fine in NSObject File
Step 1: Download the https://github.com/jdg/MBProgressHUD
Step 2: Add the Delegate MBProgressHUDDelegate
Step 3: Declare Instance MBProgressHUD *HUD;
Step 4: Write Code Where u want:
HUD = [[MBProgressHUD alloc] initWithWindow:[UIApplication sharedApplication].keyWindow];
[[UIApplication sharedApplication].keyWindow addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Processing";
[HUD show:YES];
Related
I have two view Controllers in my project ViewController, SettingsView. Here I am trying to update the ViewController's label, when i click on the SettingsView's back button. NSLog is working fine, but the label is not updating...
Please help me....
SettingsView.m
-(IBAction)backToMain:(id) sender {
//calling update function from ViewController
ViewController * vc = [[ViewController alloc]init];
[vc updateLabel];
[vc release];
//close the SettingsView
[self dismissModalViewControllerAnimated:YES];
}
ViewController.m
- (void)updateLabel
{
NSLog(#"Iam inside updateLabel");
self.myLabel.text = #"test";
}
Could you please tell me whats wrong with my code? Thank you!
You have to implement protocols for that. Follow this:
1) In SettingView.h define protocol like this
#protocol ViewControllerDelegate
-(void) updateLabel;
#end
2) Define property in .h class and synthesis in .m class..
#property (nonatomic, retain) id <ViewControllerDelegate> viewControllerDelegate;
3) In SettingsView.m IBAction
-(IBAction)backToMain:(id) sender
{
[viewControllerDelegate updateLabel];
}
4) In ViewController.h adopt protocol like this
#interface ViewController<ViewControllerDelegate>
5) In viewController.m include this line in viewDidLoad
settingView.viewControllerDelegate=self
Your label is not updating because , you are trying to call updateLabel method with a new instance.
You should call updateLabel of the original instance of viewcontroller from which you have presented your modal view.
you can use a delegate mechansim or NSNotification to do the same.
Delegate mechnaism would be clean. NSNotification is quick and dirty.
You are not exactly calling the correct vc. This is because you are creating a new instance of that class and calling the updateLabel of that instance.
You have a few options.
Either implement it as a delegate callBack (delegate messagePassing, or delegate notification - however you want to call it) to notify that class instance to call the updateLabel method.
Use the original instance VC as a dependency injection into the class that you are on right now, and use that instance to call the updateLabel
Use NSNotifications / NSUserDefaults to communicate between viewControllers and setup a notification system for your actions. This is quite easy, but not really great in the long run.
I would RECOMMEND option 1 (or) option 2.
Simply declare like this in SettingsView class:
UILabel *lblInSettings;// and synthesize it
Now assign like below when you presenting Settings viewController:
settingsVC.lblInSettings=self.myLabel;
Then whatever you update in lblInSettings it will be present in MainView obviously....
no need for any delegate methods or updating methods.
Means if you assign at the time of dismissing like
lblInSettings.text=#"My new value";
then self.myLabel also will be updated.
Let me know if you have any queries?
I have a UIViewController called ShowListViewController that uses a Modal View Controller to push another view onto the stack:
AddShowViewController *addShowViewController = [[AddShowViewController alloc] init];
[addShowViewController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentModalViewController:addShowViewController animated:YES];
I would then like to call my method populateTableData of the ShowListViewController class when the addShowViewController disappears.
I would think that the answer found here would work, but it doesn't. My method populateTableData is not detected as an optional method to use.
Essentially my questions is: How do I detect when a Modal View Controller disappears so as to call a method within the class that pushed it on the stack?
This may not be a best solution, but can do what you want at this time.
In your showlistcontroller add an instance variable like
BOOL pushedView;
#implementation ShowListViewController
and before you do the modal presentation set its values as YES like
pushedView = YES;
[self.navigationController presentModalViewController:popView animated:YES];
in the viewWillAppear of ShowListViewController you can detect whether it is appearing because pop getting dismissed or not like
if (pushedView) {
NSLog(#"Do things you would like to on pop dismissal");
pushedView = NO;
}
I think you would like something like this.
You make a delegate inside ur modalVC like this:
#protocol ModalViewDelegate <NSObject>
- (void)didDismissModalView;
#end
and implement it in your MainVC like this:
#interface MainViewController : UIViewController <ModalViewDelegate>
{
Then u will make a delegate property in your modalVC like this:
#interface ModalShizzle : UIViewController
{
id<ModalViewDelegate> dismissDelegate;
}
You set the dismissDelegate of your ModalVC to your MainVC and then you make the delegate method. Before you dismiss it however you will call the ModalVC to do one last thing. (which is populate your table). You will call for the data inside your MainVC and then do whatever you feel like it, just before you dismissed your modalVC.
-(void)didDismissModalView
{
//call ModalVC data here...
//then do something with that data. set it to a property inside this MainVC or call a method with it.
//method/data from modalVC is called here and now u can safely dismiss modalVC
[self dismissModalViewControllerAnimated:YES];
}
Hope it helps ;)
OK so it appears that in Apple's template for Utility App's they ignore what the docs for [UIViewController][1] say and actually go out of their way to call dismissModalViewControllerAnimated: from the UIViewController that pushed the modal view onto screen.
The basic idea in your case will be
Define a protocol for AddShowViewControllerDelegate
Make ShowListViewController implement this protocol
Call a method on the delegate to ask it to dimiss the modal view controller
For a full example just create a new project with Utility template and look at the source for FlipsideViewController and MainViewController
Here is an example adapted for your needs:
AddShowViewController.h
#class AddShowViewController;
#protocol AddShowViewControllerDelegate
- (void)addShowViewControllerDidFinish:(AddShowViewController *)controller;
#end
#interface AddShowViewController : UIViewController
#property (nonatomic, assign) id <AddShowViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
#end
AddShowViewController.m
- (IBAction)done:(id)sender
{
[self.delegate addShowViewControllerDidFinish:self];
}
ShowListViewController.h
#interface ShowListViewController : UIViewController <AddShowViewControllerDelegate>
{
...
}
ShowListViewController.m
- (void)addShowViewControllerDidFinish:(AddShowViewController *)controller
{
[self dismissModalViewControllerAnimated:YES];
[self populateTableData];
}
I've been trying to use a UIButton action to call a method in a different class (AppViewController). I first tried creating an instance of the view controller in the UIButton's calling class (caller.m) and then calling the method, but that kept resulting in EXC_BAD_ACCESS.
I'm realizing I need to point to the same instance of the view controller and am now trying to make sure the view controller instance is properly declared in caller.m.
I have a declaration of AppViewController *viewController in the AppDelegate, so my thought is to refer to that same instance from caller.m.
#import "caller.h"
#import "AppDelegate.h"
#implementation caller
- (id)initWithFrame:(CGRect)frame {
...
[btnSplash addTarget:viewController action:#selector(loadSplashView) forControlEvents:UIControlEventTouchUpInside];
....
}
However, viewController still shows up as undeclared. I tried a few other things, but know I'm probably missing something basic.
::::UPDATE::::
Okay, so it turns out I needed to create the following so the target "viewController" was actually declared and pointing to the correct instance:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
AppViewController* viewController = appDelegate.viewController;
The method in the view controller class is now being properly called.
For a more clearly explained and more general version of this question, go here:
Objective-c basics: Object declared in MyAppDelegate not accessible in another class
There are multiple ways for objects to initiate actions, communicate with other objects and/or observe changes they are interested in including:
UIControl target/action bindings
Protocols
Key/Value Observing (KVO)
Notifications
I don't think notifications are what you want in this case. Notifications are most appropriate when the object posting the notification does not care what object(s) are observing the notification and there can be one or more observers. In the case of a button press you would typically only want a specific object to handle the action.
I would recommend using a protocol. You'll see lots of protocols in use in the iOS frameworks, basically any class that has a delegate property usually defines a protocol that delegate objects need to conform to. The protocol is a contract between the two objects such that the object defining the protocol knows that it can communicate with the object conforming to the protocol with out any other assumptions as to its class or purpose.
Here's an example implementation. Apologies if any typos/omissions.
In caller.h (I assumed caller is a UIViewController):
#class Caller
#protocol CallerDelegate
- (void)userDidSplashFromCaller:(Caller *)caller;
#end
#interface Caller : UIViewController
id <CallerDelegate> delegate;
#end
#property (nonatomic, assign) id <CallerDelegate> delegate;
#end
In caller.m:
#implementation Caller
#synthesize delegate;
- (void)viewDidLoad {
// whatever you need
// you can also define this in IB
[btnSplash addTarget:self forAction:#selector(userTouchedSplashButton)];
}
- (void)dealloc {
self.delegate = nil;
[super dealloc];
}
- (void)userTouchedSplashButton {
if (delegate && [delegate respondsToSelector:#selector(userDidSplashFromCaller:)]) {
[delegate userDidSplashFromCaller:self];
}
}
in otherViewController.m:
// this assumes caller is pushed onto a navigationController
- (void)presentCaller {
Caller *caller = [[Caller alloc] init];
caller.delegate = self;
[self.navigationController pushViewController:caller animated:YES];
[caller release];
}
// protocol message from Caller instance
- (void)userDidSplashFromCaller:(Caller *)caller {
NSLog(#"otherVC:userDidSplashFromCaller:%#", caller);
}
[EDIT: CLARIFICATIONS]
I realized after looking at your question and code again that I made some assumptions that may not be true in your code. You most likely should still use a protocol but the exact way to integrate my example depends on your app. I don't know what class Caller is in your app but whatever it is, it is dealing with UIButtons so it is most likely a view controller or a view.
Your comment about not having the correct instance of your appViewController makes me wonder if you understand the difference between classes and instances of a class. If my answer doesn't help you, please post some more code showing how you create and present your view controller as well as how you are configuring the button and I can try to clarify my answer.
You should post a NSNotification when clicking the button that will be caught and executed in the AppViewController.
So this should be:
In the sender class:
[btnSplash addTarget:self
action:#selector(loadSplashView)
forControlEvents:UIControlEventTouchUpInside];
-(void)loadSplashView:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"notif_name" object:some_sender_object];
}
In the target class:
Register to get the notification at view's load:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(some_function:) name:#"notif_name" object:nil];
Define the action to take in this class:
-(void) some_function:(NSNotification *)notif {
//do something
// to access the object do: [notif object]
}
Communication between various objects of your app is a design level decision. Although iOS provides neat ways of doing this at code-time (properties) - it's through hard-coupling.
True inter-object communication does not bind objects at compile time - this is something that can only be assured by following design patterns.
Observers & Delegates are two most commonly used patterns, and it's worth for you to learn when to use which one - see Observer vs Delegate.
Newbie question about design patterns in objC.
I'm writing a functionality for my iphone app which I plan to use in other apps too. The functionality is written over two classes - Viewcontroller1 and Viewcontroller2. Viewcontroller1 is the root view of a navigation controller and it can push Viewcontroller2. Rest of the app will use only ViewController1 and will never access Viewcontroller2 directly. However, triggered
by user events, Viewcontroller2 has to send a message to the
rest of the app.
My question is what is the best way of achieving it?
Currently, I use two level of delegation to send the message out from Viewcontroller2. First send it to Viewcontroller1 and then let Viewcontroller1 send it to rest of the app or the application delegate. So my code looks like -
//Viewcontroller1.h
#protocol bellDelegate
-(int)bellRang:(int)size;
#end
#interface Viewcontroller1 : UITableViewController <dummydelegate> {
id <bellDelegate> delegate;
#end
//Viewcontroller1.m
#implementation Viewcontroller1
-(void)viewDidLoad {
//some stuff here
Viewcontroller2 *vc2 = [[Viewcontroller2 alloc] init];
vc2.delegate = self;
[self.navigationController pushViewController:vc2
animated:YES];
}
-(int)dummyBell:(int)size {
return([self.delegate bellRang:size]);
}
//Viewcontroller2.h
#protocol dummyDelegate
-(int)dummyBell:(int)size;
#end
#interface Viewcontroller2 : UITableViewController {
id <dummyDelegate> delegate;
#end
//Viewcontroller2.m
#implementation Viewcontroller2
-(int)eventFoo:(int)size {
rval = [self.delegate dummyBell:size];
}
#end
That's the proper way of doing things ! If all your delegate method are to be invoked on viewController2, you could have only one protocol and directly assigned the delegate from viewController1 to viewControler2 but it will block you the first time tou need to invoke the delegate from viewControler1.. bad design then !
I would say the implementation is fine, but this isn't necessarily a case where you should add another level of abstraction to get reusability, because it's just a general and recommended way to pass messages around objects, i.e. "delegation" Apple's documentation on delegation.
Also, regarding your problem case where you have to send a message "to the rest of the app", you should consider notifications, they may be very time-saving in some situations. (Apple's documentation on notifications, SO Question about Delegation vs Notifications)
We have 2 files 'MainViewController' and 'View01'
The first file addSubView the second one.
In the first file we have a function named displayView.
I want to be able to call this function from the View01 file is the following code correct
part of View01 file
#import "MainViewController.h"
- (IBAction) changeView:id(sender){
MainViewControlle *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate displayView:2];
}
is this the correct way to do it?
at the moment I use the NSNotificationCenter to send activate the functions :)
You should try using delegate methods :-)
It works like that :
// Delegate
#protocol MyViewDelegate <NSObject>
- (void)myViewAskedForSomethingWithOrWithoutParameters;
#end
In your view you must have this parameter :
id<MyViewDelegate> delegate;
And then in your Controller you must implement the delegate
#interface MainViewController : UIViewController <MyViewDelegate> {
}
In your implementation you need to add a :
myView.delegate = self;
And finally in your view when you need to call the function, just do :
[ delegate myViewAskedForSomethingWithOrWithoutParameters ];
Good Luck !
If I'm not mistaken, your code shouldn't work. Did you try it your self?
[[UIApplication sharedApplication].delegate]
will return your AppDelegate, called something like MyAppDelegate and not MainViewController. However, depending on the template you used or created, your AppDelegate might contain a MainViewController-property, most likely called viewController so you could use
[appDelegate.viewController displayView:2];
This is the quick way to do it. For a more tidy way, see Vinzius' answer.