Handle device rotation for "More" UINavigationController in UITabBarController.moreNavigationController - objective-c

I have an app which has to work in both portrait and landscape more and the UITabBar should adjust to current orientation (it has custom background and selected items). So, for the rest of views I just override the - (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation method and it works perfectly.
How would I do that for the .moreNavigationController of UITabBarController ? I've tried adding an observer (the selector is in extension of UITabBarController):
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:self.moreNavigationController];
but it never get called.
Am I missing something or what would be the best way to handle this situation ?
Solution: somewhy UIDeviceOrientation is not firing correctly, so better to use statusBarOrientation, works as a charm.
the final code which work is this:
in main UITabBarController, viewDidLoad:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
the didRotate selector method:
- (void) didRotate:(NSNotification *)notification{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if(UIInterfaceOrientationIsPortrait(orientation)) {
// Portrait
} else {
// Landscape
}
}
Thanks for help.

You are registering your UITabBarController for a notification which never gets posted. Take a look at the documentation NSNotificationCenter Reference for the addObserver:selector:name:object method
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
notificationSender:
The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer.
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer.
so, if you specify the . moreNavigationController as the sender, you wont get those notifications, because it never posts such ones. Instead pass nil to ignore the sender and listen to the status bar change regardless of who sent it.
By the way, in this SO Answer is a summary of how you can react to orientation change.
And at last. If it still doesn't work, you can try this:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification
object:nil];

Yes, you forgot to post notification, which will call you own notification:
[[NSNotificationCenter defaultCenter] postNotificationName: UIApplicationDidChangeStatusBarOrientationNotification object:self];
or if you dont wont to send anything just set object as nil:
[[NSNotificationCenter defaultCenter] postNotificationName: UIApplicationDidChangeStatusBarOrientationNotification object:nil];

The best way to implement the same is first addObserver and then remove observer to avoid the crash:-
-(void)viewDidLoad{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification" object:nil];
}
//Now Remove Observer
-(void)viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self #"UIDeviceOrientationDidChangeNotification" object:nil];
}

Related

Is there a way to pass additional data to UserInfo dictionary using NSNotificationCenter with UIKeyboardDidShowNotification

I have these lines of code:
-(void)someMethod:(UIScrollView*)scrollView{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
}
-(void)keyboardWasShown:(NSNotification *)aNotification{
// I want to get scrollView over here
}
Is there a way to make this possible? I've tried to perform this staff according to
How to pass a NSDictionary with postNotificationName:object:
but aNotification.userInfo doesn't contain my data
[[NSNotificationCenter defaultCenter]
postNotificationName:UIKeyboardDidShowNotification
object:self
userInfo:#{#"scrollView":scrollView}];
You are listening to UIKeyboardDidShowNotification and then you post UIKeyboardWillHideNotification that won't work. First maybe you like create your own notification name i.e XYZKeyboardDidShowNotification and then register for that and post using that name with the additional data you want. And then you can do:
-(void)keyboardWasShown:(NSNotification *)aNotification{
UIScrollView *scrollView = aNotification.userInfo[#"scrollView"];
}

Objective-C: How to remove notification by selector?

When setting up notification, you can set different selector to react to it. But there seems no way to remove notification by selector. For example:
// e.g. React to background notification by calling method 1
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(method1:) name:notification object:nil];
// e.g. React to background notification by calling method 2
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(method2:) name:notification object:nil];
Now when the notification fires, both methods will react to it.
How do I remove notification selectively (e.g. remove notification handler method1)?
There is a way to do it, but I don't think you will like it.
Use -addObserverForName:object:queue:usingBlock: instead.
__weak typeof(self) weakSelf = self;
// e.g. React to background notification by calling method 1
self.method1Observer = [[NSNotificationCenter defaultCenter] addObserverForName:notification object:nil queue:nil usingBlock:^(NSNotification *note) {
[weakSelf method1:note];
}];
// e.g. React to background notification by calling method 2
self.method2Observer = [[NSNotificationCenter defaultCenter] addObserverForName:notification object:nil queue:nil usingBlock:^(NSNotification *note) {
[weakSelf method2:note];
}];
Then later on:
// Remove method 1 observer while keeping method 2 observer.
if (self.method1Observer != nil) {
[[NSNotificationCenter defaultCenter] removeObserver:self.method1Observer];
self.method1Observer = nil;
}
Update: I forgot to nil check self.method1Observer before passing it to -removeObserver:.
you have to remove that notification when it will not necessary in that class. So simply use below code for remove already added Notification.
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"Notification" object:nil];

NSNotificationCenter removeObserver:name:object: not removing observer

I have a method in a view controller that sets up some notifications:
- (void)processState
{
MYGame *game = [[MYGameManager sharedInstance] getGameAtIndex:self.indexPath.row];
if(game)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notification_gameUpdated:) name:kMYNotificationGameUpdated object:game];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notification_gameEnded:) name:kMYNotificationGameEnded object:game];
}
}
Then there's a game updated method, which is called every so often:
- (void)notification_gameUpdated:(NSNotification *)notification
{
MYGame *game = notification.object;
_game_status = (game.entity.bet.isWinning) ? MYGameStatusWin : MYGameStatusLose;
}
And finally, when the game ends:
- (void)notification_gameEnded:(NSNotification *)notification
{
MYGame *game = notification.object;
// Clear the notifications
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMYNotificationGameUpdated object:game];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMYNotificationGameEnded object:game];
self.gameIsActive = NO;
}
Trouble is, that even when I remove the observers (and a breakpoint shows that this is happening), then the notification_gameUpdated: method is still being called. If I change it to
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMYNotificationGameUpdated object:nil];
This still won't clear it. But if I change it to
[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:game];
Then that does clear it. As does
[[NSNotificationCenter defaultCenter] removeObserver:self];
But I'm not keen on doing either, because I'd rather the code was clean and I don't want any "gotchas" further down the line if I need to add more observers. I've checked the rest of the code and cannot find any other classes adding observers to this object, although other view controllers do listen to the same messages.
Is processState called more than once? That would explain the behavior you are seeing.
If it is, one way to fix the issue would be to always remove listeners before adding them. See e.g. this answer.
edit #2
try registering with object:nil and when you post the notification include the reference to game in the userInfo dictionary. then, in the receiver, you can compare against game and perform whatever action you want if it is a match. this should get you the same behavior as if you were using object:game, although it does not explain why your current implementation isn't working
when you register for notifications like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notification_gameUpdated:)
name:kMYNotificationGameUpdated
object:game];
the #selector will only be performed if that particular instance of game is the sender.
is it possible that you're re-initializing your shared instance of game after registering? that could cause the behavior you're experiencing
try registering for notifications with object:nil and see what happens. (assuming there are not multiple games running concurrently)
So it turned out that the reason for the issue was Method Swizzling. The project I'm working on has addObserver:selector:name:object: and removeObserver:name:object: swizzled. The issue was that although addObserver has been handled correctly, removeObserver is only removing objects on specific conditions. This will obviously need to be changed...
But I post this as a warning to others... Swizzling can be dangerous to your health!
Apologies for any time wasted.

iPad : How to know Return key of iPad keyboard is pressed ? Please check image

I want to know that which method will be called when the following key is pressed.
I want to start action on above key press.
How do I know this is pressed ?
Observe the UIKeyboardDidHideNotification notification.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
And...
- (void)keyboardDidHide:(NSNotification *)aNotification {
}
You can also change it to UIKeyboardWillHideNotification if you need to be notified BEFORE the keyboard starts to disappear.
That's not a return key. Return key is the one above it. That's simply a button that dismisses the keyboard and you can't recognize it via standard text input methods. You need to register for UIKeyboardWillHideNotification notification.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
and implement that method:
- (void)keyboardWillHide:(NSNotification *)notification
{
// do whatever you want to do when keyboard dismiss button is tapped
}
Not sure if it's exactly what you are looking for, but you can try using notifications. Don't have Mac nearby atm, so just copy-pasting the code from github. I have that code in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
and then 2 methods:
- (void)keyboardWillShow:(NSNotification *)notification {
}
- (void)keyboardWillHide:(NSNotification *)notification {
}
Hope it helps
Use keyboard hide UIKeyboardWillHideNotification notification.
Example.

Removing a NSNotificationCenter observer in iOS 5 ARC

I have an iOS 5 ARC-based project, and am having difficulty about where I should be removing the observer for the NSNotificationCenter observations which I have registered within a UIViewController. Similar posts on SO have said this should be done in the -dealloc method. Even though this method is not required in ARC projects I have added it with the following code:
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
As a test, I open the UIViewController (within a UINavigationController), do some things which trigger the notifications, and then pop it off the stack by tapping the Back button. I then reopen the UIViewController, and do some more things to trigger the notifications, but notice that each callback is being called twice - an indication that the previous notifications have not been deregistered. Repeating this procedure just causes each callback to be called more than more times, so they appear to never be deregistering.
Any help would be appreciated!
It's pretty clear your dealloc method isn't being called (nor is the removeObserver call).
Why not remove your UIViewController's observer in the viewDidUnload: or viewWillDisappear: methods?
If your dealloc isn't being called, it's likely because someone is still holding a reference to the view controller. Perhaps you need to mark something as __weak? You can use the allocations instrument to help track down what's holding on to your view controller.
"I also need the notification callbacks to still be fired if the view is off-screen" -> you may need to register UIApplicationWillEnterForegroundNotification. If so, let try this:
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"viewWillAppear");
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
NSLog(#"viewWillDisappear");
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(#"applicationWillEnterForeground");
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
// do your stuff here
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"applicationDidEnterBackground");
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
The idea is adding or removing UIApplicationDidEnterBackgroundNotification whenever coming in and out of your screen. We just register UIApplicationWillEnterForegroundNotification when the app enter background and remove once it's back. Be noticed that we just remove UIApplicationDidEnterBackgroundNotification when viewWillDisappear.
My dealloc() is not called by somehow, so I found this way, hope it useful for you too.
Enjoy :)