How to pass paramerters to custom selector from a NSTimer object? - iphone

How to use the userInfo object in an NSTimer call such as
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
I want to send certain parameters to my custom selector.

The userInfo parameter is for just that.
- (void)onTimer:(NSTimer *)timer
{
NSLog(#"User Info %#", [timer userInfo] );
}

The difficulty is that you need to wrap your parameters, even if more than 1, even if they aren't objects, into a single object. Creating a temporary NSDictionary, and stuffing it with keyed parameters works. Or you could create an custom class just to hold the required parameters, and create and fill an object of that class (alloc, initWithMy42Parameters:) to pass as the userInfo.

Related

How to call a non-void function? Xcode

How do I call a non-void function? Normal [self methodName]; works. But how do I do this for a method that returns an NSString. I keep getting an error. For example:
+ (NSString *)formulateYQLRequestFor:(NSArray *)tickers
How do I call this? [self formulateYQLRequestFor]; gives me an error.
Sorry about the formatting, for some reason safari won't let me indent.
Thanks!
+ designates a class function. You call it with the class name, not an instance.
Instead of:
[self formulateYQLRequestFor:myArray];
Do this:
[MyClassName formulateYQLRequestFor:myArray];
Alternatively, you can do this:
[[self class] formulateYQLRequestFor:myArray];
You don't have to do anything with the return value if you don't want to. At least with ARC, the return value will be automatically released. However, since it's unlikely that the function does anything on its own, you probably should do something with the return value:
NSString *returnValue = [[self class] formulateYQLRequestFor:myArray];
// Do something with returnValue
Finally, if you want to call the function without passing in an array, you still need the array parameter, but perhaps the function will accept nil for the array:
NSString *returnValue = [[self class] formulateYQLRequestFor:nil];
There are two problems with your call to [self formulateYQLRequestFor];
Firstly, the method takes a parameter, which you haven't provided. Because of this, the compiler is looking for the method called formulateYQLRequestFor instead of formulateYQLRequestFor: This is significant, because the : is part of the method name in Objective-C. So you are trying to call a method that doesn't exist.
Secondly, self is sending a message to an instance of your class. The + in the method signature indicates that you have a class method, and so self does not respond to the method you are trying to call.
The correct way to do this is:
NSString *resultString = [[self class] formulateYQLRequestFor:someArray];
where someArray is a valid NSArray parameter.
I don't know what - (NSString *)formulateYQLRequestFor: does with the NSArray, but if it isn't necessary you can just call [self formulateYQLRequestFor:nil];. Alternatively you can call it with an empty array [self formulateYQLRequestFor:[NSArray array]];.

Arguments with NSTimers

Is it possible to give an argument in a method when setting a NSTimer? I want to create something like the following:
[NSTimer [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(moveParticle:imageView) userInfo:nil repeats:YES];
Where "imageView" is the argument of the method. It gives me a error saying the it's expecting a semi-colon right after the parathesis after "imageView".
Any help?
You want to use the userInfo to send arguments. Look at the documentation on how to use it. You will just make your function take a single NSTimer argument then the timer will return itself and you can read its userInfo dictionary.
That's what the userInfo parameter is for. You can pass your imageView as userInfo and cast it to the desired type (NSView?) in the method you provide as selector.
e.g.:
- (void)moveParticle:(NSTimer*)theTimer
{
NSView* imageView = (NSView*)[theTimer userInfo);
...
}
Another approach (probably more useful here - as your target is self), would be to make the imageView an iVar and access that within moveParticle.
See duplicate thread :
You'll need to used +[NSTimer scheduledTimerWithTimeInterval:invocation:repeats:] instead. By default, the selector used to fire a timer takes one parameter. If you need something other than that, you have to create an NSInvocation object, which the timer will use instead.
An example :
NSMethodSignature * mSig = [NSMutableArray instanceMethodSignatureForSelector:#selector(moveParticle:)];
NSInvocation * myInvocation = [NSInvocation invocationWithMethodSignature:mSig];
[myInvocation setTarget:myArray];
[myInvocation setSelector:#selector(moveParticle:)];
[myInvocation setArgument:&imageView atIndex:2]; // Index 2 because first two arguments are hidden arguments (self and _cmd). The argument has to be a pointer, so don't forget the ampersand!
NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 invocation:myInvocation repeats:true];

Selectors with arguments in Obj-C

So basically I have a big list of buttons that's present dropdowns and other things, and these buttons are created dynamically. So to capture the value for the appropriate button's data, I need to set it's action selector to a function that takes 1 extra parameter.
For example, using this selector on this dropdown, with the method below, returns an error that the selector is unrecognized. How can I get the selector to recognize the parameter I'm passing in? (In this case the variable 'name')
The apple docs at:
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocSelectors.html#//apple_ref/doc/uid/TP30001163-CH23-SW1
On the last paragraph in the header 'The Target-Action Design Pattern', the Apple Docs imply that this can be done, but do not give an example of using a custom message, or maybe I'm just misunderstanding?
SEL sel = #selector(openDropdown:name:);
[dropdownSelector addTarget:self action:sel forControlEvents:UIControlEventTouchUpInside];
-(void) openDropdown: (NSString *) anotherArg : (id) sender {
// Stuff here based on anotherArg
}
You should be able to derive the clicked button's information from the id input arg
UIButton *button = (UIButton *) sender
NSString *title = [button currentTitle];
No need to pass the extra param
What you're asking can't be done. From the docs:
UIKit allows three different forms of action selector:
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
Since you have no influence on the event parameter, the sender object must include all information you want to pass to the action method.
Despite what you have written in your comment on mihirsm's answer, you can indeed subclass UIButton to add all the additional info you want to each button instance. You could also use the button's tag property to identify it (assign a unique tag to each button) and store all the additional info in an array or dictionary using the tags as keys.
Update: In the future, you can also use associative storage to add data to objects without subclassing them but this technology is not (yet) available on the iPhone platform (10.6 only at the moment).
CALayers support arbitrary keys for key-value coding; you can use this to attach arbitrary layers:
[[button1 layer] setValue:#"firstButtonData" forKey:#"myKey"];
[[button2 layer] setValue:#"secondButtonData" forKey:#"myKey"];
And later:
- (void)action:(id)sender forEvent:(UIEvent *)event
{
NSLog(#"Data for the button that was pressed: %#", [[sender layer] valueForKey:#"myKey"]);
}
Be careful not to collide with any of the existing properties on CALayer

delegates and multiple methods

I have a problem that I solved using delegates, but now I am thinking I may have made a mistake.
This is what I want to do.
I have a class that runs on a delay. When it is done it has a finished delegate that it calls.
Now I have the main class that creates two of these delay classes.
I don't want them to both be handled by the same isfinished method in the main class. I want to use two different ones.
However I believe with the protocol method of creating delegates that this will not work for me.
Is there a way around this?
delayclass setdelegates MainclassFunction1
delayclass setdelegates MainclassFunction2
If I understand you correctly, take a look at the NSTableViewDelegate protocol. There, each delegate method's first argument is the NSTableView instance sending the message.
You can solve your issue by changing your delegate methods to have your delegating object send itself as an argument. Then, in your delegate, you'd do something like this:
if (theDelegator == objectA)
{
// Do something
}
if (theDelegator == objectB)
{
// Do something else
}
This way, you've got one cleanly-implemented delegate method that can handle multiple objects delegating to it.
Using delegates doesn't seem like the correct approach to me; they're generally used for augmenting behavior. What sounds most appropriate here is the target/selector pattern, like NSTimer.
#interface MyObject : NSObject {
#private
id target;
SEL selector;
}
#property(assign) id target;
#property SEL selector; /* The selector must return void and accept one argument, which is the MyObject instance that invoked the method. */
#end
#implementation MyObject
- (void)notifyTarget {
[[self target] performSelector:[self selector] withObject:self];
}
#synthesize target;
#synthesize selector;
#end
This is generally the cleanest approach since the delegate callback doesn't need to disambiguate the sender. Using notifications seems like too much overhead for a problem in this domain.
As mentioned, commonly delegate methods would include the object initiating the callback, so you can differentiate that way. Alternately you could have the object post a notification instead, which will also make the originator available.
Why are you not just using NSTimer, adding different timers and having them call whatever selectors you like in the class you are using as a delegate now?
Something like:
NSTimer *timer1 = [NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:#selector(myMethod1:) userInfo:nil repeats:YES];
NSTimer *timer2 = [NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:#selector(myMethod2:) userInfo:nil repeats:YES];
Where your methods are:
- (void) myMethod1:(NSTimer*)theTimer
{
// do Something
}
- (void) myMethod2:(NSTimer*)theTimer
{
// do Something different
}
You want to save off and retain both timer1/timer2 references, so that you can stop the timers in dealloc ([timer1 invalidate]).
Short note: Generally, it's bad style to have "if" statements that switch on an object. We all do it occasionally for getting that second list w/o needing a new controller, but switching is what method calls do internally, so ideally you'd just let the ObjC runtime take care of doing the right thing. Several options:
-(void) tableViewSelectionDidChange: (NSTableView*)theView
{
SEL theAction = NSSelectorFromString( [NSString stringWithFormat: #"tableView%#SelectionDidChange:", [theView autosaveName]] );
[self performSelector: theAction withObject: theView];
}
-(void) tableViewUKSourceListSelectionDidChange: (NSTableView*)theView
{
// UKSourceList-table-specific stuff here.
}
-(void) tableViewUKUsersListSelectionDidChange: (NSTableView*)theView
{
// UKUsersList-table-specific stuff here.
}
This works best when you have a non-localized string label, like the autoSave name, but can also use the tag, although that makes the code less readable (which one is "table 1"?). Sometimes it's better to just write a subclass that has a special string for that purpose, or even has methods where you can specify selector names to forward the delegate methods to.
Caleb's suggestion is also good, it's also called "target/action" in case you want to google for it. I have several (Mac) classes that have a regular "action" for clicks, a "doubleAction" for double clicks etc.

How To Pass a Dictionary To a Function

Alright, so I think I'm doing this the right way. I'm new to objective-C, so I'm not sure about the syntax... I have a set of code that I need to call multiple times, from different files. So I made a new class that has a method in it that I'll call and pass it the values that it needs.
Because I am passing different values I've put them in a dictionary and decided to just pass the dictionary. Here is that code:
NSNumber *testNum = [NSNumber numberWithInt:varMoney];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:#"OMG, Object 1!!!!" forKey:#"1"];
[dictionary setObject:#"Number two!" forKey:#"2"];
[dictionary setObject:testNum forKey:#"3"];
This code creates a test variable, and then puts it into the dictionary "dictionary." That all works, I have my nice little dictionary. However, now I need to create the class and it's method that will recieve the dictionary, and do something with it.
This is my class header file:
#import <UIKit/UIKit.h>
#interface EndOfTurnObjC : UIView {
}
#end
And this is the implementation file:
#import "EndOfTurnObjC.h"
#implementation EndOfTurnObjC
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
I haven't created any of the real code, because I'm not sure how to do the passing. I need to create a function (Method?) in the class that will take a Dictionary has a parameter, and then return the dictionary.
I also have no idea how to call such a function because it's in the class. So, the questions are:
1: How do I define the method in the class to accept the dictionary as a parameter (and then perhaps some example code to pull out one of the objects in a dictionary, so I can be sure it works)
2: How do I return the dictionary at the end of the method?
3: How do I call this method, in the class, from another class? (I know it involves making an object of thing class and calling the method of the object... I think, but I'm not sure about the syntax.)
Please include relavent code for the 3 files (header, implementation, and the other class that I call from). Thank you so much, I've been working on this particular problem for a while now.
Apple's The Objective-C Programming Language is a good and pretty concise reference for Objective-C syntax. What you want is just a normal method that takes an NSDictionary as a parameter. So as given in that document:
A message with a single argument affixes a colon (:) to the selector name and puts the argument right after the colon. This construct is called a keyword; a keyword ends with a colon, and an argument follows the colon, as shown in this example:
[myRectangle setWidth:20.0];
So a method call to pass dictionary would look like:
[someObject setAttributes:dictionary];
In the header:
-(NSMutableDictionary *) doSomethingWithDictionary:(NSMutableDictionary *) aDict;
in the implementation:
-(NSMutableDictionary *) doSomethingWithDictionary:(NSMutableDictionary *) aDict{
//do something with the dictionary
return aDict;
}
To call the method:
NSMutableDictionary *returnDict=[EndOfTurnObjC doSomethingWithDictionary:dictionary];
Note that as a matter of good design you wouldn't want to pass a mutable dictionary around like a token. That is asking for trouble. Instead pass static dictionaries and get another dictionary back.
You also shouldn't be passing data to a UIView. Instead, your UIViewController should process the data and then populate the view's UI elements as needed.
if you just want to do stuff to your dictionary u just
-(void) changeMyDictionary:(NSMutableDictionary * ) dictionary_
{
[dictionary_ doStuff];
....
...
}
no need to return the dictionary.