Objective-C – Message a view controller from application delegate - objective-c

I want to download some data in the application delegate in - application:didFinishLaunchingWithOptions:
After I have downloaded some data I want to set this data to an NSArray property in a view controller. If I have a synthesized property of NSArray (nonatomic, retain) called data I would like to do [viewController setData:downloadedData];
How would I call the active viewController instance from the application delegate?
My application structure is a Tab Bar Controller as root controller.

You'll want to use NSNotificationCenter which will essentially broadcast a message to all objects that have subscribed to that particular message.
In your view controller subscribe to the notification:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(downloadedData:)
notificationName:#"DownloadedData"
object:data];
- downloadedData:(NSNotification *)notification {
self.data = notification.object;
}
And in your app delegate send the notification to the subscribers:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"DownloadedData"
object:data];

Add delegateComplete property in your app delegate class:
//define ivar
id delegateComplete;
//define property
#property (nonatomic, retain) id delegateComplete;
//synthesize
#synthesize delegateComplete;
In init method or viewDidLoad of your viewController do following:
MainClass *appDelegate = (MainClass *)[[UIApplication sharedApplication] delegate];
appDelegate.delegateComplete = self;
replace MainClass with your app class. After downloading is complete, do following in your app delegate:
[delegateComplete loadingCompletedWithData:data];
Dont forget to add this method in your viewController:
- (void)loadingCompletedWithData:(NSData *)data
What happens is that your view controller registers to your app delegate. When loading completes, if your view controller has registered, call loadingCompletedWithData. Proper way of doing this would be through a protocol.

Related

How to Access a UIPickerView Value from Outside the View Controller that Owns the UIPickerView?

I have a picker View declared in the ViewControllerA (in Xib file). This Xib is loaded as a custom cell in ViewControllerB's tableView. If I change the value of Picker View, How can I access this changed value in ViewControllerB.
You can access the value of the UIPickerView by exposing a property on ViewControllerA... If there are set circumstances when viewControllerB needs to know the value, under the control of viewControllerB, then viewControllerB can check the property on viewControllerA at those times...
However, perhaps you're asking a more general question about communication - with the specific example of ViewControllerB needing to know about a change to the UIPickerView that's inside ViewControllerA...
This article on Communication Patterns is worth reading. It covers KVO, Notifications, Delegation, Blocks and Target/Action.
There's a flow-chart near the middle of the article that can help to evaluate what communication strategy to use in a given situation.
From what you've written, it sounds like you could use either KVO (key-value observing) or delegation.
I tend to use delegation in scenarios when one UIViewContoller wants to know about changes made in another UIViewController, e.g. when viewControllerC presents viewControllerD - and wants to know about changes made in viewControllerD.
In your case, you might use a delegate method along the lines of:
- (void)pickerViewValueDidChange:(NSString*)newValue;
That delegate method would be part of an #protocol. Something like:
#protocol ABCPickerViewDelegate
- (void)pickerViewValueDidChange:(NSString*)newValue;
#end
See Working with Protocols if this is new to you...
viewControllerB would conform to that protocol. viewControllerA would have a property that conforms to the protocol, something like:
#property (weak, nonatomic) id <ABCPickerViewDelegate> pickerDelegate;
Then, when the UIPickerView value changes within viewControllerA - it can call the delegate method... And, viewControllerB would then know that the change has occurred and what the value is.
you can get picker value from viewController A on ViewController B using various ways like static getter setter , App delegate class as I mentioned here using appDelegate class
In AppDelegate.h class declare properties with data type on your choice as i take it as NSString
#property (nonatomic, strong) NSString* pickerValue;
Get instance on AppDelegate class in ViewController A and B in viewDidLoad
self.appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
in picker delegate method set value as
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
self.appDelegate.pickerValue = self.pickerData[row];
}
In viewController B you can user value from
NSLog(#"ViewControllerB::picker valuer::%#",self.appDelegate.pickerValue);
More adaptable way:
Use NSNotification.
Implement the UIPickerViewDelegate method in ViewControllerA:
- (void)pickerView:(UIPickerView * _Nonnull)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
//get the data of the picker and save it in the dictionary
NSMutableDictionary *pickerData =[NSMutableDictionary new]
[pickerData setValue:[yourArrayForPicker objectAtIndex:row] forKey:#"value"];
//create and send the notification with the dictionary
[[NSNotificationCenter defaultCenter]
postNotificationName:#"XYYourNotification"
object:pickerData];
}
than in ViewControllerB register the listener for the notification:
//put this code where you want (maybe in view did load)
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName:#"XYYourNotification"
object:nil
queue:nil
usingBlock:^(NSNotification *notification)
{
//this code will be called when the notification is received
NSDictionary* info = [notification userInfo];
NSLog(#"selected :%#", [pickerData valueForKey:#"value"]);
}];

Can I detect UITableView delegates in parent class?

I have a ViewController that imports two TableViewControllers.
I have the delegate/datasource methods executing in these subclasses, but is there anyway that the ViewController can tell when the each TableView has executed methods such as the delegate method didSelectRowAtIndexPath or do I have to add methods in each tableviewcontroller for polling if cells had been selected?
Thanks!
You can define your own notifications, e.g. "MyDidSelectRowAtIndexPathNotification", and make your main view controller an observer of this notification:
#define kMyDidSelectNotification #"MyDidSelectRowAtIndexPathNotification"
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myDidSelectAction:) name:kMyDidSelectNotification object:nil];
Then, in your implementation of tableView:didSelectRowAtIndexPath, you can simply trigger this notification for all observers:
[[NSNotificationCenter defaultCenter]
postNotificationName:kMyDidSelectNotification object:nil];
You can see here that you could pass the UITableViewCell or some other object in the notification, if you need it. Your handler for the notification (myDidSelectAction: in this case) receives an NSNotification object which would contain your object passed in the postNotification operation.
While a protocol would also work, it is more complicated to setup, in my view.
Have a look at the docs for Notification Center.
well you can create a #protocol like this:
#protocol MyChildViewControllerDelegate<NSObject>
#optional
- (void)tablView:(UITableView *)tv didSelectRowInChildViewControllerAtIndexPath:(NSIndexPath*)indexPath;
#end
now make a property of MyChildViewControllerDelegate in class ChildViewController and #synthesize it, like this:
#property(assign, nonatomic) id<MyChildViewControllerDelegate> delegate;
Now when making an instance of ChildViewController in parent classes assign the delegate by self like this:
ChildViewController *ch = [[ChildViewController alloc] initWithBlahBlah];
ch.delegate = self;
and implement the MyChildViewControllerDelegate methods.
now when you receive any UITableViewDelegate callback in ChildViewController tell your parent classes through delegates.
- (void)tableView:(UITableView *)tView didSelectRowAtIndexPath:(NSIndexPath *)iPath
{
[delegate tablView:tView didSelectRowInChildViewControllerAtIndexPath:iPath];
}
instead of making your own MyChildViewControllerDelegate you can use apple provided UITableViewDelegate too (as you find comfort with).
Hope it helps :)

Call method from initialized UIViewController in storyboarding - objective c

I'm new to Storyboarding in objective c and I need to call method from UIVIewController. Before Storyboarding I was initializing UIViewController in AppDelegate or just assigning pointer there, and then simply call method from any class, accessing AppDelegate properties. How to do It in Storyboarding, If there are no UIViewController initializing by myself? My app is UITabBar application if it does matter.
You can get the root view controller of your storyboard from your app delegate. For example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UITabbarController *tbc = (UITabbarController *)self.window.rootViewController;
// Do any setup you want to here
// You aren't setting up the controller - just accessing the one that has been set up for you by the storyboard
return YES;
}
If you want to have this method available from any class you can keep doing the same. Place in in your AppDelegate and then just retrieve the shared instance on the controller you want to use it from. (all your application has access to your appdelegate) so just import the header of the app delegate, create a new instance of your specific appdelegatename class, and call the shared delegate. then you can use the method as if that class would have it.
Additionaly you can use something like this
#import "AppDelegate.h"
#define appDelegate (AppDelegate *) [[UIApplication sharedApplication] delegate]
IF you want to call a method from ANY of your viewcontrollers which are specified in the storyboard you can do it by referencing their identifier:
PreviewViewController *tmp = [[self storyboard] instantiateViewControllerWithIdentifier:#"PreviewViewController"];

how to wait till a modal dialog returns a result in ios

I have a custom UIViewController shown with presentModalViewController function in a http basic authentication delegate to get username and password. I want to wait till the user clicked the login button on the modal view controller shown on the screen. How can I do this? I am new to iOS any comments or links would be appreciated.
Edit : here is a sample code inside NSURLConnectionDelegate
-(void) connection(NSURLConnection*)connection willSendRequestForAuthenticationChallenge(NSURLAuthenticationChallenge*)challenge
{
CustomAuthViewController *authView = [CustomAuthViewController alloc] initWithNibName"#"CustomAuthViewController" bundle:[NSBundle mainBundle]];
[parentcontroller presentModalViewController:authView animated:YES];
//
// I want to wait here somehow till the user enters the username/password
//
[[challenge sender] userCredentials:credentials forAuthenticationChallenge:challenge];
}
Kind regards.
Edit : Solution : It is not necessary to send the credentials in the willSendRequestForAuthenticationChallenge delegate function right away. I can send it anytime later, It is strange though.
Basically what you want is to pass a message from the modal UIViewController to the caller when your login dialog completes. There are a number of ways to do this. Here's a couple:
Option 1 - Delegate Pattern:
On your modal dialog .h
#protocol LoginDelegate
- (void)loginComplete:(NSString *)userId;
- (void)loginFailed;
#end
#interface MyLoginDialog : UIViewController {
UIViewController *delegate;
}
#property (nonatomic, retain) UIViewController *delegate;
On your modal dialog .m
in your init:
delegate = nil;
in your dealloc:
[delegate release];
when you complete your login:
[delegate dismissModalViewControllerAnimated:YES];
[delegate loginComplete:userId] or [delegate loginFailed];
Then on your calling view controller implement the LoginDelegate protocol.
And when you create your login view controller, set the delegate:
UIViewController *viewLogin = [[UIViewController alloc] init];
viewLogin.delegate = self;
Option 2 - Post a notification with NSNotificationCenter:
On your login dialog:
[self dismissModalViewControllerAnimated:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:#"LoginComplete" object:nil];
On your calling view controller
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loginComplete:) name:#"LoginComplete" object:nil];
Then you implement the selector loginComplete.
If you want to pass back login information (username, userId, etc) you can package that into a dictionary and add it as the "object" in the postNotificationName method.
You also need to be sure to call
[[NSNotificationCenter defaultCenter] removeObserver:self];
when you are done listening.

How to Call a Controller Class (delegate) method from View Class Event in Objective C/Cocoa

lets say I have an NSWindow Class, that has several events for mouse and trackpad movements
for example this code
IMHO, I think this should be good programming, it is similar to pointing an action of a button to its method in controller.
in MyWindow.m Class I have (which in IB I have set the window class to it)
#implementation MyWindow
- (void)swipeWithEvent:(NSEvent *)event {
NSLog(#"Track Pad Swipe Triggered");
/* I want to add something like this
Note that, Appdelegate should be existing delegate,
not a new instance, since I have variables that should be preserved*/
[AppDelegate setLabel];
}
#end
and in My AppDelegate.h I have
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSTextField *label;
}
-(void)setLabel;
#end
and in my AppDelegate.m I have
-(void)setLabel
{
[label setStringValue:#"swipe is triggered"];
}
I have tried #import #class, [[... alloc] init], delegate referencing in IB (I made an object class of MyWindow - thanks to the answer of my previous question )
that latter seems the closest one, it works if both of the classes are delegates, so I could successfully call the "setLabel" action from a button in a second controller (delegate class)'s IBAction method,
but this View Events seem not communicating with the delegate's action although their code is executing.
You are sending a message to the AppDelegate class, not the instance of your AppDelegate class which is accessible from the NSApplication singleton ([NSApplication sharedApplication])
[AppDelegate setLabel];
This is wrong, to get the delegate do this:
AppDelegate* appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
then send the message to that instance:
[appDelegate setLabel];