Using a method in Objective-C cocoa - objective-c

I'm having trouble with something very basic. I want to call this action:
- (IBAction)changeGreeting:(id)sender {
self.userName = self.textField.text;
NSString *nameString = self.userName;
if ([nameString length]==0) {
nameString = #"World";
}
NSString *greeting = [[NSString alloc] initWithFormat:#"Hello, %#!", nameString];
self.label.text = greeting;
}
When the user presses return after entering text in the text field. This is what I have:
-(BOOL)textFieldShouldReturn:(UITextField *)theTextField{
if (theTextField == self.textField) {
[theTextField resignFirstResponder];
[changeGreeting];
}
return YES;
}
I'm not sure what to put where it says "changeGreeting." Think I'm missing the concept here. Thanks for any help.

Your syntax is wrong. Try this:
[self changeGreeting:self];
In Objective-C the syntax for sending messages (calling functions) is like this:
[receiver message];
Since you implement the changeGreeting: method in the same class as you're calling it from, the receiver will be self. As the parameter (sender) you usually pass the object that sends the message, but since you don't use it in your implementation of changeGreeting: it doesn't really matter what you pass there.

you called the changeGreeting function in wrong way
you should call it like
[self changeGreeting:nil];

Or if you want to track something from the textField then
[self changeGreeting:theTextField];
the sender will get theTextField as parameter if you need some processing on based on the textField.

Related

changing a variable in completionblock

Hello I am currently having issues bridging a variable in my completion block. I want to set isPreviewPlaying to NO in the completion block but I can't.
static void completionCallback (SystemSoundID mySSID, void *myself) {
AudioServicesRemoveSystemSoundCompletion (mySSID);
AudioServicesDisposeSystemSoundID(mySSID);
CFRelease(myself);
UIButton *previewButton = (__bridge UIButton*)myself;
[previewButton setTitle:#"Preview" forState:UIControlStateNormal];
_isPreviewPlaying = NO // I want to do this, but I can't.
}
- (void)previewButtonPressed:(id)sender {
if (_isPreviewPlaying) {
_isPreviewPlaying = NO;
NSLog(#"STOP");
AudioServicesDisposeSystemSoundID(soundID);
} else {
NSString * selectedPreviewSound = [self.soundFile objectAtIndex: _soundFileIndex];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath: selectedPreviewSound], &soundID);
AudioServicesPlaySystemSound (soundID);
_isPreviewPlaying = YES;
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, completionCallback, (__bridge_retained void *)sender);
[sender setTitle:#"Stop" forState:UIControlStateNormal];
}
}
You will need to pass the class instance to the completion block function, as it's a C function, and that way you can access properties and methods on the instance.
That is the intent of myself parameter I believe, however you are currently releasing it using CFRelease() for some reason I cannot fathom (I am assuming that sender is a button as previewButtonPressed: looks like a button event callback, and releasing it won't do it any favours; read crash).
Therefore I would suggest:
Remove the CFRelease() call.
Cast myself to whatever the class is: MyClass *myclass = (MyClass *)myself;.
Call myclass.previewPlaying = NO; (assuming it's a property). Call [myclass previewNotPlaying] (see below).
Pass self instead of sender to AudioServicesAddSystemSoundCompletion().
EDIT Having said that, I now see you are using the button instance to display information. Instead of calling the property, above, call a method on the instance instead and make that method do the work:
- (void)previewNotPlaying
{
// _previewButton is an IBOutlet to the button
[_previewButton setTitle:#"Preview" forState:UIControlStateNormal];
_isPreviewPlaying = NO;
}

Calling Obj-C Code from JavaScript via Console: Arguments get dropped?

Having a heck of a time with this one.
I've got a super-simple Cocoa app containing one WebView, a WebScripting API defined in the page, and a single NSObject defined on that API. When I turn on the debugger tools (in the embedded WebView), I can see the API on the JavaScript window object, and I can see my "api" property defined on that -- but when I call the API's "get" method, the arguments aren't being serialized -- when the Obj-C method gets called, the arguments are missing. See below, which hopefully illustrates:
I've combed through the docs, I've (apparently) set the appropriate methods to expose everything that needs to be exposed, and I can see the method being called. There has to be something stupid I'm missing, but as a relative newbie to this environment, I'm not seeing it.
Thanks in advance for your help!
Have you set WebKitDeveloperExtras to YES in your default user defaults when you send -[NSUserDefaults registerDefaults:]?
Depending on what version of Xcode you're using you could be getting a known error. If you're using LLDB on anything but the most recent version, it might not be giving you the right variables in the debugger. The solution has been to use GDB instead of LLDB until Apple fixes the problem. But I think they fixed the problem in the latest version. I'd change the debugger to use GDB and see if you're getting the right variables in Xcode. (Product-> Edit Scheme...-> Run -> Debugger). I came across this problem in iOS, though, so I don't know its applicability to OSX. Worth a try anyway.
I originally came across the problem here: https://stackoverflow.com/a/9485349/1147934
I process javascript in the main thread of my app from a local file stored in the apps directory. I check for beginning and ending tokens for the js functions I am executing and whether the function contains a variable.
Hopefully this can give you some good ideas for your issue. You could also do alerts in the js to see if the values post correctly as you run the app (I am sure you thought of that already, but it's worth mentioning.) Happy coding! I hope this helps!
in the .h file define:
NSMutableString *processedCommand;
NSArray *commandArguments;
In the .m file:
// tokens
#define kOpenToken #"<%%"
#define kCloseToken #"%%>"
// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {
[self performSelectorOnMainThread:#selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}
// this will throw
-(NSString *)executeCommand:(NSString *)command {
NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:#""]
stringByReplacingOccurrencesOfString:kCloseToken withString:#""]
stringByTrimmingLeadingAndTrailingWhitespaces];
if ([aCommand hasPrefix:#"="])
{
// variable. get value
[self getVariableFromCommand:aCommand];
}
else {
[self executeThisCommand:aCommand];
}
NSString *returnValue = [NSString stringWithString:processedCommand];
self.processedCommand = nil;
self.commandArguments = nil;
return returnValue;
}
-(void)executeThisCommand:(NSString *)aCommand {
BOOL hasError = NO;
// clear result
self.processedCommand = nil;
self.commandArguments = nil;
BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;
#try {
// first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:#":"]];
if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:#"js-call"]) {
isFromJS = YES;
[commandParts removeObjectAtIndex:0];
}
// get our function, arguments
function = [[commandParts objectAtIndex:0] retain];
[commandParts removeObjectAtIndex:0];
if ([commandParts count] > 0){
if (isFromJS == YES) {
NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([arguments length] > 0) {
self.commandArguments = [arguments JSONValue];
}
}
else {
self.commandArguments = [NSArray arrayWithArray:commandParts];
}
}
// build invoke
SEL sel = NSSelectorFromString(function);
if ([self respondsToSelector:sel]) {
[self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];
// using invocation causes a SIGABORT because the try/catch block was not catching the exception.
// using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)
}
else {
[appDelegate buildNewExceptionWithName:#"" andMessage:[NSString stringWithFormat:#"Object does not respond to selector %#", function]];
}
}
#catch (NSException * e) {
hasError = YES;
[self updateErrorMessage:[NSString stringWithFormat:#"Error processing command %#: %#", aCommand, [e reason]]];
}
#finally {
[function release];
[commandParts release];
}
if (hasError == YES) {
[appDelegate buildNewExceptionWithName:#"executeThisCommand" andMessage:self.errorMessage];
}
}
// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {
NSString *returnValue = nil;
if (queryString != nil) {
returnValue = [queryString objectForKey:[name lowercaseString]];
}
return returnValue;
}

Methods are being called yet not executing

So my Twitter/Facebook implementation in my app has been a learning experience, but I'm almost there and I have one last, probably simple question. Using the MGTwitter engine, I'm calling a method from my viewcontroller in
- (void) setAccessToken: (OAServiceTicket *) ticket withData: (NSData *) data {
The method is firing off, (confirmed by NSLog calls). However, it's not doing what it's supposed to do, which is fade in my logout button for Twitter. I'm still getting my hands around the way Objective-C handles methods and all, I feel like I'm just not pointing my variables to the right place. Any direction is much appreciated, here is the code below:
SA_OAuthTwitterEngine.m -
//
// access token callback
// when twitter sends us an access token this callback will fire
// we store it in our ivar as well as writing it to the keychain
//
- (void) setAccessToken: (OAServiceTicket *) ticket withData: (NSData *) data {
if (!ticket.didSucceed || !data) return;
NSString *dataString = [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];
if (!dataString) return;
if (self.pin.length && [dataString rangeOfString: #"oauth_verifier"].location == NSNotFound) dataString = [dataString stringByAppendingFormat: #"&oauth_verifier=%#", self.pin];
NSString *username = [self extractUsernameFromHTTPBody:dataString];
if (username.length > 0) {
[self setUsername: username password: nil];
if ([_delegate respondsToSelector: #selector(storeCachedTwitterOAuthData:forUsername:)]) [(id) _delegate storeCachedTwitterOAuthData: dataString forUsername: username];
}
[_accessToken release];
_accessToken = [[OAToken alloc] initWithHTTPResponseBody:dataString];
//Call twit login from my view controller
MyView *fvController = [[MyView alloc] init];
[MyView twitLogin];
[MyView helper];
NSLog(#"LETS TWEET DIRECTLY AFTER SUCCESSFUL LOG IN!");
}
This is what my helper method is doing in my .m file:
-(void)helper{
NSLog(#"HELPER FUNCTION");
[self fadeIn:twitterLogout withDuration:2 andWait:2.0];
}
This is the method it's calling
//FADE IN FUNCTION ------------------------------//////////////////////
-(void)fadeIn:(UIView*)viewToFadeIn withDuration:(NSTimeInterval)duration
andWait:(NSTimeInterval)wait
{
[UIView beginAnimations: #"Fade In" context:nil];
[UIView setAnimationDelay:wait];
[UIView setAnimationDuration:duration];
viewToFadeIn.alpha = 1;
[UIView commitAnimations];
}
In Objective-C, methods are declared in one of two ways:
- (returnType)methodName;
or
+ (returnType) methodName;
The first type is an "instance" method and the second type is a "class" method.
These lines should be changed:
[MyView twitLogin];
[MyView helper];
Try this instead:
[fvController twitLogin];
[fvController helper];
Additionally, you may be calling your helper method before the delegate returns a value. You should see if the MGTwitterEngine contains a delegate. (I'd be surprised if it didn't.) You should use the available callbacks to call methods only when the login is finished. Simply calling the methods in order won't do what you want.
*Are both the log statements printed?
*Is setAccessToken:withData: being called from the main thread? Try calling the fadeIn method from the main thread i.e. something like this
- (void) helper{
NSLog(#"HELPER FUNCTION");
[self performSelectorOnMainThread:#selector(callFadeIn)];
}
-(void)callFadeIn{
[self fadeIn:twitterLogout withDuration:2 andWait:2.0];
}
See if that helps. It is possible that setAccessToken: (and thus helper and fadeIn) is being called from another thread. All UI operations should happen from the main thread.

Suppressing the text completion dropdown for an NSTextField

I'm trying to create the effect of an NSComboBox with completes == YES, no button, and numberOfVisibleItems == 0 (for an example, try filling in an Album or Artist in iTunes's Get Info window).
To accomplish this, I'm using an NSTextField control, which autocompletes on -controlTextDidChange: to call -[NSTextField complete:], which triggers the delegate method:
- (NSArray *)control:(NSControl *)control
textView:(NSTextView *)textView
completions:(NSArray *)words
forPartialWordRange:(NSRange)charRange
indexOfSelectedItem:(NSInteger *)index;
I've gotten this working correctly, the only problem being the side effect of a dropdown showing. I would like to suppress it, but I haven't seen a way to do this. I've scoured the documentation, Internet, and Stack Overflow, with no success.
I'd prefer a delegate method, but I'm open to subclassing, if that's the only way. I'm targeting Lion, in case it helps, so solutions don't need to be backward compatible.
To solve this, I had to think outside the box a little. Instead of using the built-in autocomplete mechanism, I built my own. This wasn't as tough as I had originally assumed it would be. My -controlTextDidChange: looks like so:
- (void)controlTextDidChange:(NSNotification *)note {
// Without using the isAutoCompleting flag, a loop would result, and the
// behavior gets unpredictable
if (!isAutoCompleting) {
isAutoCompleting = YES;
// Don't complete on a delete
if (userDeleted) {
userDeleted = NO;
} else {
NSTextField *control = [note object];
NSString *fieldName = [self fieldNameForTag:[control tag]];
NSTextView *textView = [[note userInfo] objectForKey:#"NSFieldEditor"];
NSString *typedText = [[textView.string copy] autorelease];
NSArray *completions = [self comboBoxValuesForField:fieldName
andPrefix:typedText];
if (completions.count >= 1) {
NSString *completion = [completions objectAtIndex:0];
NSRange difference = NSMakeRange(
typedText.length,
completion.length - typedText.length);
textView.string = completion;
[textView setSelectedRange:difference
affinity:NSSelectionAffinityUpstream
stillSelecting:NO];
}
}
isAutoCompleting = NO;
}
}
And then I implemented another delegate method I wasn't previously aware of (the missing piece of the puzzle, so to speak).
- (BOOL)control:(NSControl *)control
textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
// Detect if the user deleted text
if (commandSelector == #selector(deleteBackward:)
|| commandSelector == #selector(deleteForward:)) {
userDeleted = YES;
}
return NO;
}
Update: Simplified and corrected solution
It now doesn't track the last string the user entered, instead detecting when the user deleted. This solves the problem in a direct, rather than roundabout, manner.

Need to retain a CFSocketRef?

I am facing a problem: I have two view controllers, viewController1, and viewController2.
Here is the method I'm concerned about in viewController1:
-(void)msgToServer:(NSString*)identifier:(NSString *)_username{
NSString *message = [NSString stringWithFormat:#"%#|%#|<END>", identifier, _username];
CFDataRef messageData = (CFDataRef)[message dataUsingEncoding:NSUTF8StringEncoding];
CFSocketSendData(s, NULL, messageData, 0);
}
this method is working perfectly in viewController1.
Now I am calling this method from viewController2. I am passing two arguments, identifier and _username. It generates the string perfectly, but when it reaches the last line it misses the value of s -- which is a CFSocketRef.
What should I do so that s will retain its value, even if I am in viewController2?
Code from viewController2 which calls the above method:
- (void)viewDidLoad {
[super viewDidLoad];
WatchListViewController *watchListViewController = [[WatchListViewController alloc]init];
[watchListViewController msgToServer:#"PREQ" :userName];
}
Thanks in advance.
you could use the retain function of NSObject.
- (id)retain
See in Apple Documentation
Also have a look to Object Ownership and Disposal in Memory Management Programming Guide
EDITED:
Try with below code
You might have issue with function prototype,not with retaining.
use below
-(void)msgToServer:(NSString*)identifier withUsername:(NSString *)_username;.
- (void)viewDidLoad {
[super viewDidLoad];
WatchListViewController *watchListViewController = [[WatchListViewController alloc]init];
[watchListViewController msgToServer:#"PREQ" withUsername:userName];
}
-(void)msgToServer:(NSString*)identifier withUsername:(NSString *)_username{
NSString *message = [NSString stringWithFormat:#"%#|%#|<END>", identifier, _username];
CFDataRef messageData = (CFDataRef)[message dataUsingEncoding:NSUTF8StringEncoding];
CFSocketSendData(s, NULL, messageData, 0);
}
Thanx to all who helped me well i solved this problem in this way:
when I push from viewController1 to viewController2 i am passing two parameters "username" and "s" to viewController2 and then I write this method in viewController2.
-(void)msgToServer:(NSString*)identifier:(NSString *)_username{
NSString *message = [NSString stringWithFormat:#"%#|%#|<END>", identifier, _username];
CFDataRef messageData = (CFDataRef)[message dataUsingEncoding:NSUTF8StringEncoding];
CFSocketSendData(s, NULL, messageData, 0);
}
then I am calling this method in the same view controller and it works perfectly. I think this is the only solution which i find. if any body can make it more efficient then you are welcome. :)