I am having an issue with receiving an NSNotification that is being sent from an NSButton that has been subclassed to detect double clicks. Which itself is used within a subclass of NSView.
When I post the notification to the default notification center it never arrives in my appDelegate where I am listening for it.
Here is my NSButton subclass:
#import "DoubleClickButton.h"
#import "AppDelegate.h"
#implementation DoubleClickButton
- (void)mouseDown:(NSEvent *)theEvent
{
NSInteger clickCount = [theEvent clickCount];
if (2 == clickCount)
{
[self performSelectorOnMainThread:#selector(handleDoubleClickEvent:) withObject:nil waitUntilDone:NO];
}
}
-(void)handleDoubleClickEvent:(NSEvent *)event
{
NSLog(#"DoubleClick");
[[NSNotificationCenter defaultCenter] postNotificationName:#"doubleClickButtonNotification" object:nil];
}
#end
Listening in my AppDelegate applicationDidFinishLaunching method:
//Notification for Double tap notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doubleTapAction:)
name:#"doubleClickButtonNotification"
object:self];
The notification never arrives and doubleTapAction: is never called.
Please could someone point me in the correct direction as it is melting my brain...
Many Thanks in advance
Ben
First of all: User events (as a mouse down) are always delivered on the main thread. Therefore you do not need the -performSelectorOnMainThread:….
To your Q: Likely you misunderstood the object parameter. Why do you set it to nil when posting a notification and set it to self for adding the observer? Since nil is not $anySelf that does not match. You can set this parameter to nil when you add the observer to get notifications of all senders. So simply do it the other way round. (Setting object to something useful when posting the notification and to nilwhen adding the observer.)
Related
My environment is Yosemite 10.10.5 with Xcode 7.2 using ARC.
In a simple test program, I am attempting various ways to dismiss a NSViewController and all of them are showing problems with memory handling.
In my primary view controller, I have the following code. (The notification pieces are there to test various ways of dismissing the presented controller.)
- (IBAction)showFirstReplacement:(id)sender {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissWithNotification:) name:#"removeFirst" object:nil];
NSStoryboard *sb = [self storyboard];
FirstReplacement *controller = [sb instantiateControllerWithIdentifier:#"first_replacement"];
[self presentViewControllerAsSheet:controller];
}
- (void)dismissWithNotification:(NSNotification *)notification {
NSViewController *controller = [notification object];
[self dismissViewController:controller];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Inside FirstReplacement, I have:
- (IBAction)dismiss:(id)sender {
[self dismissViewController:self];
// [[NSNotificationCenter defaultCenter] postNotificationName:#"removeFirst" object:self];
// [[self presentingViewController] dismissViewController:self];
}
Uncommenting any one of the three lines in this method produces the correct visual results but.... Depending on which of the calls I enable inside dismiss:, I get different results when profiling. Using self dismissViewController:, I see no leaks but FirstReplacement objects are not deallocated. Using either of the other two approaches gets rid of the dismissed FirstReplacement but leaks one 16-byte malloc block and one NSMutableArray every time a view controller is dismissed.
According to Instruments, the leaks are related to a method called [NSViewController _addPresentedViewController:].
Are there other clean-up steps necessary to prevent these leaks (or memory bloat in the non-leak case)?
The view controller that presents another view controller is also responsible for dismissing it. So none of the lines in FirstReplacement's dismiss method are correct. Instead, you should be creating a delegate in FirstReplacement so it can notify its delegate (the primary view controller) that it should be dismissed.
FirstReplacement.h
#class FirstReplacement;
#protocol FirstReplacementDelegate <NSObject>
- (void)firstReplacementShouldDismiss:(FirstReplacement *)controller;
#end
#interface FirstReplacement : NSViewController
#property (nonatomic, weak) id<FirstReplacementDelegate> delegate;
#end
FirstReplacement.m
- (IBAction)dismiss:(id)sender {
[self.delegate firstReplacementShouldDismiss:self];
}
Then in your primary view controller:
- (IBAction)showFirstReplacement:(id)sender {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissWithNotification:) name:#"removeFirst" object:nil];
NSStoryboard *sb = [self storyboard];
FirstReplacement *controller = [sb instantiateControllerWithIdentifier:#"first_replacement"];
controller.delegate = self;
[self presentViewControllerAsSheet:controller];
}
- (void)firstReplacementShouldDismiss:(FirstReplacement *)controller {
[self dismissViewController:controller];
}
While it may seem like posting a notification is the same as a delegate, it is not. The difference is that when dismissWithNotification fires, you are still executing the code from FirstReplacement::dismiss. NSNotificationCenter::postNotificationName does not finish executing until all observers have finished executing their selectors. So even though the dismissal code is executing in the primary view controller, it still being run from the dismiss method.
If you are still not convinced, override FirstReplacement::dealloc to print a log statement. You will see that dealloc is not called using any of your methods, but will be called using delegation.
I want my game to pause when device orientation is initiated. I have this method in my viewcontroller, which works fine:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
NSLog(#"I am starting to rotate, should pause game..");
}
But how to listen for device rotation from within my SKScene, where the actual game is playing. Hope I have made myself clear enough. Thanks for any help!
You can use NSNotification. Keeping with your code, add the following line in your ViewController init:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"RotateNotification"
object:self];
Then in your SKScene init add this line:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"RotateNotification"
object:nil];
Also add the called method to SKScene:
-(void) receiveNotification:(NSNotification *)notification
{
NSLog (#"Received notification");
// do what you have to do here...
}
I am trying to set the position of a UIScrollView by using contentOffset as such:
- (void) navigateToTableViewPosition:(CGPoint)contentOffset {
NSLog(#"Position set method gets called...");
NSLog(#"%#", NSStringFromCGPoint(contentOffset));
[mainScrollView setContentOffset:contentOffset animated:YES];
}
I call this method from another view controller before I dismiss it, and everything checks out. I pass the argument correctly, and the method gets called (checked it with NSLog), but the scroll view does not move...
What is funny is that when I call this method from the view controller, in which it is located, it works fine. Only when I call it from another view controller, it stops working.
Just for future reference, here is the calling method:
MainViewController *mainView = [[MainViewController alloc] init];
[mainView navigateToTableViewPosition:contentOffset];
Content offset is a CGPoint I set beforehand. It doesn't matter here; besides, it gets passed correctly anyways.
Try this, You have to send notification from other viewcontroller when you want to change ..
[[NSNotificationCenter defaultCenter] postNotificationName:#"changepostion" object:NSStringFromCGPoint(CGPointMake(contentOffset.x, contentOffset.y))];
in mainviewcontroller
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(navigateToTableViewPosition:) name:#"changepostion" object:nil];
}
- (void) navigateToTableViewPosition:(NSNotification *)notification
{
contentOffset =CGPointFromString([notification object]);
NSLog(#"Position set method gets called...");
NSLog(#"%#", NSStringFromCGPoint(contentOffset));
[mainScrollView setContentOffset:contentOffset animated:YES];
}
You can't set the properties of a view which is not visible. If you are using iOS5+ you can implement the offset setting in the completion in the view dismiss completion block.
Use delegate for backward messaging in view controllers.
Refer Basic Delegate Example link for more reference.
Your are making new instance of viewcontroller which will call method but will have no effect.
what is method call when change size in Window?
I find somesing aboud windowDidResize: so i try doing
- (void)windowDidResize:(NSNotification *)notification {
NSLog(#"test");
}
I found what need use NSWindowDidResizeNotification, but I work for the first time with NSNotification and bad understand about this.
Can somebody write a full example for my event, please?
The -windowDidResize: method is called on the window delegate. Is the object with the method you posted the delegate for the window?
For something other than the delegate, you can do:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(windowDidResize:) name:NSWindowDidResizeNotification object:theWindow];
and, when the observer is no longer interested or being deallocated:
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:theWindow];
Another approach is to use the new block-based API to NSNotificationCenter:
id observation = [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidResizeNotification object:theWindow queue:nil usingBlock:^(NSNotification *){
NSLog(#"test");
}];
// store/retain the observation for as long as you're interested in it. When it's deallocated, you stop observing.
You can Implement NSWindowDelegate:
class YourVC: NSWindowDelegate {
// This method trigger when you press the resize button in the window toolbar
func windowDidResize(_ notification: Notification) {
// Write your code here
}
}
And, In viewDidLoad() or viewDidAppear() method
self.view.window?.delegate = self
You can also use other delegate methods:
windowDidEnterFullScreen
windowDidExitFullScreen
...
I have defined the controller to receive the events.
#interface salesViewController : UIViewController
<UITextFieldDelegate>{
However, none of my events are not firing.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
//this is not getting called
}
In Interface Builder I assigned the TextField delegate to the salesView.
What am I missing?
You have to set the delegate properly. You observe the protocol, but you need to do this:
#interface YourController : UIViewController<UITextFieldDelegate> {
IBOutlet UITextField* field;
}
#end
#implementation YourController
-(void)viewDidLoad
{
[field setDelegate:self];
}
And you will receive the events. Alternatively, you can set the delegate in Interface Builder as well, along with doing it programmatically in loadView, allocating the field and setting the delegate.
Additionally, try to use NSNotificationCenter as little as possible. Notifications are somewhat obsolete unless there isn't really a direct path between you and the object in question. Just a small comment on the answer above.
what are you trying to accomplish? textFieldDidBeginEditing is messaged whenever the user selects the text field. If you are trying to update a label or something as the user makes edits, you need to setup an observer w/ NSNotificationCenter and watch for the notification that is fired whenever this happens.If you take this approach, make sure to remove the observer once you are done with it
for example:
#pragma mark
#pragma mark -
#pragma mark Notification Observers
- (void)addObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:) name:#"UITextFieldTextDidChangeNotification" object:nil];
}
- (void)removeObservers {
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UITextFieldTextDidChangeNotification" object:nil];
}
if you need to keep tabs on multiple text fields, do something like this for your selector:
- (void)textFieldDidChange:(NSNotification*)aNotification {
UITextField *textField = (UITextField *)[aNotification object];
if([textField isEqual:usernameTextField])
{
[user setUsername:usernameTextField.text];
}
else if([textField isEqual:phoneNumberTextField])
{
[user setPhoneNumber:phoneNumberTextField.text];
}
}