I'm converting my iPhone app to iPad and using
-(BOOL)splitViewController:(UISplitViewController *)svc shouldHideViewController:(UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation
{
return NO;
}
to keep the dual-pane layout at all times. Unfortunately this was introduced in iOS5. I've seen mention of doing this programatically for pre-iOS 5 but not managed to find any example code. Has anyone got any pointers please?
this area is a bit of a slippery slope because Apple has been tweaking the splitview in 5.0, 5.1 and rotation in 6.0
Not to be flip, but if you can just move your app to require 5.0, it would be the simplest way to achieve your goal.
if you want to do this your self, you would basically need to hijack the popover and re-implement in your own frame. There is a method called:
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)newpopoverController
in this method, you can grab the viewController and store it.
then sensing when in portrait using:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
, you need to grab the detail view and adjust it - making room for your new frame on the left, which you fill using the view controller you grabbed the reference from in the previous method.
its a bit of a juggling act.
If you really, really need to stay with 4.3 compatibility and you want that feature, I'd suggest you look at an open source SplitView Controller called MGSplitViewController by Matt Gemmell - you can find it here: https://github.com/mattgemmell/MGSplitViewController.
His implementation is quite nice and will not only give you that feature, but several other options related to view position on the screen.
Now here is where the slippery slope begins though, Matt hasn't updated the code for a couple of years (although there are some forks that have) and may become difficult to maintain as you move your application forward.
If nothing else, it does contain code that does exactly what you want to do, so it would be an excellent reference to help you with your project.
sorry I can't provide a complete recipe, but really, that library should help regardless if you use it or just user it as a reference.
best of luck
Related
I'm using the VFR PDF viewer library in my app, where I present it thus:
ReaderDocument *document = [ReaderDocument withDocumentFilePath:pdfFile password:nil];
ReaderViewController *vc = [[ReaderViewController alloc] initWithReaderDocument:document];
[self.navigationController pushViewController:vc animated:YES];
If I run on iOS7, everything works fine.
If I run my app on iOS8, the willRotateToInterfaceOrientation method in ReaderViewController never gets called, so when the device is rotated the document doesn't get reformatted correctly.
However, if I run the demo app that comes with the library on iOS8, the willRotateToInterfaceOrientation in ReaderViewController does get called, which leads me to believe the library is ok, and I'm doing something wrong (or neglecting to do something) in my app.
I'm rather puzzled at this behaviour. Why doesn't willRotateToInterfaceOrientation get called in my app on iOS8, but it does under the other variations? How can I try to track this down?
I finally managed to resolve my problem; here is what the issue was in case anyone else has the same problem.
My ReaderViewController was being presented from a class that had implemented viewWillTransitionToSize:withTransitionCoordinator:, so the deprecated methods on child view controllers weren't being called.
Also in that implementation it wasn't calling
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]
and so the viewWillTransitionToSize:withTransitionCoordinator: method of all child view controllers wasn't being called either.
The fix was to add a call to
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]
into the parent VC's viewWillTransitionToSize:withTransitionCoordinator: method, subclass ReaderViewController, then add a viewWillTransitionToSize:withTransitionCoordinator: to my subclass to call the appropriate methods in ReaderViewController.
Simply because willRotateToInterfaceOrientation is no more called in iOS8, it is deprecated.
If the new rotation methods are not implemented, and a project is
compiled with the iOS 8 SDK, the view controllers -will not receive
calls- to the deprecated rotation methods.
A similar question to yours can be found here
Citation of #mluisbrown :
The rotation methods are deprecated in the iOS 8 SDK. This will have
no effect at all on apps built with the iOS 7 SDK, even running in iOS
8 and probably several future versions of iOS.
I struggled a bit with some kind of a similar problem to yours. I tried following Apple recommendation to use viewWillTransitionToSize, which in my case did not solve my problem because this only gets triggered on changes from regular to compact for example an not on rotation.
viewWillTransitionToSize:withTransitionCoordinator: to make
interface-based adjustments.
Which is detailed in apple documentation
Also a video of WWDC 2014 explains this but I can't remember which video it was. Perhaps the one on What's new in Cocoa touch or the one on View Controller Advancements in iOS 8.
EDIT
Note that in my case viewWillTransitionToSize, as explained, was not called because I wasn't changing from regular to compact so there was no size transition, strictly speaking for Apple.
The only solution I fount was to handle this manually in the viewDidLayoutSubviews of the corresponding view controller.
In my case I wanted to keep track of the top cell displayed in a tableview with autolayout. As it wasn't tracked automatically by the system, I had to check that manually and scroll manually to the adequate cell on rotation. That's why I did it that way. It's quite "heavy" but works in my case.
I would also be interested if anyone has an easier solution.
I am updating my app to support iOS 8. In a few places, I do a modal presentation after popover dismissal. This is the code I used for iOS 7
[self.POP dismissPopoverAnimated:YES];
// initialization code for self.SMVC
MPTAdvancedSearchNav* nav = [[MPTAdvancedSearchNav alloc]initWithRootViewController:self.SMVC];
nav.modalPresentationStyle = UIModalPresentationFormSheet;
[self.navigationController presentViewController:nav animated:YES completion:nil];
When I run this on an iOS 8 device, I see a lag (about 1/4 of a second) before UIModalPresentationFormSheet animation begins.
I did some research and found this WWDC video https://developer.apple.com/videos/wwdc/2014/#214 (minute 29) that says I should use a transition coordinator to schedule a new presentation after the popover dismissal.
Problem is, I cant figure out how to do this. I think I am supposed to use
- (void)notifyWhenInteractionEndsUsingBlock:(void (^)(id<UIViewControllerTransitionCoordinatorContext> context))handler
Is this the method I should use? Also, how do I get a hold of UIViewControllerTransitionCoordinatorContext
Unfortunately, Popovers were only converted to a "presentation style" in iOS 8, so you'll still need the old code you have for iOS 7, and have a fork in there for iOS 8 specific code. On top of that, in order to make everything work nicely, you'll have to abandon UIPopoverController because it does not inherit from UIPresentationController, which is a requirement to hook into the transitioning system in iOS 8 for popovers specifically. All UIPopoverController does in iOS 8 is wrap UIPopoverPresentationController.
However, I think there's an easier solution that will work nicely in both 7 & 8: have whatever class is doing the creation / presentation of MPTAdvancedSearchNav become the delegate of UIPopoverController, and implement didDismissPopover:, and move your code into there that will do the presentation.
Checkout the docs here, and let me know how that works - https://developer.apple.com/Library/ios/documentation/UIKit/Reference/UIPopoverControllerDelegate_protocol/index.html#//apple_ref/doc/uid/TP40009307
I'm just started working in Titanium. And now working on popover in titanium, using the following code.
var myPopover = Ti.UI.iPad.createPopover({ height: 150, width: 150 });
popover.add(view);
popover.show({ view: button });
My issue is when user touches outside of the popover the popover is dismissing (default behaviour), I don't want that.
In iOS there is a delegate method called: - (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController . If we don't want to dismiss the popover when user touches outside just return NO, is there any way to do this in Titanium ?
I read the Titanium.UI.iPad.Popover documentataion, but couldn't find any such methods.
Please help me.
Thanks in advance.
Unfortunately without significant work yourself you won't be able to in the short-term.
Some tips:
Don't trust the api docs to have everything in. Instead either look through the SDK in Github or find the SDK on your system and search through the iOS (in the iPhone folder) classes for this method.
If you go to appcelerator's JIRA account, you can request this to be implemented. Go to the Q&A and make some noise about it to get other people to watch the JIRA issue so it is prioritised by Appcelerator.
You could write a module that extends the popover functionality, in fact with modules you can extend anything and create the javascript bridge yourself so you can access any properties or anything you want. There's guides for that on the Wiki..
Happy hunting ;)
So I'm creating and showing a custom window in my iOS app because I'm writing a dynamic alert view that also functions like a growl/toast alert. It works AWESOMELY in ios6 (Hopefully I can open source this baby and you can all check it out)
But anyway, when I run this in ios5, the window that my alerts exist on doesn't seem to rotate with the device/simulator. No matter what, my custom window stays in portrait mode.
The UIWindow is just a UIView subclass, so there's no nice 'shouldRotate' delegate method.
I'm kinda stumped on why this is happening in ios5 but not 6. Any help would be GREATLY appreciated ^_^
My window has a rootviewcontroller, which I completely forgot about. I just needed to implement
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return YES;
}
To get it to work.
:-D
It's usually not recommended two use multiple instances of UIWindow in one iOS app. The only valid reason to do so is to support external screens. You should use a UIView instead, ideally managed by a UIViewController.
I assume, (since you didn't provide any code, I can only assume) the reason why your window doesn't 'rotate' is, that it's simply not getting any notifications about device rotation. Only the keyWindow receives them by default.
I would highly recommend to redesign your app to use a properly managed UIView instead. If you desperately don't want that for some reason, you would have to register your instance of UIWindow to receive the UIDeviceOrientationDidChangeNotification and then (in the handler) evaluate what the new orientation is and change the window's frame accordingly (plus maybe other things that need to be done in response to the orientation change)
Please note that this question is from 2008 and now is of only historic interest.
What's the best way to create an iPhone application that runs in landscape mode from the start, regardless of the position of the device?
Both programmatically and using the Interface Builder.
Historic answer only. Spectacularly out of date.
Please note that this answer is now hugely out of date/
This answer is only a historical curiosity.
Exciting news! As discovered by Andrew below, this problem has been fixed by Apple in 4.0+.
It would appear it is NO longer necessary to force the size of the view on every view, and the specific serious problem of landscape "only working the first time" has been resolved.
As of April 2011, it is not possible to test or even build anything below 4.0, so the question is purely a historic curiosity. It's incredible how much trouble it caused developers for so long!
Here is the original discussion and solution. This is utterly irrelevant now, as these systems are not even operable.
It is EXTREMELY DIFFICULT to make this work fully -- there are at least three problems/bugs at play.
try this .. interface builder landscape design
Note in particular that where it says "and you need to use shouldAutorotateToInterfaceOrientation properly everywhere" it means everywhere, all your fullscreen views.
Hope it helps in this nightmare!
An important reminder of the ADDITIONAL well-known problem at hand here: if you are trying to swap between MORE THAN ONE view (all landscape), IT SIMPLY DOES NOT WORK. It is essential to remember this or you will waste days on the problem. It is literally NOT POSSIBLE. It is the biggest open, known, bug on the iOS platform. There is literally no way to make the hardware make the second view you load, be landscape. The annoying but simple workaround, and what you must do, is have a trivial master UIViewController that does nothing but sit there and let you swap between your views.
In other words, in iOS because of a major know bug:
[window addSubview:happyThing.view];
[window makeKeyAndVisible];
You can do that only once. Later, if you try to remove happyThing.view, and instead put in there newThing.view, IT DOES NOT WORK - AND THAT'S THAT. The machine will never rotate the view to landscape. There is no trick fix, even Apple cannot make it work. The workaround you must adopt is having an overall UIViewController that simply sits there and just holds your various views (happyThing, newThing, etc). Hope it helps!
From the Apple Dev Site:
To start your application in landscape
mode so that the status bar is in the
appropriate position immediately, edit
your Info.plist file to add the
UIInterfaceOrientation key with the
appropriate value
(UIInterfaceOrientationLandscapeRight
or
UIInterfaceOrientationLandscapeLeft),
as shown in Listing 2.
Listing 2: Starting your application
in landscape mode
<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeRight</string>
Summary and integration from all the posts, after testing it myself; check the update for 4.x, 5.x below.
As of 3.2 you cannot change the orientation of a running application from code.
But you can start an application with a fixed orientation, although doing so this is not straightforward.
Try with this recipe:
set your orientation to UISupportedInterfaceOrientations in the Info.plist file
in your window define a 480x320 "base view controller". Every other view will be added as a subview to its view.
in all view controllers set up the shouldAutorotateToInterfaceOrientation: method (to return the same value you defined in the plist, of course)
in all view controllers set a background view with
self.view.frame = CGRectMake(0, 0, 480, 320)
in the viewDidLoad method.
Update (iOS 4.x, 5.x): the Apple iOS App Programming Guide has a "Launching in Landscape Mode" paragraph in the "Advanced App Tricks" chapter.
References:
interface builder landscape design
interface builder landscape design-1
First I set in info.plist
<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeRight</string>
then I put this code in applicationDidFinishLaunching:
CGAffineTransform rotate = CGAffineTransformMakeRotation(1.57079633);
[window setTransform:rotate];
CGRect contentRect = CGRectMake(0, 0, 480, 320);
window.bounds = contentRect;
[window setCenter:CGPointMake(160.0f, 240.0f)];
This way I can work on the view in Interface Builder in landscape mode.
sasb's and michaelpryor's answer appears to be correct, but if it's not working for you, try this alternative:
- (void)applicationDidFinishLaunchingUIApplication *)application {
application.statusBarOrientation = UIInterfaceOrientationLandscapeRight;
}
Or this one:
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
Or this one:
[application setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO];
You may also have to call window makeKeyAndVisible; first.
A few links: Developing in landscape mode, iPhone SDK: How to force Landscape mode only?
#Robert: please refer to The iPhone SDK, NDA, and Stack Overflow.
I'm surprised no one has come up with this answer yet:
In all my tests when a dismissing a modal view controller the parent view controller's preferred orientation set in shouldAutorotateToInterfaceOrientation is honored even when part of a UINavigationController. So the solution to this is simple:
Create a dummy UIViewController with a UIImageView for a background. Set the image to the default.png image your app uses on startup.
When viewWillAppear gets called in your root view controller, just present the dummy view controller without animation.
when viewDidAppear gets called in your dummy view controller, dismiss the view controller with a nice cross dissolve animation.
Not only does this work, but it looks good! BTW, just for clarification i do the root view controller's viewWillAppear like this:
- (void)viewWillAppear:(BOOL)animated
{
if ( dummy != nil ) {
[dummy setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:dummy animated:NO];
[dummy release];
dummy = nil;
}
...
}
The latest iPhone OS Programming Guide has a full section on this, with sample code. I am sure this is a recent addition, so maybe you missed it. It explains all the conditions you have to comply with; basically...
set the Info.plist properties (this changes the position of the status bar, but not the view)
rotate your view manually around its center, on either your UIViewController viewDidLoad: method or your applicationDidFinishLaunching: method or implement auto rotation ("Autoresizing behaviors", page 124)
Look for "Launching in Landscape Mode", page 102.
See this answer: Landscape Mode ONLY for iPhone or iPad
add orientation to plist
shouldAutorotateToInterfaceOrientation = YES in all files
Although if you're using mixed modes, you might be better off with
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];