Why doesn't this code work:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSScrollWheelMask) handler:^(struct NSEvent *event){
[scrollEvent:event];
}];
}
- (void)scrollEvent:event {
NSLog( #"scroll" );
}
It says "'scrollEvent' undeclared".
I'm just learning objc and cocoa, so I assume this is just a simple error.
Your code appears to have some bugs in it. I've cleaned up some, please see below.
- (void) applicationDidFinishLaunching:(NSNotification *) aNotification {
[NSEvent addGlobalMonitorForEventsMatchingMask: NSScrollWheelMask handler:^(NSEvent *event){
[self scrollEvent: event];
}];
}
- (void) scrollEvent: (NSEvent *) event {
NSLog( #"scroll" );
}
To summarize:
Your argument to the block should have been an NSEvent *, not a
struct NSEvent *.
Your invocation of scrollEvent needed to be sent to self.
Your implementation of scrollEvent had an incorrect signature.
Hope that helps and good luck with the program.
I believe your :event parameter of the scrollEvent method needs to have a type (NSEvent*) for this to be a valid method signature.
- (void)scrollEvent:(NSEvent*)event {
NSLog( #"scroll" );
}
Related
I have a subclass of webView that overrides - hitTest: The basic idea is that I want clicks on the webView to pass through to the nextResponder if the click was on the body DOM element. The method looks like this
- (NSView *)hitTest:(NSPoint)aPoint
{
NSDictionary *dict = [self elementAtPoint:aPoint];
if([[dict valueForKey:#"WebElementDOMNode"] isKindOfClass:[DOMHTMLBodyElement class]])
{
return (NSView *)[self nextResponder];
}
return [super hitTest:aPoint];
}
When run, it crashes on elementAtPoint with EXC_BAD_ACCESS code=2
Now, it gets weirder. If I breakpoint the app at that line, and do a po [self elementAtPoint:aPoint] in LLDB, LLDB just hangs until I do a ^C.
Weirder yet. If I comment out everything but the last return, break on the return statement, and run po [self elementAtPoint:aPoint] in LLDB—I get exactly what I expect, a nice dictionary telling me all about the DOM at that point.
What could be causing this behavior?
Note: This is on OS X, not iOS.
What could be causing this behavior?
Just look at [WebView _elementAtWindowPoint:] and [WebView _frameViewAtWindowPoint:] implementations in the WebKit source code. Unfortunately, they use hitTest: to determine elementAtPoint:.
In my case, this workaround seems to work:
- (NSView *)hitTest:(NSPoint)point
{
if (m_hitTestEnabled)
{
[NSTimer scheduledTimerWithTimeInterval:0.
target:self
selector:#selector(hitTestDelayed:)
userInfo:#[ #(point.x), #(point.y) ]
repeats:NO];
}
return [super hitTest:point];
}
- (void)hitTestDelayed:(NSTimer *)timer
{
NSPoint point = NSMakePoint([[timer userInfo][0] floatValue], [[timer userInfo][1] floatValue]);
m_hitTestEnabled = false;
NSDictionary *dict = [self elementAtPoint:point];
m_hitTestEnabled = true;
if ([[dict valueForKey:#"WebElementDOMNode"] isKindOfClass:[DOMHTMLDivElement class]])
{
NSLog(#"divAtPoint: %#", dict);
}
}
m_hitTestEnabled is set to YES in the initWith... method.
Why with a timer? Such operations on WebView are allowed only in main thread. So, we cannot launch another thread to get elementAtPoint: and wait for its completion in the "main" hitTest:. Maybe someone will come up with a better solution.
I am trying to create a generic method that takes a SEL as a parameter and passes it to dispatch_async for execution, but i am clueless how to execute the passed in SEL.
Can anyone here help me please.
// Test.m
-(void) executeMe
{
NSLog(#"Hello");
}
- (void)viewDidLoad
{
[super viewDidLoad];
SEL executeSel = #selector(executeMe);
[_pInst Common_Dispatch: executeSel];
}
// Common.m
-(void) Common_Dispatch:(SEL) aSelector
{
dispatch_async(iDispatchWorkerQueue, ^(void) {
// How to execute aSelector here?
});
}
You need to also have a "target" parameter on your Common_Dispatch method since you need to call the selector on a specific object.
- (void)viewDidLoad {
[super viewDidLoad];
SEL executeSel = #selector(executeMe);
[_pInst Common_Dispatch:executeSel target:self];
}
- (void)Common_Dispatch:(SEL)aSelector target:(id)target {
dispatch_async(iDispatchWorkerQueue, ^(void) {
[target performSelector:aSelector];
});
}
BTW - standard naming conventions state that method names should begin with lowercase and use camelCase. Your method should be commonDispatch.
Alternatively, you could use a block parameter, e.g.
- (void)commonDispatch:(void (^)(void))block
{
dispatch_async(iDispatchWorkerQueue, block);
}
You'd then invoke that as:
[_pInst commonDispatch:^{
[self executeMe];
}];
This way, you could use this dispatcher to call methods like executeMe which take no parameters, or to dispatch methods that take lots of parameters, e.g.:
[_pInst commonDispatch:^{
[self executeOtherMethodForURL:url requestType:type priority:priority];
}];
Or more complicated situations, too:
[_pInst commonDispatch:^{
[self executeOtherMethodForURL:url requestType:type priority:priority];
dispatch_async(dispatch_get_main_queue(), ^{
// update my UI to say that the request is done
});
}];
You simply call the performSelector method, like this:
[self performSelector:aSelector];
There are other useful overrides to performSelector you'll find.
Edit
The target of the selector will also have to be passed as a param:
// Test.m
-(void) executeMe
{
NSLog(#"Hello");
}
- (void)viewDidLoad
{
[super viewDidLoad];
SEL executeSel = #selector(executeMe);
[_pInst Common_Dispatch: executeSel target:self];
}
// Common.m
-(void) Common_Dispatch:(SEL) aSelector target:(id)target
{
dispatch_async(iDispatchWorkerQueue, ^(void) {
[target performSelector:aSelector];
});
}
I'm new to Obj-c. I've got a class which sets a var boolean to YES if it's successful (Game Center login = successful), what it would be great to do, is somehow have a listener to that var that listens to when it is YES and then executes some code. Do I use a block for that? I'm also using the Sparrow framework.
Here's my code in my GameCenter.m file
-(void) setup
{
gameCenterAuthenticationComplete = NO;
if (!isGameCenterAPIAvailable()) {
// Game Center is not available.
NSLog(#"Game Center is not available.");
} else {
NSLog(#"Game Center is available.");
__weak typeof(self) weakSelf = self; // removes retain cycle error
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer]; // localPlayer is the public GKLocalPlayer
__weak GKLocalPlayer *weakPlayer = localPlayer; // removes retain cycle error
weakPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
{
if (viewController != nil)
{
[weakSelf showAuthenticationDialogWhenReasonable:viewController];
}
else if (weakPlayer.isAuthenticated)
{
[weakSelf authenticatedPlayer:weakPlayer];
}
else
{
[weakSelf disableGameCenter];
}
};
}
}
-(void)showAuthenticationDialogWhenReasonable:(UIViewController *)controller
{
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:controller animated:YES completion:nil];
}
-(void)authenticatedPlayer:(GKLocalPlayer *)player
{
NSLog(#"%#,%#,%#",player.playerID,player.displayName, player.alias);
gameCenterAuthenticationComplete = YES;
}
-(void)disableGameCenter
{
}
But I need to know from a different object if that gameCenterAuthenticationComplete equals YES.
You can use a delegate pattern. It's far easier to use than KVO or local notifications and it's used a lot in Obj-C.
Notifications should be used only in specific situations (e.g. when you don't know who wants to listen or when there are more than 1 listeners).
A block would work here but the delegate does exactly the same.
You could use KVO (Key-Value Observing) to watch a property of your object, but I'd rather post a NSNotification in your case.
You'll need to have the objects interested in knowing when Game Center login happened register themselves to NSNotificationCenter, then post the NSNotification in your Game Center handler. Read the Notification Programming Topics for more details !
If there is a single method to execute on a single delegate object, you can simply call it in the setter. Let me give a name to this property:
#property(nonatomic,assign, getter=isLogged) BOOL logged;
It's enough that you implement the setter:
- (void) setLogged: (BOOL) logged
{
_logged=logged;
if(logged)
[_delegate someMethod];
}
Another (suggested) way is to use NSNotificationCenter. With NSNotificationCenter you can notify multiple objects. All objects that want to execute a method when the property is changes to YES have to register:
NSNotificationCenter* center=[NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(handleEvent:) name: #"Logged" object: nil];
The handleEvent: selector will be executed every time that logged changes to YES. So post a notification whenever the property changes:
- (void) setLogged: (BOOL) logged
{
_logged=logged;
if(logged)
{
NSNotificationCenter* center=[NSNotificationCenter defaultCenter];
[center postNotificationName: #"Logged" object: self];
}
}
I have a solution for a notification problem which works well, but I'm afraid might be a bad idea.
I have a notification that needs to be handled by each instance of a class and by the class itself. To handle this, I'm registering for a notification by both the class and instances of the class. Because it's the exact same notification, I've named the class and instance method the same. This follows the standard we've set for how notification handlers are named.
Is this a bad idea? Is there some hidden got'ca that I'm missing. Will I be confusing the heck out of future developers?
+ (void)initialize
{
if (self == [SICOHTTPClient class]) {
[[self notificationCenter] addObserver:self
selector:#selector(authorizationDidChangeNotification:)
name:SICOJSONRequestOperationAuthorizationDidChangeNotification
object:nil];
}
}
- (id)initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (self) {
self.parameterEncoding = AFJSONParameterEncoding;
[self registerHTTPOperationClass:[SICOJSONRequestOperation class]];
[self setDefaultHeader:#"Accept" value:#"application/json"];
if ([[self class] defaultAuthorization])
[self setDefaultHeader:#"Authorization" value:[[self class] defaultAuthorization]];
[[[self class] notificationCenter] addObserver:self
selector:#selector(authorizationDidChangeNotification:)
name:SICOJSONRequestOperationAuthorizationDidChangeNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[[[self class] notificationCenter] removeObserver:self
name:SICOJSONRequestOperationAuthorizationDidChangeNotification
object:nil];
}
#pragma mark Notifications
- (void)authorizationDidChangeNotification:(NSNotification *)notification
{
NSString *authorization = notification.userInfo[SICOJSONRequestOperationAuthorizationKey];
if ([authorization isKindOfClass:[NSString class]]) {
[self setDefaultHeader:#"Authorization" value:authorization];
} else {
[self clearAuthorizationHeader];
}
}
+ (void)authorizationDidChangeNotification:(NSNotification *)notification
{
NSString *authorization = notification.userInfo[SICOJSONRequestOperationAuthorizationKey];
if ([authorization isKindOfClass:[NSString class]]) {
[self setDefaultAuthorization:authorization];
} else {
[self setDefaultAuthorization:nil];
}
}
This is what code comments are for :)
There's no problem in Objective C with a class method and instance method having the same name.
I would suggest either:
amend your notification method name spec to handle this (and then handle the class notification with a different appropriately named method), or
add appropriate comment to explain what's happening for benefit of future potentially confused developers
The language itself and the runtime will see no ambiguity in what you're doing. So you're safe on that front.
In terms of confusing future maintainers I guess you needn't be too concerned with silly autocomplete mistakes because it's not a method you intend to make manual calls to.
That said, I'd be tempted to move the class stuff into an artificial category. That'll not only give separation on the page but make it explicit that the class intends to respond as a separate tranche of functionality from the instance responses.
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.