iOS 5 AirPlay screen not drawing correctly in landscape - ios

I have a simple app that is targeting iPad and iOS 5+ only. The app is landscape-only. Now, I want to take advantage of AirPlay mirroring on the iPad 2.
I have followed all of Apple's examples/documentation I can find and can't get past this drawing problem. The secondary display doesn't draw in the correct orientation.
Below is the output I am seeing from a small test app. The blue box is just a simple UIView that should be taking up the entire screen, but is not. It seems to be drawing correctly in landscape, but the view is rotated 90 degrees. Notice how the blue extends past the margin on the bottom of the TV:
I think I need to somehow force the ViewController of the external window to correctly draw in landscape, but I don't know the proper way to do this. Any ideas?
Below are the relevant pieces code:
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(screenDidConnect:)
name:UIScreenDidConnectNotification
object:nil];
[self initScreens];
return YES;
}
- (void)screenDidConnect:(NSNotification *)note
{
[self initScreens];
}
- (void)initScreens
{
if ([[UIScreen screens] count] > 1)
{
UIScreen *screen = [[UIScreen screens] lastObject];
if (!self.secondaryWindow)
{
self.secondaryWindow = [[UIWindow alloc] initWithFrame:screen.bounds];
self.secondaryWindow.screen = screen;
self.secondaryWindow.hidden = NO;
}
if (!self.secondaryViewController)
{
self.secondaryViewController = [[CCKSecondaryViewController alloc] init];
}
self.secondaryWindow.rootViewController = self.secondaryViewController;
}
}
CCKSecondaryViewController.m: This is the rootViewController of the external window
- (void)loadView
{
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
view.backgroundColor = [UIColor blueColor];
self.view = view;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = #"Secondary Screen";
[label sizeToFit];
[self.view addSubview:label];
label.center = self.view.center;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
You can find the sample app here:
http://dl.dropbox.com/u/360556/AirplayTest.zip

It's displaying in portrait on the connected screen. Having your shouldAutorotateToInterfaceOrientation method always return NO should sort it out for you.

Related

UIInterfaceOrientation not yet updated when UIDeviceOrientationDidChangeNotification caught in UIView

I'm working on an open source library to provide an iMessage-like text input view - MessageComposerView (relevant to question). The view will stick to the keyboard like an inputAccessoryView and grow with text, but won't disappear when the keyboard does.
I have recently run into a maddening issue when rotating this custom UIView that only appears if the view has been instantiated via initWithFrame. Basically at the point when a UIDeviceOrientationDidChangeNotification notification has been caught if the view has been instantiated via initWithFrame, the UIInterfaceOrientation and frame have NOT yet been updated. Here are both ways instantiating.
loadNibNamed:
self.messageComposerView = [[NSBundle mainBundle] loadNibNamed:#"MessageComposerView" owner:nil options:nil][0];
self.messageComposerView.frame = CGRectMake(0,
self.view.frame.size.height - self.messageComposerView.frame.size.height,
self.messageComposerView.frame.size.width,
self.messageComposerView.frame.size.height);
initWithFrame:
self.messageComposerView = [[MessageComposerView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height-54, 320, 54)];
When init via loadNibNamed and rotated to landscape, upon receiving a UIDeviceOrientationDidChangeNotification notification:
UIDeviceOrientation = [[notification object] orientation] = UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientation = [UIApplication sharedApplication].statusBarOrientation = UIDeviceOrientationLandscapeRight
self.frame.size = (width=480, height=90)
Important to note here is that both the interface and device orientations are landscape and the width has already been changed to landscape width (testing on iPhone 6.1 simulator). Now performing the same test but using initWithFrame:
UIDeviceOrientation = [[notification object] orientation] = UIDeviceOrientationLandscapeRight
UIInterfaceOrientation = [UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait
self.frame.size = (width=320, height=54)
This time notice that the interface orientation is still portrait and that the frame width has NOT changed to landscape width. If I set a breakpoint in the setFrame:(CGRect)frame method I can see that the frame is set to the Landscape frame AFTER the UIDeviceOrientationDidChangeNotification has been caught and handled.
Both init methods have almost identical setup:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.frame = frame;
self.sendButton = [[UIButton alloc] initWithFrame:CGRectZero];
self.messageTextView = [[UITextView alloc] initWithFrame:CGRectZero];
[self setup];
[self addSubview:self.sendButton];
[self addSubview:self.messageTextView];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self setup];
}
With setup doing necessary view tweaks:
- (void)setup {
self.backgroundColor = [UIColor lightGrayColor];
self.autoresizesSubviews = YES;
self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth;
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = NO;
CGRect sendButtonFrame = self.bounds;
sendButtonFrame.size.width = 50;
sendButtonFrame.size.height = 34;
sendButtonFrame.origin.x = self.frame.size.width - kComposerBackgroundRightPadding - sendButtonFrame.size.width;
sendButtonFrame.origin.y = kComposerBackgroundRightPadding;
self.sendButton.frame = sendButtonFrame;
self.sendButton.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin;
self.sendButton.layer.cornerRadius = 5;
[self.sendButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.sendButton setTitleColor:[UIColor colorWithRed:210/255.0 green:210/255.0 blue:210/255.0 alpha:1.0] forState:UIControlStateHighlighted];
[self.sendButton setTitleColor:[UIColor grayColor] forState:UIControlStateSelected];
[self.sendButton setBackgroundColor:[UIColor orangeColor]];
[self.sendButton setTitle:#"Send" forState:UIControlStateNormal];
self.sendButton.titleLabel.font = [UIFont boldSystemFontOfSize:14];
CGRect messageTextViewFrame = self.bounds;
messageTextViewFrame.origin.x = kComposerBackgroundLeftPadding;
messageTextViewFrame.origin.y = kComposerBackgroundTopPadding;
messageTextViewFrame.size.width = self.frame.size.width - kComposerBackgroundLeftPadding - kComposerTextViewButtonBetweenPadding - sendButtonFrame.size.width - kComposerBackgroundRightPadding;
messageTextViewFrame.size.height = 34;
self.messageTextView.frame = messageTextViewFrame;
self.messageTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
self.messageTextView.showsHorizontalScrollIndicator = NO;
self.messageTextView.layer.cornerRadius = 5;
self.messageTextView.font = [UIFont systemFontOfSize:14];
self.messageTextView.delegate = self;
[self addNotifications];
[self resizeTextViewForText:#"" animated:NO];
}
So my question is, why does my custom UIView init via initWithFrame still have the portrait frame and interface orientation and the time when the UIDeviceOrientationDidChangeNotification is received. I use this notification to do my frame adjustment and need the width to have already been updated to the landscape width.
I'm hoping there is some kind of autorotation property that I'm missing in the programmatical setup that is buried somewhere in the XIB but just can't find it. I've gone through probably a dozen UIDeviceOrientationDidChangeNotification stackoverflow posts without finding a solution.
MessageComposerView looks neat, thanks for sharing.
Instead of listening to the UIDeviceOrientationDidChangeNotification, you should implement layoutSubviews. It is called in response to rotation and anything else the system would need the UIView to reposition its subviews (https://stackoverflow.com/a/5330162/1181421). There you can position your subviews relative to the UIView's frame, and you can be sure that the frame will be correct.

iOS 7 status bar like iOS 6

I have an app with a support landscape and portrait mode. And I need the same behavior status bar like on iOS 6. What is the simplest way to do this?
I've tried the solutions in Stack Overflow question iOS 7 status bar back to iOS 6 style?, but it doesn't work. My subview depend on the view size, and my view doesn't stretch correctly. I don't want to update my XIB files; I simply want to add something that helps me. I don't know what it may be (hack or prayers).
You can try writing this in your ViewWillappear or DidAppear. Here we are shifting the view frame 20 pixels down.
CGRect frame = self.view.frame;
frame.origin.y = 20;
if (self.view.frame.size.height == 1024 ||
self.view.frame.size.height == 768)
{
frame.size.height -= 20;
}
self.view.frame = frame;
This will work, but however this is not a very good idea. You can also change the text colour of the status bar to light or dark depending on your app background by calling the following method if it helps.
-(UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent; // For light status bar
return UIStatusBarStyleDefault // For Dark status bar
}
If you are on Xcode 5 and you are installing in iOS 7 then sorry, this will not happen (as far as I know).
If you want to see the status bar on iOS 7 like iOS 6 than open your project in Xcode 4.x.x and install in iOS 7. One problem with this approach I found is that sometimes Xcode 4.x.x doesn't recognise an iOS 7 device.
But if your Xcode 4.x.x can show your iOS 7 device then it will work.
The .api generated from Xcode 4.x.x will work in both iOS 6 and iOS 7, but you will not get extra space (of the status bar) on iOS 7 and the new look of keyboard, picker, switch, etc. But yes, you will get the new UIAlertView (I don't know why this is new and the other controls are old.)
I hope we will soon get a better solution in Xcode 5 for this.
UPDATE:
I found the way to run the app from Xcode 5 as Xcode 4. This is just matter of the base SDK.
If you want to built as Xcode 4 (iOS 6 SDK) from Xcode 5 then do the following.
Close Xcode 4 and 5.
In Xcode 4 Go to
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs
Here you will find iPhoneOS6.1.sdk. Copy this folder. And now go in Xcode 5 on the same path. In Xcode 5, you will find iPhoneOS7.0.sdk. Paste iPhoneOS6.1.sdk with it.
Now close the Finder and launch Xcode 5. Go to project target setting -> Build Setting and find Base SDK. Select iOS 6.1 as Base SDK. This will also work for 6.0. You just need to find iPhoneOS6.0.sdk.
Now you will see the device name twice in the run dropdown box. One for SDK 7.0 and one for SDK 6.1. So now you can run both ways with iOS 6 SDK and iOS 7 SDK.
I hope this will help someone.
I recently had to solve a similar problem, and I approached it in a slightly different way...
The approach was to use an extra view controller that acted as a container view controller for what was originally my rootViewController. First, i set up a container like this:
_containerView = [[UIView alloc] initWithFrame:[self containerFrame]];
_containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_containerView.clipsToBounds = YES;
[self.view addSubview:_containerView];
[self.view setBackgroundColor:[UIColor blackColor]];
[UIApplication.sharedApplication setStatusBarStyle:UIStatusBarStyleLightContent animated:NO];
where the containerFrame was defined like this:
- (CGRect)containerFrame
{
if ([MyUtilityClass isSevenOrHigher])
{
CGFloat statusBarHeight = [MyUtility statusBarHeight]; //20.0f
return CGRectMake(0, statusBarHeight, self.view.bounds.size.width, self.view.bounds.size.height - statusBarHeight);
}
return self.view.bounds;
}
Finally, I added what was originally my rootViewController as a childViewController of the new one:
//Add the ChildViewController
self.childController.view.frame = self.containerView.bounds;
self.childController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self addChildViewController:self.childController];
[self.containerView addSubview:self.childController.view];
[self.childController didMoveToParentViewController:self];
Things to note:
- Modal view controllers will still be presented in the iOS7 style, so I still have to account for that somehow.
Hope this helps someone!
This Guide helps me.
http://www.doubleencore.com/2013/09/developers-guide-to-the-ios-7-status-bar/
The most robust way to handle the 20 point size difference is Auto Layout.
If you aren’t using Auto Layout, Interface Builder provides you with tools to handle the screen size difference between iOS 7 and the older versions. When Auto Layout is turned off, you will notice an area in the sizing tab of the utility area (right pane) of Interface Builder that allows you to set iOS 6/7 Deltas.
1) It's a hack, but it works!
Use it if you doesn't use UIAlertView or KGStatusBar!!!
#import <objc/runtime.h>
#interface UIScreen (I_love_ios_7)
- (CGRect)bounds2;
- (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation;
#end
#implementation UIScreen (I_love_ios_7)
- (CGRect)bounds2
{
return [self boundsForOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
- (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation
{
CGRect resultFrame = [self bounds2];
if(UIInterfaceOrientationIsLandscape(orientation))
resultFrame.size.width -= 20;
else
resultFrame.size.height -= 20;
return resultFrame;
}
#end
void Swizzle(Class c, SEL orig, SEL new)
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
Swizzle([UIScreen class], #selector(bounds2), #selector(bounds));
[application setStatusBarStyle:UIStatusBarStyleLightContent];
self.window.clipsToBounds =YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidChangeStatusBarOrientation:)
name:UIApplicationWillChangeStatusBarOrientationNotification
object:nil];
NSDictionary* userInfo = #{UIApplicationStatusBarOrientationUserInfoKey : #([[UIApplication sharedApplication] statusBarOrientation])};
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillChangeStatusBarOrientationNotification
object:nil
userInfo:userInfo];
}
return YES;
}
- (void)applicationDidChangeStatusBarOrientation:(NSNotification *)notification
{
UIInterfaceOrientation orientation = [[notification.userInfo objectForKey: UIApplicationStatusBarOrientationUserInfoKey] intValue];
CGSize size = [[UIScreen mainScreen] boundsForOrientation:orientation].size;
int w = size.width;
int h = size.height;
float statusHeight = 20.0;
switch(orientation){
case UIInterfaceOrientationPortrait:
self.window.frame = CGRectMake(0,statusHeight,w,h);
break;
case UIInterfaceOrientationPortraitUpsideDown:
self.window.frame = CGRectMake(0,0,w,h);
break;
case UIInterfaceOrientationLandscapeLeft:
self.window.frame = CGRectMake(statusHeight,0,w,h);
break;
case UIInterfaceOrientationLandscapeRight:
self.window.frame = CGRectMake(0,0,w,h);
break;
}
}
#end
2) Create category, and always use contentView instead of view
#interface UIViewController(iOS7_Fix)
#property (nonatomic, readonly) UIView* contentView;
- (void)updateViewIfIOS_7;
#end
#implementation UIViewController(iOS7_Fix)
static char defaultHashKey;
- (UIView *)contentView
{
return objc_getAssociatedObject(self, &defaultHashKey)?: self.view;
}
- (void)setContentView:(UIView *)val
{
objc_setAssociatedObject(self, &defaultHashKey, val, OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
}
- (void)updateViewIfIOS_7
{
if([[[UIDevice currentDevice] systemVersion] floatValue] < 7 || objc_getAssociatedObject(self, &defaultHashKey))
return;
UIView* exchangeView = [[UIView alloc] initWithFrame:self.view.bounds];
exchangeView.autoresizingMask = self.view.autoresizingMask;
exchangeView.backgroundColor = [UIColor blackColor];
UIView* view = self.view;
if(self.view.superview){
[view.superview addSubview:exchangeView];
[view removeFromSuperview];
}
[exchangeView addSubview:view];
self.view = exchangeView;
CGRect frame = self.view.bounds;
frame.origin.y += 20.0;
frame.size.height -= 20.0;
view.frame = frame;
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self setContentView:view];
}
In every UIViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
[self updateViewIfIOS_7];
UILabel* lab = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 130, 30)];
lab.backgroundColor = [UIColor yellowColor];
[self.contentView addSubview:lab];
//...
}

Launching app in Landscape Mode

I have an app that I want to only work with in Landscape.
For the first time ever, I'm foregoing IB and trying to set up all my views programmatically, so I'm creating a view and adding a bunch of subviews in loadView method:
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// Create a GMSCameraPosition that tells the map to display the
// coordinate -33.86,151.20 at zoom level 6.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.86
longitude:151.20
zoom:6];
self.mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];
self.mapView.myLocationEnabled = YES;
self.mapView.delegate = self;
self.mapView.mapType = kGMSTypeHybrid;
self.mapView.frame = self.view.frame;
[self.view addSubview:self.mapView];
// add the toolbar
UIToolbar* toolbar = [[UIToolbar alloc] init];
toolbar.frame = CGRectMake(0, self.view.frame.size.height - 44, self.view.frame.size.width, 44);
toolbar.barStyle = UIBarStyleDefault;
NSMutableArray* items = [NSMutableArray array];
[items addObject:[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"location-arrow.png"]
style:UIBarButtonItemStyleBordered
target:self
action:#selector(locateMe:)]];
[items addObject:[[UIBarButtonItem alloc] initWithTitle:#"Tools"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(toolsButtonTapped:)]];
[toolbar setItems:items];
[self.view addSubview:toolbar];
In my project settings, I have disabled both portrait orientations. I also have this in my root view controller:
// Enforce Landscape Orientation
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeRight;
}
My problem is that the simulator starts in landscape mode, but all of the views are sized for portrait - so the bottom chunk of my views are below the screen and the right side of my screen is a big empty region.
I tried fixing this by switching the width and height of the application frame in the first line, but then that leaves some empty vertical room on the left edge of the screen for the status bar.
So, what's the correct way of doing what I'm trying to do?
Instead of using [[UIScreen mainScreen] applicationFrame]
try using [[[[[self view] window] rootViewController] view] bounds]
The bounds will represent the width and height correctly in Landscape orientation, because the bounds will take into account the transform (rotation) that has been applied, while the frame will not.
To see what I mean, set a breakpoint, and in the debugger print out the description of the top level view lldb> po [[[[self view] window] rootViewController] view]
You'll see that the view has a rotation transform and that its frame does not represent the dimensions of the screen in landscape, but represents the dimensions in portrait!
The long way to calculate the correct applicationFrame would be
BOOL iOS7 = NO;
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:#"7.0" options:NSNumericSearch] != NSOrderedAscending)
iOS7 = YES;
CGRect theFrame;
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGRect screenBounds = [[UIScreen mainScreen] bounds];
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
theFrame.origin = CGPointZero;
theFrame.size.width = screenBounds.size.height;
theFrame.size.height = screenBounds.size.width;
if (iOS7 == NO) {
// statusBarFrame will be CGRectZero if not visible, so this is safe
theFrame.size.height -= statusBarFrame.size.width; // because we're in landscape orientation
}
}
else {
theFrame = screenBounds;
if (iOS7 == NO) {
// statusBarFrame will be CGRectZero if not visible, so this is safe
theFrame.size.height -= statusBarFrame.size.height; // because we're in portrait orientation
}
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (UIInterfaceOrientationIsLandscape( interfaceOrientation))
{
return YES;
}
return NO;
}
Put this code Appdelegate .M.....
Put this in current viewcontroller
// ios 5
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (UIInterfaceOrientationIsLandscape( interfaceOrientation)) {
return YES;
}
return NO;
}
// ios6
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}

IOS - External (hdmi) output fills only half the screen except when coding view manually

So, as the title says, I have an hdmi out on the iPad and an observer registered for screen connections, upon connection the user chooses the res and a view is outputted.
However, if I load a view from a nib, or even from a programatic view controller, the ipad shows a landscape view in portrait (yes, both situations are set to landscape).
I.e.
ExternalViewController *ex = [[ExternalViewController alloc] init];
[externalWindow setRootViewController:ex];
does this:
If I create the view itself programatically. like so:
UIView *test = [[UIView alloc] initWithFrame:[externalScreen applicationFrame]];
[test setBackgroundColor:[UIColor whiteColor]];
UILabel *msgLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 40, 100, 30)];
msgLabel.text = #"External!";
[test addSubview:msgLabel];
It runs like some form of magical dream:
However I want the viewcontroller to load (and work!) so, StackOverflow, I ask you. has anyone come across this before?
EDIT: It does go without saying that common sensical answers do not get a bounty, I am after a fix, not a workaround. With my limited brain, all I can think to do is create a method that creates a view based on it's inputs and adds that as a subview of the external monitor, it is clear that this is a hack solution so a fix is appreciated! Thanks!
EDIT:
-(id)initWithFrame:(CGRect)_rect
{
rect = _rect;
if (self = [super init])
{
externalView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"105.png"]];
externalView.alpha = 0.0;
[externalView setFrame:rect];
[externalView setBackgroundColor:[UIColor yellowColor]];
self.view = [[UIView alloc] initWithFrame:rect];
self.view.backgroundColor = [UIColor whiteColor];
return self;
}
}
- (void)loadView
{
self.view = [[UIView alloc] initWithFrame:rect];
self.view.backgroundColor = [UIColor blackColor];
[self.view addSubview:externalView];
}
As requested, this is how I am loading the viewcontroller, initialising with the size of the external screen. Thanks
Just return NO in - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation in your UIViewController subclass.
// ugly, don't use this in real code
if ([UIScreen screens].count == 1) return; // just one screen, eww.
// getting the secondary screen
UIScreen *screen = [[UIScreen screens] objectAtIndex:1];
__block UIScreenMode *highestWidthMode = NULL;
[screen.availableModes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIScreenMode *currentModeInLoop = obj;
if (!highestWidthMode || currentModeInLoop.size.width > highestWidthMode.size.width)
highestWidthMode = currentModeInLoop;
}];
// setting to the highest resolution available
screen.currentMode = highestWidthMode;
NSLog(#"screen.currentMode = %#", screen.currentMode);
screen.overscanCompensation = UIScreenOverscanCompensationScale;
// initializing screen
secondWindow = [[UIWindow alloc] initWithFrame:[screen bounds]];
[secondWindow setScreen:screen];
// other view is a UIViewController, just remember to return NO in - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation or the iOS will rotate the frame.
UIViewController *vc = [[OtherView alloc] initWithNibName:#"OtherView" bundle:nil];
secondWindow.rootViewController = vc;
[secondWindow makeKeyAndVisible];

How should I get my inputAccessoryView to resize when rotating device?

I'm attaching a UIToolbar to my UITextView as its inputAccessoryView in order to add a button to dismiss the keyboard. This works great and it looks correct when the device is in portrait mode. But I'm having trouble figuring out how to resize the toolbar to the lower height used for toolbars when the device is in landscape mode.
I'm adding the toolbar in my text view's delegate's -textViewShouldBeginEditing: method:
if (!textView.inputAccessoryView) {
UIToolbar *keyboardBar = [[UIToolbar alloc] init];
keyboardBar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;
keyboardBar.barStyle = UIBarStyleBlackTranslucent;
UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissKeyboard:)];
[keyboardBar setItems:[NSArray arrayWithObjects:spaceItem, doneButton, nil]];
[spaceItem release];
[doneButton release];
[keyboardBar sizeToFit];
textView.inputAccessoryView = keyboardBar;
[keyboardBar release];
}
I get strange behavior from this code in landscape mode, though. If I start editing in landscape mode, the toolbar has the landscape height but the Done button is drawn half off the screen. If I then rotate to Portrait mode, the Done button is drawn in the correct location, and it remains in the correct location when I rotate back to landscape mode.
If I start editing in portrait mode, the toolbar is drawn with portrait height, but the Done button is drawn in the correct location. If I then rotate to landscape mode, the toolbar remains portrait height, but the Done button is still drawn in the correct position at least.
Any suggestions for how to get this to resize when the device rotates? I'm really hoping there's a more automatic way than manually plugging in the height magic numbers in one of the view controller's rotation events.
That's a tough problem. I've solved this in the past by adjusting the frame when the accessory view gets laid out after rotating. Try something like this:
#interface RotatingTextInputToolbar : UIToolbar
#end
#implementation RotatingTextInputToolbar
- (void) layoutSubviews
{
[super layoutSubviews];
CGRect origFrame = self.frame;
[self sizeToFit];
CGRect newFrame = self.frame;
newFrame.origin.y += origFrame.size.height - newFrame.size.height;
self.frame = newFrame;
}
#end
And using the the RotatingTextInputToolbar instead of the UIToolbar in your code, above.
Voila:
#interface AccessoryToolbar : UIToolbar #end
#implementation AccessoryToolbar
-(id)init
{
if (self = [super init])
{
[self updateSize];
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(orientationDidChange:) name:UIApplicationDidChangeStatusBarOrientationNotification object:NULL];
}
return self;
}
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
-(void)orientationDidChange:(NSNotification*)notification
{
[self updateSize];
}
-(void)updateSize
{
bool landscape = UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]);
CGSize size = UIScreen.mainScreen.bounds.size;
if (landscape != size.width > size.height)
std::swap(size.width, size.height);
if (size.height <= 320)
size.height = 32;
else
size.height = 44;
self.frame = CGRectMake(0, 0, size.width, size.height);
}
#end

Resources