Detect autorotation inside UIView subclass - iphone

I'm creating a subclass of UIView that needs to relayout its subviews when the orientation changes.
it needs to be able to "plug in anywhere" without any extra code so I'd rather not rely on the UIViewController methods to detect the autorotation
Is there anyway for the UIView to know when the orientation has changed?

How about a notification like this to detect the orientation change??
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
But, before that you need to register for generating notifications like this.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

I found that for me this worked better than the UIDeviceOrientationDidChangeNotification:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("layoutControls"), name: UIApplicationDidChangeStatusBarOrientationNotification, object: nil)
I didn't even need to add the first line with beginGeneratingDeviceOrientationNotifications.

In Swift, it will be done like this:
NotificationCenter.default.addObserver(self, selector: #selector(orientationChange(inNotification:)), name: NSNotification.Name.UIApplicationDidChangeStatusBarOrientation, object: nil)
#objc private func orientationChange(inNotification:Notification) -> Void {
// handle notification
}

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

NSNotification only NSLog works

In my Class X I post a Notification like this:
[[NSNotificationCenter defaultCenter] addObserver:viewController
selector:#selector(doThis:)
name:#"myNotification"
object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:nil];
In my Class Y I recieve it like this:
- (void) doThis: (NSNotification *) notification {
NSLog(#"It works.");
[uiTextView resignFirstResponder]; }
The console shows the NSLog message but my UITextView does not hide its keyboard.
(In e.g. viewDidLoad the resignFirstResponder/becomeFirstResponder works.)
Is there any special thing I need to do?
FWIW, in most, but not all, cases, observers should be added and removed by the observer itself, not by a separate object. (What happens if the observer goes away before the separate object, and fails to have the observer properly removed? Or vice-versa? It makes it all too easy to either leak observers or crash on notifications to deallocated objects.)
Anyhow, first thing's first: have you verified that uiTextView is not nil and points at the first responder? I rather suspect that uiTextView is not what you think it is.
As Conrad says, observers should be added and removed by themselves...
Use the best practice to define the name of the notifications as static constants like follows:
static NSString *const kMyNotification = #"myNotification";
Why? because there is a risk that both #"myNotification" might be two different objects and then the notificationName is different and you won't receive the notification. Since I always declare them as static constants I have never had issues with NSNotifications.
Then use it like this:
To register the observer
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(doThis:)
name: kMyNotification
object: nil];
To post the notification
[[NSNotificationCenter defaultCenter] postNotificationName: kMyNotification
object: nil];
To remove the observer:
[[NSNotificationCenter defaultCenter] removeObserver: self];

Determine when a UIMenuController is dismissed?

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)

Custom Keyboard (iPhone), UIKeyboardDidShowNotification and UITableViewController

On an iPhone App, I've got a custom keyboard which works like the standard keyboard; it appears if a custom textfield becomes first responder and hides if the field resigns first responder. I'm also posting the Generic UIKeyboardWillShowNotification, UIKeyboardDidShowNotification and their hiding counterparts, like follows:
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:5];
[userInfo setObject:[NSValue valueWithCGPoint:self.center]
forKey:UIKeyboardCenterBeginUserInfoKey];
[userInfo setObject:[NSValue valueWithCGPoint:shownCenter]
forKey:UIKeyboardCenterEndUserInfoKey];
[userInfo setObject:[NSValue valueWithCGRect:self.bounds]
forKey:UIKeyboardBoundsUserInfoKey];
[userInfo setObject:[NSNumber numberWithInt:UIViewAnimationCurveEaseOut]
forKey:UIKeyboardAnimationCurveUserInfoKey];
[userInfo setObject:[NSNumber numberWithDouble:thisAnimDuration]
forKey:UIKeyboardAnimationDurationUserInfoKey];
[[NSNotificationCenter defaultCenter] postNotificationName:UIKeyboardWillShowNotification
object:nil
userInfo:userInfo];
This code is working and I use it in UIViewController subclasses.
Now since iPhone OS 3.0, UITableViewController automatically resizes its tableView when the system keyboards show and hide. I'm only now compiling against 3.0 and I thought that the controller should also resize the table if my custom keyboard appears, since I'm posting the same notification. However it doesn't. The table view controller is set as the delegate of the input fields.
Does anyone have an idea why this might be the case? Has anyone implemented something similar successfully?
I have standard input fields along the custom ones, so if the user changes the fields the standard keyboard hides and the custom one shows. It would be beneficial if the tableView didn't resize to full height and I didn't have to resize it back with a custom method.
Well there are a few possibilities that you poke around in. From your description, it seems that UITableView is not using the UIKeyboard Notifications.
But possibly, it is the UINavigationController that is responding to this notification (or UITabBarController).
You could override methods like setFrame: drawRect: and setNeedsDisplay in the tableview to see what is happening in the call stack. You may be able to figure out what is actually causing the tableview to redraw at the correct size.
But in all likelihood, just changing the size of the tableView yourself is the much easier solution. These suggestions are just for fun!
I have done something similar. If I recall, I ended up just having the TableViewController subecibe to the notifications that either you send or the system sends, and then animate the change to the tableview's frame. Presumably there are doing something similar internally, but I think the end result just becomes two animation blocks wrapped around each other that both run when the system is posting the notifications, but the end result should be the same.
In your viewdidLoad:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(keyboardWillShow:)
name: UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(keyboardDidShowOrHide:)
name: UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(keyboardWillHide:)
name: UIKeyboardWillHidewNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(keyboardDidShowOrHide:)
name: UIKeyboardDidHideNotification object:nil];
and in the view controller:
-(void) keyboardWillShow:(id)sender {
[UIView beginAnimations];
[UIView setAnimationDuration:0.3];
self.view.frame = //Your new size
}
-(void) keyboardDidShowOrHide:(id)sender {
[UIView commitAnimations];
}
-(void) keyboardWillHide:(id)sender {
[UIView beginAnimations];
[UIView setAnimationDuration:0.3];
self.view.frame = //Your old size
}

Is there a way to catch an WillRotateToInterfaceOrientation event from an UIView?

every UIViewController has a method called willRotateToInterface.
Is it possible to do this within a UIView too?
Does this match the idea of model view controller ?
The only way I can think of is to send the event from the UIViewController to the UIView.
Is there a global variable for the current orientation?
Observe UIDeviceOrientationDidChangeNotification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
...
- (void)orientationDidChange:(NSNotification *)note
{
NSLog(#"new orientation = %d", [[UIDevice currentDevice] orientation]);
}
UIDevice Class Reference
I should note that yo uneed to add -beginGeneratingDeviceOrientationNotifications when you want these notifications to be sent, and call -endGeneratingDeviceOrientationNotifications when you want them to stop. There is a battery impact of generating these, so you should only do so when your view is on screen. UIViewController does all this for you, so if you have a view controller, it is worth letting it do the work.
If you just want to adjust view to new size/layout when orientation changes, you should just override its layoutSubviews method.
It will be called whenever size of the view changes, which usually happens when view is rotated.
You can subscribe to a global notification and get a call when the device is rotated, it wont do anything for you though..
[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification" object:nil];