On iOS6 I had a method to make one view controller in my navigation-style app auto rotate to landscape when I pushed it. (Basically present a bogus view controller and dismiss it in viewWillAppear).
UIViewController *mVC = [[UIViewController alloc] init];
[self presentModalViewController:mVC animated:NO];
if (![mVC isBeingDismissed])
[self dismissModalViewControllerAnimated:NO];
With the latest SDK this no longer works. Does anyone have another way to auto rotate?
Turns out the solution is simple, just pass YES to dismissModalViewControllerAnimated
UIViewController *mVC = [[UIViewController alloc] init];
[self presentModalViewController:mVC animated:NO];
if (![mVC isBeingDismissed])
[self dismissModalViewControllerAnimated:YES]; //Fix here
From Developer site
"When a view controller is presented over the root view controller, the system behavior changes in two ways. First, the presented view controller is used instead of the root view controller when determining whether an orientation is supported. Second, the presented view controller can also provide a preferred orientation. If the view controller is presented full screen, the user interface is presented in the preferred orientation. The user is expected to see that the orientation is different from the device orientation and rotate the device. A preferred orientation is most often used when the content must be presented in the new orientation."
I think here you can use the preferred orientation method here.
Related
In my iOS 7/8 app, I have several viewcontrollers all of which should be portrait. I have a view controller which has a full screen view which will play video. I need this view to able to rotate depending on orientation of the phone. However the view controller that contains the video view is not allowed to rotate, reason being there are some other views sometimes overlaid above the full screen video view that are part of the viewcontroller and these should not be rotated.
So how can I rotate a single view and how do I get informed that the orientation of the device has changed. The usual [UIApplication sharedApplication].statusBarOrientation doesn't work as the viewcontroller itself does not rotate.
How to solve this problem.
Thanks
You can't rotate the screen forcefully. There is another way, in your case you have all the screens in Portrait, and you need a ViewController which can rotate both ways. You need to follow few steps described below.
See image for the settings you need to do in the Build Settings.
Create a Custom Class of type UINavigationController say CustomNavController, then in the .m class add the following code.
-(BOOL)shouldAutorotate{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskAll;
}
Present the video controller as Modal using the CustomNavController, like below
VideoViewController *controller=[[VideoViewController alloc] initWithNibName:#"VideoViewController" bundle:nil];
CustomNavController *nav=[[CustomNavController alloc] initWithRootViewController:controller];
[self presentViewController:nav animated:YES completion:nil];
nav.navigationBar.hidden=YES;
And it should work, the trick is to separate rotations.
I am updating our app to be compiled with xcode6/iOS8.
One issue I am running into is that when a modal view is presented. the underlying subview is removed. It is completely blacked out.. until the modal view is dismissed.. then it re-appears.
Has anyone run into this with iOS8? The same code has worked since iOS4.
Code:
PigDetailViewController *pigDetailViewController = [[PigDetailViewController alloc] initWithNibName:#"PigDetailViewController" bundle:nil];
self.navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
self.navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:pigDetailViewController animated:YES completion:nil];
In iOS 8 they've added a new presentation style that behaves like UIModalPresentationCurrentContext in the circumstance you've described, it's UIModalPresentationOverCurrentContext. The catch here is that unlike with UIModalPresentationCurrentContext, you want to set the view controller to be PRESENTED with this presentation style, like so:
PigDetailViewController *pigDetailViewController = [[PigDetailViewController alloc] initWithNibName:#"PigDetailViewController" bundle:nil];
pigDetailViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:pigDetailViewController animated:YES completion:nil];
Note that to support iOS 7 and below you'll likely need to check the OS version and decide how to present the view controller based on that.
edit: I'd like to add one more note to this. In iOS7 with UIModalPresentationCurrentContext, when the presented VC was dismissed, the underlying VC had its viewDidAppear method called. In iOS8 with UIModalPresentationOverCurrentContext, I've found the underlying VC does not have its viewDidAppear method called when the VC presented over top of it is dismissed.
Adding a point to BrennanR's answer.. even viewWillAppear doesn't call when the VC presented over top of it is dismissed.
I think you are misunderstanding how a modal view controller works.
When you present a view controller modally it will control the entire screen. It has an opaque background (normally black) and then draws its view on top of that.
So, if you set the view.backgroundColor to yellow (for example) it will have a yellow background. If you set it to clear then it will show through to the black background.
What I think you want is for the other view to "show through" so it looks like the modal view is sitting on top of it.
The best way I have found of doing this is to use this method...
// in the view controller that is presenting the modal VC
modalVC = // the modal VC that you will be presenting
UIView *snapshotView = [self.view snapshotViewAfterScreenUpdates:NO];
[modalVC.view insertSubView:snapshotView atIndex:0];
// present the modal VC
This will take a "screenshot" of the current view hierarchy and then place that snapshot underneath everything in the modal VC.
That way your black screen will be replaced by a screenshot of the previous view controller.
I have a problem with an app I am working on that goes like this:
The app's window has a rootViewController which is set to a custom class (MenuViewController) of UIViewController. This view controller has a rootViewController property of it's own. Whenever set this happens (really short version of the code):
- (void)setRootViewController:(UIViewControlelr *)rootViewController
{
...
_rootViewController = rootViewController;
if (self.rootViewController) {
[self addChildViewController:rootViewController];
[self.view addSubview:rootViewController.view];
}
...
}
Now this MenuViewController can show a modal view controller on top of it's rootViewController.
I do that like this:
[self.rootViewController presentModalViewController:viewController animated:YES completition:nil];
Everything looks to be ok until here. Now on iPad whenever I call [self.presentingViewController dismissViewControllerAnimated:YES completion:nil] from my modal view controller the interface beneath it rotates to the same orientation (that is UIInterfaceOrientationLandscapeLeft) regardless of what the initial orientation was when the view controller was presented.
So to conclude, my view's hierarchy is this:
Window
|
- Menu View Controller
|
- Root View Controller
|
- Modal view controller
Does anyone know how I can fix this? It doesn't happen on the iPhone.
It sounds to me as though on the iPad your MenuViewController's rootViewController supports multiple interface orientations, whereas on the iPhone it does not. This is speculation, as you have not said anything about this.
If this is the case, and the rootViewController does indeed support multiple interface orientations, the fix would be to override - supportedInterfaceOrientations and return portrait, which seems to be what you are suggesting you would want.
When using a customer UIActivity subclass in iOS 6, it's possible to specify a custom view controller that will be displayed when your action is chosen from the initial UIActionViewController's view. You do this by returning a reference to a custom view controller from your UIActivity subclass's activityViewController method.
According to the UIActivity class reference:
activityViewController
The default implementation of this method returns nil. Subclasses that provide additional UI using a view controller can override this method to return that view controller. If this method returns a valid object, the system presents the returned view controller for you, instead of calling the performActivity method. On iPad, your view controller is presented inside of a popover. On iPhone and iPod touch, your view controller is presented modally.
Your custom view controller should provide a view with your custom UI and should handle any user interactions inside those views. Upon completing the activity, do not dismiss the view controller yourself. Instead, call the activityDidFinish: method and let the system dismiss it for you.
Note that bit at the end of the first paragraph: On iPad, your view controller is presented inside of a popover. On iPhone and iPod touch, your view controller is presented modally.
However, on iPad the view controller returned by activityViewController always displays modally, no matter how I present the UIActivityViewController (either modally or via a popover). When presenting via a popover, it causes it to crash since it doesn't think it's been dismissed.
What am I doing wrong? Is this a bug in iOS 6?
Update: here's a simple Xcode project that illustrates the problem. Feel free to clone it and play around to see if you can see where we're going wrong: github.com/simonwhitaker/GSActivityDemo
As we are talking about the UIActivityViewController, which is the view showing the available activities to the user. Apple state the following...
Your app is responsible for configuring, presenting, and dismissing this view controller. Configuration for the view controller involves specifying the data objects on which the view controller should act. (You can also specify the list of custom services your app supports.) When presenting the view controller, you must do so using the appropriate means for the current device. On iPad, you must present the view controller in a popover. On iPhone and iPod touch, you must present it modally.
I took the last line as a sign that you have to handle how the view is presented, so I check whether the code is running on iPad and use a UIPopover accordingly. As you can sere here... https://github.com/bufferapp/buffer-uiactivity/blob/master/BufferUIActivity/Views/FirstViewController.m within the following method.
-(IBAction)openUIActivityView:(id)sender {
NSString *text = #"Hello world";
NSString *url = #"http://bufferapp.com";
NSArray *activityItems = #[text, url];
BufferUIActivity *bufferActivity = [[BufferUIActivity alloc] init];
UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:#[ bufferActivity ]];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
[self presentViewController:activityView animated:YES completion:^{
}];
} else {
// Change Rect to position Popover
self.popup = [[UIPopoverController alloc] initWithContentViewController:activityView];
[self.popup presentPopoverFromRect:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.width/2, 100, 100) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}
I think the issue with the activity view controller not showing in a popover is a bug and the docs reflect the correct intent. However, I don’t know of a way to workaround this atm.
The part about dismissing the view controller, however, is a different issue. You are not supposed to dismiss the view controller that you return from -[UIActivity activityViewController], but you are responsible for dismissing the popover that you have presented, which in turn will also remove your custom view controller from the hierarchy. (And because it works this way, I’m inclined to believe that the custom view controller would normally have to be shown in the popover.)
Here’s an example with code from your example app:
UIActivityViewController *vc = [[UIActivityViewController alloc] initWithActivityItems:activityItems
applicationActivities:applicationActivities];
vc.completionHandler = ^(NSString *activityType, BOOL completed){
[self.activityPopoverController dismissPopoverAnimated:YES];
};
I had the same problem in iOS 7. The solution to show the custom view in the popover is to create and show it in the -(void)performActivity method instead of returning it in -(UIViewController *)activityViewController.
You can see example code in my question/answer under this link:
iOS 7 custom UIActivity as popover
I just had the same problem but solved it by setting the ViewController to:
[yourViewController setModalPresentationStyle:UIModalPresentationPageSheet];
in
- (UIViewController *)activityViewController
hope this helps
I am trying to display a modal viewController in an iPad app using the UIModalPresentationFormSheet view style. I am looking to produce something similar to the Mail app's new message UI/animation.
There are two things that are not behaving correctly:
The modal viewController that is presented always animates to y=0, i.e. to the very top of the
view and not some pixels below the status bar as it does in the mail app.
The documentation says:
UIModalPresentationFormSheet The width
and height of the presented view are
smaller than those of the screen and
the view is centered on the screen. If
the device is in a landscape
orientation and the keyboard is
visible, the position of the view is
adjusted upward so that the view
remains visible. All uncovered areas
are dimmed to prevent the user from
interacting with them.
However, in my case there is no dimming and I can still interact with the parentView below the modalViewController.
The controller that presents the modalView I do this:
AddNewItemViewController *newItemViewController = [[AddNewItemViewController alloc] initWithNibName:#"AddNewItemViewController" bundle:nil];
[self presentModalViewController:newItemViewController animated:YES];
[newItemViewController release];
In the viewController being presented I do this:
- (void)viewDidLoad {
[nameField becomeFirstResponder];
[self setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self setModalPresentationStyle:UIModalPresentationFormSheet];
[super viewDidLoad];
}
I hope someone can help me out.
Is there some other properties I need to set on the parent and modalViewController?
Is the viewDidLoad not the right place to do this setup?
Thanks in advance:)
You set the transition and presentation styles when you create the modal view, before you call presentModalViewController. Remember, the view that creates the modal view 'owns' that object. You want the owner to set these properties because you might implement this modal view elsewhere in the app and want different transition or presentation styles. This way, you set it each time as appropriate.
AddNewItemViewController *newItemViewController = [[AddNewItemViewController alloc] initWithNibName:#"AddNewItemViewController" bundle:nil];
newItemViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:newItemViewController animated:YES];
[newItemViewController release];
You're right in calling becomeFirstResponder in viewDidLoad.