Distinguish between two instances of the same class Objective-c? - 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.

Related

Singleton IBOutlet called from another class returns nil

I try to call an IBOutlet, which is a property of a Singleton Object from another class but it returns nil. I´ve read a lot of of other posts about singletons here but there seems to be something that I´m missing. This is a OSX question.
In the Interface Builder I have a Button and an Object with a Singleton set as custom class. The Singletons class is called "CEOptionsST". The button is connected as IBOutlet to the Singleton´s class header.
#property (weak) IBOutlet NSButton *mybutton;
Later I´d like to access the button´s state from another class. Therefore I call:
CEOptionsST *opt = [CEOptionsST sharedOptions];
NSLog(#"state: %ld",(long)[[_opt mybutton] state]);
This always returns 0 no matter if the button is on or off. Using Textfields instead of buttons returns nil.
The pointer is the same when I log self from inside the Singleton´s and opt. So the Singleton is not instanced twice.
When I check opt in debug mode it tells me that all properties are nil. The IBOutlet property "mybutton" is nil. So the Singleton Object initiated by the Interface Builder (or nib) is the same as opt but the state of mybutton and other properties is not accessible - what am I missing??
What I´m trying to achieve is to have UI controls as "options" for the user, which are accessible throughout the application.
Here´s my Singleton class (by Dave DeLong´s answer):
#interface CEOptionsST : NSObject
+ (id) sharedOptions;
#property (weak) IBOutlet NSButton *mybutton;
#end
#implementation CEOptionsST
+ (void)initialize{
[self sharedOptions];
}
+ (id)sharedOptions {
static CEOptionsST *sharedOptions = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedOptions = [[self actualAlloc] initActual];
});
return sharedOptions;
}
+ (id)actualAlloc {
return [super alloc];
}
+ (id)alloc {
return [CEOptionsST sharedOptions];
}
- (id)initActual {
self = [super init];
if (self) {
}
return self;
}
- (id)init {
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
return self;
}
Thanks,
Thomas
I would strongly suggest you rethink the architecture of your app! Irrespective of that, perhaps you find your "answer" in nibs allocating instances via allocWithZone: not alloc, try overriding that to call your sharedOptions class method.
Your myButton object may be deallocated because only your singleton is pointing to NSButton. You should try to change reference type from weak to strong.
Another possible reason of your bug may be connected to IBOutlet and singleton. To be honest I have never seen such combination and it may not be a good practice according to Apple Guidelines. Please try to remove IBOutlet and set NSButton programically.

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

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
}
}

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

How to pass values between 2 View Controllers without protocol?

I have two view controllers, call them viewA and ViewB
All the action happens in main view - ViewA
A menu button is hit, brings up ViewB, all is well and the menu comes up
Now, the user touches one IBAction button, which programmatically just needs to:
change the value of a BOOL, call it myBOOL to YES
dismiss ViewB
pass the myBOOL variables current state of YES back to ViewA
I have declared the same BOOL, set property, synthesized on both Views, but per my NSLog upon dismissal of ViewB and loading back up ViewA, it reverts back to NO
So I know I'm going off on a tangent, I just want to know if you can send the value of a BOOL between two controllers and if so, please show me an example... as searches have found Protocols and Delegate examples with NSString's, and when I attempt with a BOOL I get stuck in an import loop, however I've read that its possible to make a global BOOL, as bad design as it is, I just need to get over this block for now.
A question on this topic should really be focused more on NSNotificationCenter rather than NSUserDefaults, taking note that both are singletons.
NSUserDefaults:
The purpose of this class is NOT to pass variables between classes. It's purpose is, well, to store user's defaults. (ie preferences, settings, ... etc).
NSNotificationCenter:
This class is very handy, and has many different uses, one of which is to broadcast a variable for any class to receive. The receiving class is called the observer. This pattern is known as the Observer Pattern.
NOTE: The NSUserDefaults approach has the advantage of allowing you to set the variable before the other class is initialized, and can be retrieved at anytime. However, that's really sloppy (IMHO) and considered bad practice.
Quick and Dirty code sample on NSNotificationCenter:
// upon initializing the class that wants to observe the changes, we add it as an observer.
// So, somewhere in the A.m, upon being initialized (init, maybe?).
- (id)init {
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(calledUponNotif:)
name:#"MyObserveKey"
object:nil];
}
return self;
}
// the selector should look something like this:
- (void)calledUponNotif:(NSNotification *)notif {
id sentVar = [notif object];
}
// Somewhere in the B.m
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyObserveKey"
object:varToSend];
Another note: After calling the postNotification method, the registered selector in the other class will be called synchronously, so you don't have to worry about that.
This is not a good encapsulation answer but without being able to use protocols or delegates I don't believe it will have good encapsulation.
You can also create a global variable that you can set in one view controller and access in another.
ViewControllerOne.h
extern NSString *globalVariable;
#interface ViewControllerOne
#end
ViewControllerOne.m
#import "ViewControllerOne.h"
#implementation ViewControllerOne
NSString *globalVariables = #"Some String in the variable to access in second controller";
#end
ViewControllerTwo.m
#import "ViewControllerTwo.h"
#import "ViewControllerOne.h"
#implemetation ViewControllerTwo
- (void)viewDidLoad
{
NSLog("%#", globalVariables);
}
#end
This will print out into the console
****CONSOLE****
Some String in the variable to access in second controller
There is View-independent value keeping tool. You can use:
[[NSUserDefaults standardUserDefaults]setObject:<#(id)#> forKey:<#(NSString *)#>]
For example, you inputs strings or datas in A view, you can store them in above variables. And then, in B view, you can use them by below code:
[[NSUserDefaults standardUserDefaults]objectOrKey:<#(NSString *)#>]
These are a example of NSUserDefaults data using:
View A:
- (void)textFieldDidEndEditing:(UITextField *)sender
{
if (sender == homepage) {
[[NSUserDefaults standardUserDefaults]
setURL:[NSURL URLWithString:homepage.text] forKey:Ever5secHomepagePrefKey];
if( [homepage canResignFirstResponder] ) {
[homepage resignFirstResponder];
}
} else if (sender == userId) {
[[NSUserDefaults standardUserDefaults]
setObject:userId.text forKey:Ever5secUserIdPrefKey];
objectForKey:Ever5secUserIdPrefKey]);
if( [userId canResignFirstResponder] ) {
[userId resignFirstResponder];
}
} else if (sender == password) {
[[NSUserDefaults standardUserDefaults]
setObject:password.text forKey:Ever5secPasswordPrefKey];
if( [password canResignFirstResponder] ) {
[password resignFirstResponder];
}
}
}
View B:
userId.text = [[NSUserDefaults standardUserDefaults]
objectForKey:Ever5secUserIdPrefKey];
password.text = [[NSUserDefaults standardUserDefaults]
objectForKey:Ever5secPasswordPrefKey];
homepage.text = [[[NSUserDefaults standardUserDefaults]
URLForKey:Ever5secHomepagePrefKey]
description];
You don't need to use NSNotificationCenter, NSUserDefaults or global variables.
As long as the view controllers are related (and looking at the OP's question, they certainly seem to be) you can simply set the view controllers up to hold a reference to each another (with one of the references being weak of course in order to avoid a "retain", or "strong reference", cycle). Then each view controller can set the property on the other view controller as needed. Example follows...
NB: This concept is valid for any two related view controllers. However, the following code assumes that:
The view controllers in question are related via a navigation controller and the second view controller is attached to the first via a push segue.
iOS 5.0 or above is in use (as it makes use of storyboards).
FirstViewController.h
#interface FirstViewController : UIViewController
/* Hold the boolean value (or whatever value should be
set by the second view controller) in a publicly
visible property */
#property (nonatomic, assign) BOOL someBooleanValue;
/* Provide a method for the second view controller to
request the first view controller to dismiss it */
- (void)dismissSecondViewController;
#end
FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
#implementation FirstViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
/* Get the reference to the second view controller and set
the appropriate property so that the secondViewController
now has a way of talking to the firstViewController */
SecondViewController *vc = [segue destinationViewController];
vc.firstViewController = self;
}
- (void)dismissSecondViewController
{
// Hide the secondViewController and print out the boolean value
[self.navigationController popViewControllerAnimated:YES];
NSLog(#"The value of self.someBooleanValue is %s", self.someBooleanValue ? "YES" : "NO");
}
#end
SecondViewController.h
#import "FirstViewController.h"
#interface SecondViewController : UIViewController
// Create a 'weak' property to hold a reference to the firstViewController
#property (nonatomic, weak) FirstViewController *firstViewController;
#end
SecondViewController.m
#implementation SecondViewController
/* When required (in this case, when a button is pressed),
set the property in the first view controller and ask the
firstViewController to dismiss the secondViewController */
- (IBAction)buttonPressed:(id)sender {
self.firstViewController.someBooleanValue = YES;
[self.firstViewController dismissSecondViewController];
}
#end
Of course, the most correct way to handle this sort of inter-viewController communication is to use protocols/delegates/data sources so that the SecondViewController doesn't need to know the specifics of its parent/owner object. However, sometimes it is quicker/simpler to build a solution like this just to prove the concept. Then if all is well and the code is worth keeping, refactor to use protocol(s).
In the case where view controllers don't - and shouldn't - know about each other, it may be necessary to use NSNotificationCenter. Don't use global variables or NSUserDefaults for communication between view controllers.
There are two options available storing and retrieving data in different view controllers.
1)NSUserDefaults is best option for storing data and accessing in any other view controllers.
The NSUserDefaults class provides convenience methods for accessing common types such as float, double, integer, Boolean.
A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
This is very easy and best method for storing and retrieving data.
if you want to read about NSUserDefaults, here I am sharing document.
NsuserDefaults Document.
2) You would create properties when you want them to be accessible outside the class or other view controllers.
Create property in this way. #property (nonatomic, retain) NSArray *arrayData; and then you can use this array value in other view controllers also.
Properties replace the accessor methods for objects.
You can see my answer here. Pass value from one view controller to another
There are two options available storing and retrieving data in different view controllers.
1)NSUserDefaults is best option for storing data and accessing in any other view controllers.
The NSUserDefaults class provides convenience methods for accessing common types such as float, double, integer, Boolean.
A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
This is very easy and best method for storing and retrieving data.
if you want to read about NSUserDefaults, here I am sharing document.
[NsuserDefaults Document.][1]
2) You would create properties when you want them to be accessible outside the class or other view controllers.
Create property in this way. #property (nonatomic, retain) NSArray *arrayData; and then you can use this array value in other view controllers also.
Properties replace the accessor methods for objects.
I think best way to use powerful features of blocks in below ways.
In ViewB.h
typedef void (^CompletionHandler)(BOOL myBool);
#interface ViewB : UIViewController {
CompletionHandler completionHandler;
}
- (void)dismissHandler:(CompletionHandler)handler;
In ViewB.m
- (void)dismissHandler:(CompletionHandler)handler {
completionHandler = handler;
}
- (IBAction)dismiss:(id)sender {
completionHandler (YES); // your yes no logic here
}
In ViewA.m
- (IBAction)showPopup:(id)sender {
ViewB *vc = [[ViewB alloc] init];
[self.view addSubview:vc.view];
[vc dismissHandler:^(BOOL myBool) {
if (myBool) {
//Do your work;
}
}];
}

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