I have a Navigation controller that takes a user to another view and lays out the new view based on which button the user clicked on and the orientation. I have a method called layout and I call this method in viewDidLoad, viewWillAppear, and willAnimateRotationToInterfaceOrientation
However, when the view is first loaded, the app does not recognise it's in Landscape. The code I have is:
UILabel *shareLabel = [[UILabel alloc] initWithFrame:CGRectMake(488, 90, 179, 30)];
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
shareLabel.frame = CGRectMake(543, 90, 238, 30);
}
I've even tried putting a break point on the if statement, and when it gets to the if statement, it steps over it, which tells me it's not recognising that it is landscape? Any ideas on why this would be?
Check this out
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
if ( ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait) )
{
// do something for Portrait mode
}
else if(([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight) || ([[UIDevice currentDevice] orientation] == UIInterfaceOrientationLandscapeLeft))
{
// do something for Landscape mode
}
Related
UIDeviceOrientationDidChangeNotification is calling multiple times when the device changes orientation. I am unsure why this happens or how to fix it.
What I am trying to do is to keep the contentoffset of the scrollview the same, so when the user rotates the screen the app keeps the page they were on.
The odd thing is when I rotate the screen the first time the code executes like I would want. But every time after that the code executes multiple times and eventually the contentoffset is set o 0.
Here's what I have.
- (void)loadView {
//some code that sizes itself depending on the current orientation
//WILL BE CALLED AFTER EVERY ORIENTATION CHANGE
}
- (void)viewDidLoad {
//begin generating messages
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:[UIDevice currentDevice]];
//if is portrait and was landscape
if (orientation==1 && temp==2) {
int cal = offsetHolder.x/screenframe.size.height;
offsetHolder.x = cal * screenframe.size.width;
ScrollView.contentOffset = offsetHolder;
}
//if is landscape and was portrait
if (orientation==2 && temp==1) {
int cal = offsetHolder.x/screenframe.size.width;
offsetHolder.x = cal * screenframe.size.height;
ScrollView.contentOffset = offsetHolder;
}
}
On orientation change I change the value of 'int orientation' then call loadview to change the sizing of the view. Then I call viewdidload to get the proper contentoffset
- (void) orientationChanged:(NSNotification *)note {
CGRect screenframe = [[UIScreen mainScreen] bounds];
//holding the current offset
offsetHolder = ScrollView.contentOffset;
if ([[UIDevice currentDevice] orientation] == 1 || [[UIDevice currentDevice] orientation] == 0 || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown) {
temp=orientation;
orientation = 1;
[self loadView];
[self viewDidLoad];
}
else if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationFaceDown || [[UIDevice currentDevice] orientation] == UIDeviceOrientationFaceUp){
temp=orientation;
}
else{
temp=orientation;
orientation = 2;
[self loadView];
[self viewDidLoad];
}
}
EDIT:
I have found the problem. What I am doing is creating another instance of self.view instead of overwriting this one. Is there an easy way to destroy this view and re-initialize it?
EDIT2:
Have found a fix. I stopped calling loadview and viewdidload as per jsds' instructions. And instead moved all code in my loadview to another function that I called from loadview. All this code does is instantiate the UI (initview) objects and places them in the correct places depending upon orientation.
Then I create another function that removes all subviews from the view. Then on orientation change I call this function and my initview to destroy all subviews and then recreate them on orientation change.
UIDeviceOrientation basically has 6 different states, namely two portrait, two landscape, and face up and face down. So lets say when you pick up your device from flat position to vertical position, the notification will be triggered.
You can filter the faceup, facedown, and unknown states by using the macro UIDeviceOrientationIsValidInterfaceOrientation
UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
// Ignore changes in device orientation if unknown, face up, or face down.
if (!UIDeviceOrientationIsValidInterfaceOrientation(currentOrientation)) {
return;
}
If you still find the notification getting triggered multiple times, I am afraid you may need to use your custom logic with flags to check the previous and current value.
In my case, since I use reactive cocoa framework, the following code works for me :
if (IS_IPAD) {
#weakify(self);
[[[[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIDeviceOrientationDidChangeNotification object:nil]
takeUntil:[self rac_willDeallocSignal]]
map:^id (NSNotification *notification) {
return #([[UIDevice currentDevice] orientation]);
}]
filter:^BOOL(NSNumber *deviceOrientationNumber) {
UIDeviceOrientation currentOrientation = [deviceOrientationNumber integerValue];
//We ignore the UIDeviceOrientationUnknown, UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown
return UIDeviceOrientationIsValidInterfaceOrientation(currentOrientation);
}]
distinctUntilChanged]
subscribeNext:^(id x) {
#strongify(self);
//do something here...
}];
}
Here the distinctUntilChanged method makes sure the code gets triggered only when the orientation changes to a new valid value.
You should never call loadView or viewDidLoad yourself. That's up to the framework to manage.
If you are positioning or resizing views based on the view bounds in viewDidLoad, don't. Move that to viewWillLayoutSubviews. That will be called automatically when the device orientation changes.
Alternatively, use autolayout constraints and then you won't have to do anything at all.
I'm writing a program that detects a change in orientation of the device (portrait to landscape, etc.). When the device is in portrait a collectionview should be displayed and a scrollview should be hidden. When the device is in landscape mode the collectionview should be hidden and the scrollview should be displayed.
Through NSLogs I have confirmed that rotating the device is detected. and the hidden property is being set to YES.
I has essentially the same code working with two scrollviews, but due to performance issues I switched to using a collectionview and now it doesn't work. Any advice would be much appreciated. Thank You!
I have this code in the viewDidAppear method:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(detectOrientation) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
And this code in the detectOrientation method
-(void) detectOrientation {
if (([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) ||
([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)) {
[myCollectionView setHidden:YES];
myPanoramicScrollView.hidden = NO;
NSLog(#"collectionView.hidden = %hhd", myCollectionView.hidden);
} else if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait) {
myPanoramicScrollView.hidden = YES;
[myCollectionView setHidden:NO];
NSLog(#"collectionView.hidden = %hhd", myCollectionView.hidden);
}
}
I do some custom layout including an animation in willAnimateRotationToInterfaceOrientation:duration:
The problem I have is that if the device changes from landscapeLeft to landscapeRight the interface should rotate but the layout code, especially the animation should not be run. How can I detect that it is changing from one landscape to another? self.interfaceOrientation as well as [[UIApplication sharedApplication] statusBarOrientation] don't return valid results, they seem to think the device is already rotated. As a result the following does not work.
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) {...}
You can check the device orientation and then set a flag as to whether you are in left orientation or right orientation. Then when your device switches you can catch it and handle it however you want.
To determine orientation use:
if([UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft)
{
//set Flag for left
}
else if([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)
{
//set Flag for right
}
You can also catch a notification when the device is rotating using:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(detectOrientation) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
And then write a method for detectOrientation like so:
-(void) detectOrientation
{
if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft)
{
//Set up left
} else if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight)
{
//Set up Right
} else if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown)
{
//It's portrait time!
}
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft || [[UIDevice currentDevice] orientation ]== UIDeviceOrientationLandscapeRight)
{
NSLog(#"Lanscapse");
}
if([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown )
{
NSLog(#"UIDeviceOrientationPortrait");
}
}
It seems that the only solution is to cache the last orientation change. By the time willAnimateRotationToInterfaceOrientation: is called the device and interface orientations have already been updated. The solution is to record the destination orientation at the end of each change so that this value can be queried when the orientation is set to change again. This is not as elegant as I was hoping (yet another property on my view controller) but seems to be the only way as far as I can tell.
Is anyone know a way to detect the orientation of the iPad programmatically when launch a app.
I'm using following mechanism.
- (void) detectDeviceInitialOrientation
{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
MyAppDelegate *appDelegate=(MyAppDelegate*)[[UIApplication sharedApplication] delegate];
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (UIDeviceOrientationIsPortrait(orientation)) {
appDelegate.orintation = PORTRAIT;
}
else if (UIDeviceOrientationIsLandscape(orientation)) {
appDelegate.orintation = LANDSCAPE;
}
}
But it fails to detect the device's orientation when it lay parallel to the floor. So, I'm looking another solution. Please help......
Have a look at the UIDeviceOrientation enum in the docs.
typedef enum {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait,
UIDeviceOrientationPortraitUpsideDown,
UIDeviceOrientationLandscapeLeft,
UIDeviceOrientationLandscapeRight,
UIDeviceOrientationFaceUp,
UIDeviceOrientationFaceDown
} UIDeviceOrientation;
Notice that this defines UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown. Unfortunately, there is no built in check for whether the device is in one of these orientations like there is for portrait or landscape. However, you can do the checking your self with a simple if statement. Something like this:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationFaceUp || orientation == UIDeviceOrientationFaceDown) {
// device is flat on the ground
}
I have the following code to fill a view according to the orientation. This always returns landscape.
- (void)setData:(BCPlaylist *)list {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown){
NSLog(#"portrait");
[self setPlaylist:list];
[self renderPlaylist];
[activity stopAnimating];
}else{
NSLog(#"landscape");
[self setPlaylist:list];
[self renderPlaylistOne];
[activity stopAnimating];
}
}
I change views correctly in - (void)animateRotation:(UIInterfaceOrientation)interfaceOrientation
duration:(NSTimeInterval)duration {
But that doesn't work when already in landscape or portrait when changing a playlist.
Instead of using [[UIDevice currentDevice] orientation] for checking orientation use statusbar orientation to get the exact orientation.
UIDeviceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
To avoid a warning, use :
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
You'd always get landscape, because that's your default (from your if statement). If you walk through the debugger with a breakpoint there, you'll see that the orientation reported is that of Unknown.
In fact, your code is fine, but this is a limitation of the simulator. If you take the same code, using Device orientation and not Interface orientation, you'll get actual values if you use it on the physical device, which can be driven by the if-statement you have.