I am developing an app with Xamarin MVVMCross technology and would like to ask a question about InterfaceOrientation option: all view controllers of application support all orientations but one should use only Portrait mode.
When I go to that ViewController from others in Portrait mode, I am able to keep my portrait viewcontroller in Portrait mode due to method in my AppDelegate.cs :
[Export("application:supportedInterfaceOrientationsForWindow:")]
public UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, IntPtr forWindow)
{
if (this.RestrictRotation)
return UIInterfaceOrientationMask.All;
else
return UIInterfaceOrientationMask.Portrait;
}
But when I go to my portraitview controller when device in LandscapeLeft(LandscapeRight) orientations, GetSupportedInterfaceOrientations from AppDelegate.cs method is not called and that view controller is opened in Landscape mode.
Is there a way to programmatically change orientation from Landscape to Portrait Mode just for one view controller in Xamarin IOS?
Thank you
In your helper class;
public static void LockOrientation(UIInterfaceOrientationMask uIInterfaceOrientationMask, UIInterfaceOrientation uIInterfaceOrientation)
{
if (UIApplication.SharedApplication.Delegate != null)
{
CommonUtils.LockOrientation(uIInterfaceOrientationMask);
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)uIInterfaceOrientation), new NSString("orientation"));
}
}
In AppDelegate.cs
//Set the orientation for rest of the app
public UIInterfaceOrientationMask OrientationLock = UIInterfaceOrientationMask.All;
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
{
return OrientationLock;
}
Now simply call the LockOrientation method wherever you would like to lock/change orientation.
Example:
CommonUtils.LockOrientation(UIInterfaceOrientationMask.Portrait, UIInterfaceOrientation.Portrait);
If you want to control the orientation of a single ViewController don't use the AppDelegate GetSupportedInterfaceOrientations method instead use the one in the ViewController you want to control.
These lines will prevent the ViewController you implement this method to get any other orientation but Portrait.
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations ()
{
return UIInterfaceOrientationMask.Portrait;
}
Note: You will need to repeat the same for every ViewController in case you want to force the orientation to more than one.
i hope that work..
put in your controller class
public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
{
base.DidRotate(fromInterfaceOrientation);
UIAlertView alert = new UIAlertView();
alert.Title = UIDevice.CurrentDevice.Orientation + "";
alert.AddButton("ok");
alert.Show();
}
Related
This is a common question on StackOverflow, but none of the other solutions worked. Many were also written several years ago.
Here are some of the posts considered:
Is it possible to have different orientations for viewControllers inside UINavigationController?
Support landscape for only one view in UINavigationController
Is it possible to have different orientations for viewControllers inside UINavigationController?
Why can't I force landscape orientation when use UINavigationController?
How to force view controller orientation in iOS 8?
We have several view controllers embedded inside a UINavigationController: A, B, C, D.
A, B use portrait.
C, D use landscape.
A is the root controller.
Assume B gets pushed onto A. That works since B is portrait. However, when C gets pushed onto B, the screen doesn't rotate since as the class docs state:
Typically, the system calls this method only on the root view
controller of the window or a view controller presented to fill the
entire screen; child view controllers use the portion of the window
provided for them by their parent view controller and no longer
participate directly in decisions about what rotations are supported.
So overriding supportedInterfaceOrientations inside a custom UINavigationController doesn't help because it isn't consulted on transitions within embedded controllers.
Effectively, we need a way to force orientation changes upon transitions, but there seems to be no supported method for this.
Here's how we override UINavigationController (extension is only used now for debugging purposes since apparently extensions shouldn't be used for overriding):
extension UINavigationController {
override open var shouldAutorotate: Bool {
return true
}
override open var supportedInterfaceOrientations : UIInterfaceOrientationMask {
return visibleViewController?.supportedInterfaceOrientations ?? UIInterfaceOrientationMask.landscapeRight
}
}
Within embedded view controllers, we try to set the orientation like this:
override var shouldAutorotate: Bool {
return true
}
override var preferredInterfaceOrientationForPresentation : UIInterfaceOrientation {
return UIInterfaceOrientation.landscapeRight
}
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.landscapeRight
}
To summarize, the goals are:
1) Show view controllers embedded inside a UINavigationController with different orientations.
2) VC transitions should yield proper orientation change (e.g., popping from C->B should yield portrait, popping from D->C should yield landscape, pushing from B->C should yield landscape, pushing from A->B should yield portrait).
If it were possible to force the UINavigationController into an orientation (with publicly supported method), one possible solution could be to force the orientation upon showing the new view controller. But this also doesn't seem possible.
Suggestions?
Step 1
Subclass UINavigationController.
class LandscapeNavigationController: UINavigationController {
public var vertical: Bool = true
override var shouldAutorotate: Bool {
get { return true }}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get { return (vertical) ? .portrait : .landscapeLeft }}
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
get { return (vertical) ? .portrait : .landscapeLeft }}
}
Step 2
Use different UINavigationController for different orientations. Yes, pushing a new UINavigationController atop a previous UINavigationController will essentially be modal, but the transition looks nice.
For added convenience, use User Defined Runtime Attributes to control the orientation of the LandscapeNavigationController.
Step 3
Add a pop method to handle Back buttons on the now modal UIViewController.
#IBAction func doBack(_ sender: UIBarButtonItem) {
if let navigationController = navigationController {
navigationController.dismiss(animated: true, completion: {
})
}
}
In Action
Notice how the Top and Bottom labels on view C are properly laid out.
↻ replay animation
► Find this solution on GitHub and additional details on Swift Recipes.
I know that I can select available orientation of my app in targets menu, but I would like to block landscape orientation for each screen EXCEPT the one with youtube player.
I suppose this couldn't be done via app delegate since it does not distinct controllers and apply the rule for the whole app.
I suspect maybe there is a way to do that via sharedApplication(), but have no clue how to.
Do you have any idea how such thing could be done?
Add this to the UIViewController class you wish to set to Portrait only.
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
Provided you haven't changed any settings in your target the other screens without this should be able to rotate correctly.
Use this override func in each VC you want to block .LandscapeLeft and .LandscapeRight
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Portrait.rawValue) | Int(UIInterfaceOrientationMask.PortraitUpsideDown.rawValue)
}
On general settings I allow portrait and landscapeleft , landscaperight modes. I want to turn off landscape modes. On viewController I write this code:
override func shouldAutorotate() -> Bool {
return false
}
However, the auto rotation works ignoring this function. How do I disable and enable autorotation on swift? IOS programming
It might be the right code, but not in the right View Controller. For example, if the View Controller is embedded in a UINavigationController, the navigation controller can still rotate, causing the View Controller to still rotate. It really depends on your specific situation.
I had the same problem, i have fixed that.
Follow-
info --> custom ios target properties --> Support Interface Orinetations. and delete ![Delete - Landscape (left home button), Landscape (right home button), Landscape (top home button)][1]
This will help you 😊
You can do it by creating a subclass of UINavigationController and inside that, override the should AutoRotate function,then
return false for the viewControllers in which you want to disable autorotation
return true for the viewControllers in which you want autorotation
import UIKit
class CustomNavigationController: UINavigationController {
override func shouldAutorotate() -> Bool {
if !viewControllers.isEmpty {
// Check if this ViewController is the one you want to disable roration on
if topViewController!.isKindOfClass(ViewController) { //ViewController is the name of the topmost viewcontroller
// If true return false to disable it
return false
}
}
// Else normal rotation enabled
return true
}
}
If you want to disable autorotation throughout the navigation controller, remove the if condition and return false always
Extending Josh Gafni's answer and user3655266, the concept also extends to view controllers.
If we have a UIViewController that is a child of another UIViewController in the view hierarchy, overriding the child's shouldAutorotate() to false might still rotate since it parent's controller might still be returning true. It is also important to know that even though the child VC is being displayed, the parent's shouldAutoRotate function are still called. Hence the control should lie there.
Swift 5
class ParentViewController:UIViewController{
override func shouldAutorotate() -> Bool {
// Return an array of ViewControllers that are children of the parent
let childViewControllersArray = self.children
if childViewControllersArray.count > 0 {
// Assume childVC is the ViewController you are interested in NOT allowing to rotate
let childVC = childViewControllersArray.first
if childVC is ChildViewController {
return false
}
}
return true
}
}
** The same can also be done to only allow certain phone orientation **
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
// This function is called on the parent's controller whenever the child of this parent is trying to rotate
let childrenVCArray = self.children
if childrenVCArray.count > 0 {
// assuming the array of the first element is the current childVC
let topMostVC = childrenVCArray[0]
if topMostVC is ChildViewController {
// Assuming only allowing landscape mode
return .landscape
}
}
// Return portrait otherwise
return .portrait
}
I am not sure if shouldAutorotate() function is still enabled in swift 5 for year 2021. However I recommend calling one of the following functions as part of standard procedure for managing rotation of ViewControllers. ("Handling View Rotations" part in apple developer webside For example preferredInterfaceOrientationForPresentation
So, I have a custom UIWindow, a custom UIViewController, and a custom UIImageView.
private class CoachingWindow : UIWindow
{
public CoachingWindow(...)
{
RootViewController = new CoachingOverlayViewController(...);
}
private class CoachingOverlayViewController : UIViewController
{
public CoachingOverlayViewController(...)
{
View = new CoachingOverlayView(...);
}
public override void DidRotate (UIInterfaceOrientation fromInterfaceOrientation)
{
...
}
public override bool ShouldAutorotate()
{
return true;
}
private class CoachingOverlayView : UIImageView
{
public CoachingOverlayView(...)
{
...
}
}
}
}
The window, view controller, and view all display properly when first called: the custom UIWindow appears as an overlay over the rest of the existing UIWindows, the view controller is properly assigned as the RootViewController, the view is assigned to the View property of the view controller. So, it all renders correctly.
However, the overridden DidRotate() and ShouldAutorotate() methods never get get called when I physically rotate the device or simulator.
I'm thinking it may have something do with the fact that I'm using a custom UIWindow. Perhaps the window isn;t receiving notifications of orientation change from iOS? Or does iOS send these notifications directly to view controllers? Maybe I have to somehow make the view controller subscribe to these events because it's a custom view controller???
I'm using MonoTouch and iOS 6.
Any suggestions would be great. I'm beating my head against the wall here.
in iOS 6 you can use
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
{ ... }
Figured it out. I had override ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation) instead of ShouldAutorotate(). But this is actually deprecated in iOS6. Still have to figure out how to do it the iOS6 way, but this will do for now.
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
...
}
I am building an monoTouch-iPad application and I am stumbling around because of the start-interface-orientation.
One problem is, when the app starts UIDevice.CurrentDevice.Orientation always returns Unknown. How can you decide in which orientation your app starts? All properties that I found by now just return portrait-mode, unknown or the frame-size of portrait mode - even if it's landscape mode.
I also created two UIViews (one for landscape, one for portrait) and changing them by now in the WillRotate method of the UIViewController. But my code:
if(toInterfaceOrientation==UIInterfaceOrientation.LandscapeLeft || toInterfaceOrientation==UIInterfaceOrientation.LandscapeRight){
_scrollView.RemoveFromSuperview();
this.View.Add (_scrollViewLandscape);
}else{
_scrollViewLandscape.RemoveFromSuperview();
this.View.Add (_scrollView);
}
produces a short and ugly "flickering" when rotating the screen - at least in the simulator.
Is there a best practice for laying out your Views? And I know about ShouldAutorotateToInterfaceOrientationbut this doesn't work for me, as I am doing much owner drawn stuff, which gets broken when autoresized (see my other question).
I would really appreciate a solution without using the Interface-Builder, as I am doing everything in code by now.
UPDATE: Short-Descripton what I want to achieve:
AppStart -> knowing the correct Framsize (1024,748 or 768,1004) -> Adding my custom view in correct framesize
UPDATE2: simple and basic code-snippet
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
Console.WriteLine (this.InterfaceOrientation);
}
returns portrait. Even if the simulator is in landscape-mode.
inside your UIViewController can just check the InterfaceOrientation
public override void ViewDidLoad ()
{
if (this.InterfaceOrientation == UIInterfaceOrientation.Portrait
|| this.InterfaceOrientation == UIInterfaceOrientation.PortraitUpsideDown)
{
// portrait
}
else
{
// landsacpe
}
}
but i would really recommend using View.AutoresizingMask or overriding LayoutSubviews, both makes the all the transitions really smooth
UPDATE: using AutoresizingMask
public override void ViewDidLoad ()
{
UIView view = new CustomView(View.Bounds);
view.AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth;
View.AddSubview(view);
}
UPDATE: overriding LayoutSubviews
LayoutSubviews is called everytime the Size changes
public class CustomView : UIView
{
public override void LayoutSubviews ()
{
//layout your view with your own logic using the new values of Bounds.Width and Bounds.Height
}
}