iOS - Single delegate to multiple objects. How to vary behavior? - objective-c

I have a UIViewController which is also a NSURLConnectionDelegate. As such, it defines behavior such as:
– connection:didReceiveResponse:
– connection:didReceiveData:
– connectionDidFinishLoading:
However, in this view I have multiple NSURLConnections which assign it as the delegate. I need to achieve custom behavior in connectionDidFinishLoading: depending on which object is calling the delegate (e.g playing audio vs displaying an image vs opening a link)
What is the correct way to achieve this?

Each of the delegate methods pass in the NSURLConnection as a parameter. Store a reference to your connection in a property and then check the if the connection parameter passed into connectionDidFinishLoading is your audio connection or your image connection etc.

You could declare each of the connection
#interface YourViewController
#property (retain, nonatomic) NSURLConnection *audioConnection;
#property (retain, nonatomic) NSURLConnection *anotherConnection;
then, on your connectionDidFinishLoading: method call each connection like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (connection == audioConnection) {
//doSomething
} else if (connection == anotherConnection) {
//doSomethingElse
}
}

If you have a lot of urlconnections which falls under different categories(say 10 connections out of 5 are for audio, 3 for display image, 2 for opening link etc..), better option is to subclass NSURLConnection and create a custom NSURLConnection class. You can add your own property like a tag to this class. And define your own custom tags to different type of connections. In your UIViewController and delegate methods try to use this subclass object and use this tag property to differentiate between different NSURLConnections.
For eg:-
Create a CustomNSURLConnection file and write,
#define kAudioConnectionTag 100
#define kDisplayConnectionTag 200
#define kOpenURLConnectionTag 300
#interface CustomNSURLConnection : NSURLConnection
#property (nonatomic) NSInteger tag;
In UIViewController class,
CustomNSURLConnection *audioConnection = [CustomNSURLConnection ...];
audioConnection.tag = kAudioConnectionTag;
CustomNSURLConnection *displayConnection = [CustomNSURLConnection ...];
audioConnection.tag = kDisplayConnectionTag;
CustomNSURLConnection *openURLConnection = [CustomNSURLConnection ...];
audioConnection.tag = kOpenURLConnectionTag;
- (void)connectionDidFinishLoading:(CustomNSURLConnection *)connection{
if (connection.tag == kAudioConnectionTag) {
//code
} else if (connection.tag == kDisplayConnectionTag) {
//code
} else {
//code
}
}

Related

Objective-c: Singleton - passing variables

I have a singleton that I'd like to use to manage the onscreen animation of my views. Here's my.
#import <Foundation/Foundation.h>
#interface OAI_AnimationManager : NSObject {
NSMutableDictionary* sectionData;
}
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
.m file
#import "OAI_AnimationManager.h"
#implementation OAI_AnimationManager
#synthesize sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
static OAI_AnimationManager* sharedAnimationManager;
#synchronized(self) {
if (!sharedAnimationManager)
sharedAnimationManager = [[OAI_AnimationManager alloc] init];
return sharedAnimationManager;
}
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", sectionData);
}
#end
You'll see in the .h file I added a NSMutableDictionary and am using #property/#synthesize for it's getter and setter.
In my ViewController I instantiate the animation manager as well as a series of subclasses of UIView called Section. With each one I store the data (x/y w/h, title, etc.) in a dictionary and pass that to the dictionary delcared in animation manager. In the Section class I also instantiate animation manager and add a UITapGestureRecognizer which calls a method, which passes along which section was tapped to a method (checkToggleStatus) in animation manager.
As you can I see in the method I am just logging sectionData. Problem is I am getting null for the value.
Maybe my understanding of singletons is wrong. My assumption was the class would only be instantiated once, if it was already instantiated then that existing object would be returned.
I do need all the other Section classes data as if one animates others animate in response and I can get around it by passing the tapped Section to the animation manager and doing [[Section superview] subviews] and then looping and getting the data from each that way but it seems redundant since that data is available in the ViewController when they are created.
Am I doing something wrong in trying to transfer that data? Is there a better solution? I am open to suggestions and criticisms.
Thanks
h file
#interface OAI_AnimationManager : NSObject
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
m file
static OAI_AnimationManager* _sharedAnimationManager;
#implementation OAI_AnimationManager
#synthesize sectionData = _sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
#synchronized(self) {
if (!_sharedAnimationManager) {
_sharedAnimationManager = [[OAI_AnimationManager alloc] init];
}
}
return _sharedAnimationManager;
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", _sectionData);
}
#end
Notice I moved your sectionData variable from the header and moved it to the implementation file. A while back, they changed it to where you can synthesize properties and specify their instance variable names along side it... hence:
sectionData = _sectionData;
I also added and underscore to the instance variable... this is a universal convention for private variables and it also will throw a compile error now if you try to type just sectionData as you did in the return statement of checkToggleStatus:. Now you either have to type self.sectionData or _sectionData.
You didn't include the code that creates an instance of your dictionary but I bet you didn't set it as self.sectionData = [[NSDictionary alloc] init] which means it would not retain the value and you would get null the next time you called it. Classic memory management mistake... I know it well because I learned the hard way hehehe

share NSArray between different UIViewControllers

I am making an application that uses a webService to get data in a JSON format... I get the data I parse them into a object NSArray ... and i use it .. it works fine ...
Now, if the user clicks a button I need to send him to an other Uiview ... which contains more data about the clicked object ..
The problem is here ... I don't want to request again and download the result from the server ... because i already did ... All I want is to have access to that NSArray that I have in the first UIViewController.
You can add on AnotherView.h another property:
#property (nonatomic, retain) NSArray *jsonData;
On AnotherView.m synthesize it. When you are going to to call AnotherView from InitialView, you can set jsonData with the data you retrieved on InitialView.
Create a custom initializer in your other view controller like so:
#import <UIKit/UIKit.h>
#interface OtherViewController : UIViewController
#property (nonatomic, strong) NSArray *myArray;
- (id)initWithArray:(NSArray *)anArray;
#end
Then implement it like so:
#import "OtherViewController.h"
#implementation OtherViewController
#synthesize myArray=_myArray;
- (id)initWithArray:(NSArray *)anArray {
if (!(self = [self initWithNibName:#"OtherViewController" bundle:nil]))
return nil;
if (!anArray) {
#throw [NSException exceptionWithName:#"OtherViewControllerBadInitCall" reason:#"array is nil" userInfo:nil];
}
_myArray = anArray;
return self;
}
//...
#end
You can then init and display your controller like so:
OtherViewController *otherViewController = [[OtherViewController alloc] initWithArray:greatJSONArray];
[self.navigationController pushViewController:otherViewController animated:YES];
There you go.
You can set the array as the property. You can either create a new class and set the array as the property and after you fetch the array, set the property. Or, you can create a property of the existing UIVIewController Class and pass the object.
Either way, you have to set property.
You could define a new property in your second ViewController that holds an NSArray and pass the firt array to the second ViewController before show it.
Well you have not outlined whether you send the data forward or backward. In the later case you will need to implement protocol and delegate(Define your own protocol) but for the prior case you just need to create the property of the Object you want to access in any other class. In case of web-services it is better to use protocol and delegates if u abide by the norms of MVC architecture.

How to generate a generic table view controller?

I've created a custom TablePickerViewController which is a subclass of UITableViewController. I'm using this class to display a list of object of a custom type TablePickerItem.
I'm using TablePickerViewController multiple times in my iOS application to show different kinds of lists where the user has to pick an item -- and then another view controller MainViewController should react on this selection and do something.
I've created this protocol and created a delegate property in the TablePickerViewController:
#protocol TablePickerViewControllerDelegate <NSObject>
- (void)tablePickerViewController:(TablePickerViewController *)controller
didSelectItem:(TablePickerItem*)item;
#end
When I setup a new TablePickerViewController in MainViewController it is also set as delegate -- than it will be notified when the user taps an cell in the table view.
The problem is that my MainViewController will setup multiple TablePickerViewController with different data (TablePickerItem). How should I setup my MainViewController to handle these multiple TablePickerViewController? Events from each of them will results in calling to the same protocol-method in my MainViewController.
Further I need to get the element which the TablePickerItem represents, as I need to know for instance the elements ID when acting in the tablePickerViewController:didSelectItem method. Should I just handle this by adding something like #property (nonatomic) id element to the TablePickerItem and set the original object into this property then creating it?
Maybe someone can give me an example on how to create an generic table view controller, if my solutions seems being done in the wrong way.
I'm not entirely sure of your set up, but if you have multiple pickers that feedback to the main controller then you could just have a reference to the picker e.g.
// MainViewController.m
#interface MainViewController ()
#property (nonatomic, strong) TablePickerViewController *picker1;
#property (nonatomic, strong) TablePickerViewController *picker2;
// ... and so on. Obviously you know your problem domain so you can change
// the terrible naming above to something appropriate
#end
#implementation MainViewController
// ...
- (void)theMethodWhereYouSetUpYourPickers;
{
TablePickerViewController *picker1 = [[TablePickerViewController alloc] init];
picker1.delegate = self;
self.picker1 = picker1;
// ...
}
- (void)tablePickerViewController:(TablePickerViewController *)controller
didSelectItem:(TablePickerItem*)item;
{
if (controller == self.picker1) {
NSLog(#"Something was picked in picker 1 %#", item);
} else if (controller == self.picker2) {
NSLog(#"Something was picked in picker 2 %#", item);
}
}
// ...
#end

Distinguish between two instances of the same class Objective-c?

I have two UIWebView objects that share the same delegate.
I want the delegate methods to distinguish between the two UIWebView objects.
Right now I'm setting the background color of one of them to red and using that property to distinguish between the two, but its messy. There must be a better way...
Thanks!
I'd keep a pointer to each UIWebView instance in the delegate and just compare them in the delegate methods. e.g.,
#property (nonatomic, weak) UIWebView* webView1;
#property (nonatomic, weak) UIWebView* webView2;
...
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (self.webView1 == webView) {
// do something specific to webView1
} else if (self.webView2 == webView) {
// do something specific to webView2
}
}
Delegate methods in general have the instance that's calling them as one of the arguments -- e.g. in webView:didFailLoadWithError: the first argument will be the web view which has failed to load.
A delegate/controller object will/should already have references to the instances for which it's delegating.
All you have to do, then, is compare the reference you already have to the object that's passed along in the delegate method to know which web view has messaged the delegate.
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
if( webView == myFirstWebView ){
}
else if( webView == mySecondWebView ){
}
}
all UIView subivews, including UIWebView has a tag property, that can be used as following:
const NSInteger kLeftWebView = 20;
const NSInteger kRightWebView = 21;
...
// somewhere in initialization code
webView1.tag = kLeftWebView;
webView2.tag = kRightWebView;
...
//check for tag value for distinguishing
if (webView.tag == kLeftWebView) {
....
}
you can also setup tag value in InterfaceBuilder property inspector.

Delegates query in Objective - C

So, I have a decent idea of what a delegate does, why use it, how to implement it etc. and I'm working on implementing it in one of my projects. The problem I'm trying to solve is to decouple my Controller objects from my Network Access class. In this context, the ideas get a little messy in my head.
I somehow intuitively feel that the NetworkAccessClass should be the delegate for a Controller object, because the NetworkAccessClass is acting as a helper for the Controller object. But it seems to work in a reverse fashion, because the following is apparently the right way to do it:
NetworkaccessClass.h
#protocol NetworkAccessDelegate
-(void) requestSucceded:(NSData *) data
-(void) requestFailed:(int) responseCode;
#end
#interface NetworkAccessClass : NSObject
{
id<NetworkAccessDelegate> networkDelegate;
}
#property(nonatomic, assign) id networkDelegate;
-(void) initWithDelegate:(id) delegate; //
#end
NetworkAccessClass.m
#implementation
#synthesize networkDelegate
-(void) initWithParams:(id) delegate
{
networkDelegate = delegate;
// Assign GET/POST vals, create request etc
[request startAsynchronous];
}
-(void) requestSucceded:(ASIHTTPRequest *) request
{
if([networkDelegate respondsToSelector:#selector(requestSucceded:)]) {
// Send the data to the controller object for it to use
...
}
}
-(void) requestFailed:(ASIHTTPRequest *) request
{
// Same as above. Send to request failed.
}
#end
And finally in my FirstViewController.h
#import "NetworkAccessClass.h"
#interface FirstViewController<NetworkAccessDelegate>
{
}
-(void) requestSucceded:(NSData *) data;
-(void) requestFailed:(int) responseCode;
#end
And the same in SecondViewController.h and so on.
Although this does decouple my Controllers from my Network class, I can't help feel it's wrong because the controllers in this case are acting as delegates or helper methods to the Network Class and not the other way round. Am I missing something basic? Or is this how it is?
Thanks,
Teja.
Delegates aren't "helper methods". Think of them as objects that get notified when something happens. (Although don't confuse them with "Notifications"--that's a different thing entirely.) In this case, your network class does it's stuff and then calls its delegate method on the View Controller that instantiated and fired, it to report the contents of that response to the view controller. The controller will then, presumably, update the view with the data that the network connector got. Classic delegate pattern, right there.