In my view controller, I implement two methods for controlling interface orientation:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotate
{
return YES;
}
In the supportedInterfaceOrientations method, I return UIInterfaceOrientationMaskPortrait but at that time I realized that the shouldAutorotate method is not being called.
But I change to return UIInterfaceOrientationPortrait in the supportedInterfaceOrientations method. The shouldAutorotate method is being called, but there is an error that mentions in following:
UIApplicationInvalidInterfaceOrientation, reason: 'Supported orientations has no common orientation with the application, and shouldAutorotate is returning YES'
By the way, I select all orientations in the supported interface orientations.
EDITED
i use viewController and embed with navigationController.
here is AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate,UINavigationControllerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (readonly, strong, nonatomic) UINavigationController *navController;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
#end
in didFinishLaunchingWithOptions method under AppDelegate.m
navController = (UINavigationController *)self.window.rootViewController;
IPad_HomeViewController *rootVC=(IPad_HomeViewController *)navController.topViewController;
rootVC.managedObjectContext = self.managedObjectContext;
return YES;
in my IPad_HomeViewController,
#interface IPad_HomeViewController : UIViewController <UINavigationControllerDelegate>
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#end
- (BOOL) shouldAutorotate {
return YES;
}
// for landscape
- (NSInteger) supportedInterfaceOrientations {
return (1 << UIInterfaceOrientationLandscapeLeft) |
(1 << UIInterfaceOrientationLandscapeRight);
}
As noted above, check out the mask values for specific orientations : UIInterfaceOrientationMask values.UIInterfaceOrientationMask
Two points:
Your error message makes complete sense because it makes no sense to use UIInterfaceOrientationPortrait as a return value from supportedInterfaceOrientations, which is returning a bit mask. Use one of the UIInterfaceOrientationMask values.
You seem to be concerned that if you use the proper UIInterfaceOrientationMaskPortrait, that iOS doesn't appear to call shouldAutorotate. It may only call shouldAutorotate if, having considered the physical device's physical orientation and the app's current orientation against the supportedInterfaceOrientations that a rotation might be needed. Why should it check shouldAutorotate if it concludes that the device is in an acceptable orientation already?
See supportedInterfaceOrientations and Handling View Rotations for more information.
I know this sounds pretty elementary, but I was wracking my brain to figure out orientation issues while testing on my iPhone - I had the physical auto lock mode in Portrait mode - so nothing I changed programmatically mattered - thought this should be troubleshooting step number 1!
In order to change the orientation setting, select menu Targets->Summary-> Supported Device Orientations and change as following.
If the buttons for the orientation are dark then that means you have selected it as one of the orientation.
rather than using an integer to declare the orientation why don't you use a bool and plug in a if statement in there to detect whatever orientation you want. here is a example code that i hope would help you:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationPortrait) {
return YES;
}
if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
return YES;
}
return NO;
}
you can add all of the orientations in the if statement and it should work just fine. adrian
Edit:
and if you want to have an option for ios 6 the below code should work just fine for you.
- (BOOL) shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
}
you can change just about all the supported orientations with this in ios 6. happy coding
EDIT 1:
to rotate only certain viewcontrollers in a certain way, just use the ios 6 code that i posted above in all viewcontrollers. here are the steps:
in the project level where all four internfaceorientations are located, go ahean and turn everything off so app would go to default.
implement the ios 6 code that i supplied in all viewcontrollers.
rather than yes declare no in shouldautorotate method.
in the second method, plug in any type of orientation you want.
this should do the trick for you. happy coding
Related
Hi i am now problem with orientation of viewcontroller. the following is my .h file.
#interface IPad_HomeViewController : UIViewController <UINavigationControllerDelegate>{
UIAlertView *alertWithYesNoButtons;
}
#property (weak, nonatomic) IBOutlet UILabel *lblStatus;
#end
i implement the following method in .m file.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return NO;
}
then i made a breakpoint. i realized shouldAutorotateToInterfaceOrientation is totally not being called. don't know why it is not being called.
pls advise me.
thanks
I think it is because your UINavigationController handles the call. Implement the following to send it back to the viewController.
- (BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
// pre-iOS 6 support
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
Rotation support changed a lot between iOS 5 and iOS 6.
Go to your project's target summary page and check "iPhone/Ipod Deployment Info" and there "Supported Interface Orientations". Have you enabled device rotation?
Rotation was working JUST FINE with iOS 5 and now it doesn't work at all. I had it set so that ALL of my views stayed Portrait exception when on Tab 1 when a certain view was open, then users could rotate and it would show a coverflow-style view.
My setup is that I create my tabbar at runtime in the AppDelegate. I then set it as the main root view:
self.window.rootViewController = self.tabBarController;
But ALL of my views, on all tabs, now rotate left or right no matter what. And I've tried adding the new code (from multiple examples in the forums) to no avail.... I breakpoint everything and NO rotation code ever gets called when I rotate my phone.
Each TabController has within it a NavigationController and then within that has my main views with all of my UI.
Any ideas or pointers on how to do rotation correctly in iOS 6? Very frustrating because this is the final problem I need to fix before I can ship.
This will get you up and running. Ultimately you really should subclass these UIKit classes instead of using categories, but unfortunately that will not work for third party libraries which are not yet fixed for iOS 6. These categories should work for everything without requiring you to muck about in other people's code.
I have yet to see any solution for the UITabBarController or UINavigationController issues that do not involve subclassing (or writing a category). I wish one existed, though.
Make sure you import the three .h files (or one if you choose to add them all to a single file) at the top of your Prefix.pch file. You must make sure this code is loaded ASAP!
UITabBarController+LegacyRotation.h
#import <UIKit/UIKit.h>
#interface UITabBarController (LegacyRotation)
#end
UITabBarController+LegacyRotation.m
#import "UITabBarController+LegacyRotation.h"
#implementation UITabBarController (LegacyRotation)
- (NSUInteger)supportedInterfaceOrientations
{
return [self.selectedViewController supportedInterfaceOrientations];
}
- (BOOL)shouldAutorotate
{
return [self.selectedViewController shouldAutorotate];
}
#end
UINavigationController+LegacyRotation.h
#import <UIKit/UIKit.h>
#interface UINavigationController (LegacyRotation)
#end
UINavigationController+LegacyRotation.m
#import "UINavigationController+LegacyRotation.h"
#implementation UINavigationController (LegacyRotation)
- (NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
- (BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
#end
UIViewController+LegacyRotation.h
#import <UIKit/UIKit.h>
#interface UIViewController (LegacyRotation)
#end
UIViewController+LegacyRotation.m
#import "UIViewController+LegacyRotation.h"
#implementation UIViewController (LegacyRotation)
- (NSUInteger)supportedInterfaceOrientations
{
NSUInteger ret = 0;
if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortrait]) {
ret |= UIInterfaceOrientationMaskPortrait;
}
if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortraitUpsideDown]) {
ret |= UIInterfaceOrientationMaskPortraitUpsideDown;
}
if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeLeft]) {
ret |= UIInterfaceOrientationMaskLandscapeLeft;
}
if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeRight]) {
ret |= UIInterfaceOrientationMaskLandscapeRight;
}
return ret;
}
- (BOOL)shouldAutorotate
{
return YES;
}
#end
I just needed to add the following method in the appDelegate , in order for the rotation to
work on ios 6.
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:
(UIWindow *)window{
return UIInterfaceOrientationMaskAll;
}
Where as for ios 4 & 5 we still have to use :
(BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation in the specific view controller
I am a novice to IOS programming hence struggling a bit with the below problem. I will do my best to describe the problem and any help is greatly appreciated.
I have the following created:
AboutViewController (.h, .m and .xib ) which has two subviews called - mainView, infoView. mainView and infoView interfaces are created in the XIB file.
TestView ( View to deal with initiating, toggling between mainView and infoView )
TestView.h is as follows:
#interface TestView : UIView {
IBOutlet UIView *mainView;
IBOutlet UIView *infoView;
UILabel *lbltitle;
UIImageView *imgIcon;
IBOutlet UITextView *txtInfo1;
IBOutlet UITextView *txtInfo2;
IBOutlet UITextView *txtInfo3;
}
#property (nonatomic, retain) IBOutlet UILabel *lbltitle;
#property (nonatomic, retain) IBOutlet UIImageView *imgIcon;
TestView.m is as follows:
#import "TestView.h"
#import <QuartzCore/QuartzCore.h>
#implementation TestView
#synthesize lbltitle, imgIcon;
-(void)awakeFromNib
{
[self addSubview:mainView];
}
In portrait mode the views are working great but when it comes to landscape mode, the views are all kinda screwed up. I tried to use the XIB but I guess you can only do so much so I decided to do this programmatically.
In the AboutViewController.m, I am trying to override willAnimateRotationToInterfaceOrientation method and place the objects based on the orientation. When I put breakpoints, I can see that code is being called except it is not translating into the UI i.e., no change in the UI. What am I doing wrong ? Should I be approaching this in a different way. Any suggestions or guidance is greatly appreciated.
- (void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
TestView *t = [[TestView alloc]init];
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
[t.imgIcon setFrame:CGRectMake(10,74,165,190)];
[t.lbltitle setFrame:CGRectMake(20, 10, 387, 21)];
}
else
{
[t.imgIcon setFrame:CGRectMake(10,74,165,190)];
[t.lbltitle setFrame:CGRectMake(189, 10, 387, 21)];
}
}
Well the particular problem is you manipulate a view that you just created using
TestView *t = [[TestView alloc]init];
You don't display the view anywhere, so it sits in memory. You can apply all changes you want, but to be able to see them, you must first display the view:
Find the appropriate parent and do:
[parentview addSubview:t];
In more general terms you shouldn't be creating new views in the rotation handling code.
Let me start off by saying that this is my first real Cocoa app. It's a simple app that pretty much displays my website in a borderless window. The way I'm currently creating a borderless window is using the following:
- (void) awakeFromNib {
[window setStyleMask:NSBorderlessWindowMask];
[window setAcceptsMouseMovedEvents:YES];
[window setMovableByWindowBackground:YES];
[window setLevel:NSNormalWindowLevel];
}
The problem with this is that as a result, the WebView within the window does not pass mouse over events to elements on the loaded page, nor does it provide the ability to type in text fields. I know that I'm supposed to create a custom window instead and move the contentView into it but I'm too new to Objective-C to figure out how.
I've also tried declaring all of these with no luck:
#implementation specikAppDelegate
#synthesize window;
#synthesize webView;
- (BOOL) canBecomeKeyWindow { return YES; }
- (BOOL) canBecomeMainWindow { return YES; }
- (BOOL) acceptsFirstResponder { return YES; }
- (BOOL) becomeFirstResponder { return YES; }
- (BOOL) resignFirstResponder { return YES; }
...
#end
Additionally, I'd like to be able to move the window by clicking and dragging it anywhere but that's a side thought. I've searched extensively online, and cannot find a solution to this.
Contents of my .h file (just in case):
#interface specikAppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSWindow *window;
IBOutlet WebView *webView;
}
#property (assign) IBOutlet NSWindow *window;
#property (nonatomic, retain) IBOutlet WebView *webView;
- (IBAction)openAboutPanel:(id)sender;
#end
Any help would be appreciated, and like I said, I'm super new to the world of Objective-C and Cocoa, but I do come from a PHP development background.
As explained in this answer, windows without title or resize bar (including borderless windows) cannot become key windows.
You were right about overriding -canBecomeKeyWindow, but you’ve missed the correct place. You shouldn’t do it in your application delegate. You need to create an NSWindow subclass and then override that method.
This sample code of apple should give you the information you need, its really easy to change the way it works and change it into your own drawn NSWindow ( without a border :D )
http://developer.apple.com/library/mac/#samplecode/RoundTransparentWindow/Introduction/Intro.html
I'm having a lot of trouble with what seems like a very simple thing. I cannot update a UILabel programmatically from a Navigation-based iOS App. I don't want to use a button as this label is designed to report the status of an external system, and should update on launch. There is no need to make the user go though the extra step on touching the button if I don't have to.
The following is a somewhat exhaustive list of the steps I've taken. I'm sorry if some of this seems unnecessary, but in my experience even the smallest forgotten step can be the cause of the issue.
From a fresh Navigation-based App in Xcode here are the steps I'm taking:
Replace UITableView with a generic UIView class
Re-wire File's Owner's view outlet to the new UIView
Add a UILabel to the center of the UIView, make the text centered, and leave the default text.
Save and Exit Interface Builder
RootViewController.h
#import <UIKit>
#interface RootViewController : UIViewController {
UILabel *myLabel;
}
#property (nonatomic, retain) IBOutlet UILabel *myLabel;
#end
RootViewController.m
#import "RootViewController.h"
#implementation RootViewController
#synthesize myLabel;
...
Removed TableView stuff from RootViewController.m
Wire IBOutlet myLabel to the Label in RootViewController.xib
Save and Exit Interface Builder
tempNavAppAppDelegate.m
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
RootViewController *rootViewCont = navigationController.visibleViewController;
rootViewCont.myLabel.text = #"test";
NSLog(#"Label Text: %#", rootViewCont.myLabel.text);
return YES;
}
...
Build/Run
The Label shows as "Label" not "test". And the log reports:tempNavApp[94186:207] Label Text: (null)
I've tried a number of different ways to get this done, but any help would be appreciated.
The Journey
After discovering that my rootViewCont.myLabel was also nil, thanks to the help of mprudhom, I decided to test and see if I could assign myLabel.text a value in RootViewController.m's - (void)viewDidLoad method.
It worked, I was able to change the text directly from the RootViewController. But while this proved my View Controller wasn't broken, it did not solve my initial desire to change the UILabel from tempNavAppAppDelegate.m.
Elliot H. then suggested that navigationController.visibleViewController wasn't actually returning a view controller. I had tested for the value of rootViewCont and it came back as a RootViewController, but Elliot's suggestion got me thinking about the app's lifecycle and when the different parts of my code was actually loaded up.
So I started printing an NSLog at each step of the launch process (application:didFinishLaunchingWithOptions:, applicationDidBecomeActive:, viewDidLoad, viewDidAppear:), and discovered to my surprise that [self.window makeKeyAndVisible]; does not mean that the view will load before application:didFinishLaunchingWithOptions: is complete.
With that knowledge in hand I knew where the problem was. The solution (or at least my solution) seems to be NSNotificationCenter. I have now registered for notifications in tempNavAppAppDelegate and I am broadcasting a notification in RootViewController's viewDidAppear: method.
The Pertinent Code
RootViewController.h:
#interface RootViewController : UIViewController {
IBOutlet UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
RootViewController.m:
#implementation RootViewController
#synthesize myLabel;
- (void)viewDidLoad {
[super viewDidLoad];
NSParameterAssert(self.myLabel);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:#"viewDidAppear" object:self];
}
tempNavAppAppDelegate.h:
#interface tempNavAppAppDelegate : NSObject {
UIWindow *window;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
- (void)viewDidAppearNotification:(id)notification;
#end
tempNavAppAppDelegate.m:
#implementation tempNavAppAppDelegate
#synthesize window;
#synthesize navigationController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(viewDidAppearNotification:) name:#"viewDidAppear" object:nil];
return YES;
}
- (void)viewDidAppearNotification:(id)notification
{
NSString *noteClass = [NSString stringWithFormat:#"%#", [[notification object] class]];
if ([noteClass isEqualToString:#"RootViewController"]) {
RootViewController *noteObject = [notification object];
noteObject.myLabel.text = #"Success!";
}
}
If this code is printing nil:
rootViewCont.myLabel.text = #"test";
NSLog(#"Label Text: %#", rootViewCont.myLabel.text);
Then almost certainly it is because rootViewCont.myLabel itself is nil. Try logging the value of rootViewCont.myLabel as well and you'll see.
Are you sure you wired up the label to your UILabel IBOutput declaration in Interface Builder? That's most commonly the problem.
I personally always assert all my expected outlets in viewDidLoad so that I catch early on when the outlets have been (accidentally or not) been decoupled in Interface Builder. E.g.:
- (void)viewDidLoad {
[super viewDidLoad];
NSParameterAssert(rootViewCont.myLabel);
}
your interface should look like this
#import <UIKit>
#interface RootViewController : UIViewController {
// IBOutlet here...
IBOutlet UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
Is visibleViewController actually returning the view controller? My guess is since application:didFinishLaunchingWithOptions: hasn't returned yet, it's possible UINavigationController hasn't properly configured that property to return yet, even though you've added the navigation controller's subview to the view hierarchy, it's probably that visibleViewController isn't valid until after viewDidAppear: is called on the view controller in question.
Try having an IBOutlet to the RootViewController directly, or create it programmatically, and then assign the label text.
Just a general reminder: If an object is nil (in this case visibleViewController would be returning nil), and you send it a message, you won't crash, because messages to nil are valid and won't do anything. When you call the myLabel accessor on the rootViewCont object, if rootViewCont is nil, myLabel will return nil always.