Determine when a UIMenuController is dismissed? - iphone

Is there a way to determine when a UIMenuController has been dismissed? I have a (non-editable) text area I'm highlighting when the menu is brought up, and I'd like to un-highlight it when they either select an item (easy) or cancel (not possible?)

On state changes UIMenuController posts notifications to the default NSNotification center. You can subscribe to them to get notified when the system hides the menu:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willHideEditMenu:) name:UIMenuControllerWillHideMenuNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didHideEditMenu:) name:UIMenuControllerDidHideMenuNotification object:nil];

Building on #Markus Müller's suggestion, here's a pattern you can copy:
- (BOOL)becomeFirstResponder
{
// starts listening for UIMenuControllerDidHideMenuNotification & triggers resignFirstResponder if received
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(resignFirstResponder) name:UIMenuControllerDidHideMenuNotification object:nil];
return [super becomeFirstResponder];
}
- (BOOL)resignFirstResponder
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidHideMenuNotification object:nil];
// your custom cleanup code here (e.g. deselection)
return [super resignFirstResponder];
}
In my case I have hundreds of selectable objects, so I didn't want them all observing this notification all the time! What this pattern does start observing when it gains firstResponder, triggers resignFirstResponder when the menu is dismissed, and ends observing in the same.
In my my case (as in the OP's), as the item is uneditable it is desirable for me to call resignFirstResponder when the menu is dismissed. This way, resignFirstResponder gets called if whether they select one of the menu options, or not, so the cleanup code will always fire.

Swift 3 & 4
NotificationCenter.default.addObserver(
self,
selector: #selector(self.didHideEditMenu),
name: NSNotification.Name.UIMenuControllerDidHideMenu,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.willHideEditMenu),
name: NSNotification.Name.UIMenuControllerWillHideMenu,
object: nil)

Swift 5
NotificationCenter.default.addObserver(
self,
selector: #selector(willHideMenu),
name: UIMenuController.willHideMenuNotification,
object: nil)

Related

No orientation-related notifications are being fired. Why?

I want to be notified when the orientation of the device changes. I've setup a test method that's suppose to receive the notification. I'm trying several different observers to achieve that, and none of them are working. Why isn't testMethod being fired?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
// register for orientation change notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(testMethod) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(testMethod)
name: UIApplicationWillChangeStatusBarOrientationNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(testMethod)
name: UIApplicationDidChangeStatusBarOrientationNotification
object: nil];
}
- (void)testMethod
{
NSLog(#"phone was rotated");
}
I accidentally had the rotation lock engaged on my phone. Always test on more than one phone!
You need to use the Orientation notification, which is UIDeviceOrientationDidChangeNotification. Do not put it inside the #"UIDeviceOrientationDidChangeNotification" because you dont know the actual content of the constant which is UIDeviceOrientationDidChangeNotification. It appears you are using a view controller. You should use willRotateToInterfaceOrientation:duration: and didRotateFromInterfaceOrientation:
end of the bool type something set the orientation .write return yes in all end of the view ..orientation in potrait and landscape mode takeplace

How to send object in NSNotification?

I want to send object to selector in NSNotification.I mean, I have 3 buttons and on click of each button I am registering notification and when that event occurred I am calling one selector and in that selector I want to find out which button user has clicked because I have common action for all 3 buttons.
-(void)allThreeButtonAction:(sender)id
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(performSomeOperationWhenEventOccur) name:#"EventCompletedNotification" object:nil];
}
//Some event occurred, so I am sending notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"EventCompletedNotification" object:nil];
//Notified method
-(void)performSomeOperationWhenEventOccur
{
//Here I want to know which button is pressed.
}
I hope I am clear.
You may want to look at postNotificationName:object:userInfo: from NSNotificationCenter documentation
You simply send a UserInfo containing whatever you need to identify the button (easiest is the pointer to the button) that you retrieve in your selector.
Your selector signature should receive the notification:
- (void)performSomeOperationWhenEventOccur:(NSNotification*) notification:(NSNotification*) notification
{
// Use [notification userInfo] to determine which button was pressed...
}
Don't forget to modify the selector name when your register it:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(performSomeOperationWhenEventOccur:) name:#"EventCompletedNotification" object:nil];
You can't pass an object when adding a notification observer, so you'll have to store the button that was pressed somewhere:
-(void)allThreeButtonAction:(id)sender
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(performSomeOperationWhenEventOccur) name:#"EventCompletedNotification" object:nil];
self.buttonPressed = sender;
}
Then you can just read it in your notification handler:
-(void)performSomeOperationWhenEventOccur
{
if ( self.buttonPressed = self.button1 )
...
}
Below Snippet will help you.
Button1
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(performSomeOperationWhenEventOccur:) name:#"button1" object:button1];
Button2
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(performSomeOperationWhenEventOccur:) name:#"button2" object:button2];
Change method to following
- (void) performSomeOperationWhenEventOccur:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"button1"])
{
NSButton *button1=[notification button1];
NSLog (#"Successfully received the test notification! from button1");
}
else
{
NSButton *button2=[notification button2];
NSLog (#"Successfully received the test notification! from button2");
}
}

"unrecognized selector sent to instance" when keyboard appears

When I click a text field into my app screen and the keyboard is showing up xcode debugger shows this error:
[mainViewController keyboardWasShown]: unrecognized selector sent to instance 0x5867ac0
In the viewDidLoad method of the mainViewController I'm calling the registerForKeyboardNotifications method like that:
[self registerForKeyboardNotifications];
Here's its implementation (in mainViewController.m):
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
}
Any idea what could be wrong?
Make sure the notification selector has the colon at the end; this is important, keyboardWasShown and keyboardWasShown: are different selectors.

Pass An Object Between 2 View Controllers

I'm trying to pass an object between 2 VCs, from a popover to the detail view of split view controller.
I think I need to use NSNotificationCenter.
I tried this but can't seem to get it to work.
In didSelectRow of popover
[[NSNotificationCenter defaultCenter] postNotificationName:#"PassObject" withObject:objectToPass];
In detail VC
- (void) didReceiveNotificationPassObject:(NSNotification*)notification
{
YourObjectClass *theObject = (YourObjectClass*)notification.object;
}
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didReceiveNotificationPassObject:) name:#"PassObject" object:nil];
}
Probably just a typo when entering the question but in the first line where you post the notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"PassObject" withObject:objectToPass];
the method signature is wrong - it should be 'object:objectToPass' not 'withObject:objectToPass'. The line you have there will compile with a warning and crash at runtime.
Aside from that all the logic seems fine.
What is the problem you are facing? Does didReceiveNotificationPassObject: hit? If it doesn't, you could verify that viewDidLoad executes before didSelectRow.
Use [[NSNotificationCenter defaultCenter] postNotificationName:#"PassObject" object:objectToPass]; instead of [[NSNotificationCenter defaultCenter] postNotificationName:#"PassObject" withObject:objectToPass];
Also, don't forget to removeObserver in viewDidUnload.
HTH,
Akshay
A fast and easy solution to notify with multiple parameters is to call the notification it like this
[[NSNotificationCenter defaultCenter] postNotificationName:#"shareButton" object:#"camera"];
Where "camera" acts like your parameter. Then
- (void)shareButton:(id)sender
{
NSString *kindOf = [sender object];
if ([kindOf isEqualToString:#"camera"]) {
// Your code goes here
}
}

dismiss a modalviewcontroller when the application enters background

i need to dismiss my uiimagepicker modal viewcontroller automatically when the application enters the background.i tried to put the code to dismissmodalviewcontroller code in viewdiddissappear method,but its not being called.so i made a reference to the viewcontroller in appdelegate and tried to put it in the applicationdidenterbackgroundmethod but still it is not working.can someone point out the right way to do this
Try to add an NSNotificationCenter observer for UIApplicationDidEnterBackgroundNotification in the UIViewController that you want to dismiss. Use the selector to dismiss the modalview
- (void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(didEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver: self
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
- (void)didEnterBackground:(NSNotification*)note
{
[self.navigationController dismissModalViewAnimated:NO];
}
Best way to remove the modal when app is moving to background and it works fine .
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(dismissView:)
name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void)dismissView:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Also you can remove observer like this
[[NSNotificationCenter defaultCenter] removeObserver: self
name:UIApplicationDidEnterBackgroundNotification
object:nil];
I don't think you need to go through all that.
From the docs:
If you present several modal view controllers in succession, and thus build a stack of modal view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack.
Try calling [self dismissModalViewController:NO] from the parent view controller in your implementation of - (void) viewDidUnload.
This is untested, but the docs imply that it should do the job for you.