Wie stelle ich die Geräteorientierung in iOS 7 programmgesteuert ein?

72

Ich arbeite an einer iPad-App mit AutoLayout. Wenn der Benutzer einen bestimmten Modus aktiviert ("Heads-up" -Modus), möchte ich nur die Ausrichtung im Hochformat (oder im Hochformat auf dem Kopf stehend) unterstützen und außerdem, wenn sich das Gerät befindet Querformat möchte ich automatisch in den Hochformatmodus wechseln.

In der Draufsicht-Steuerung habe ich Folgendes:

- (NSUInteger) supportedInterfaceOrientations {
    if (self.modeHeadsUp) {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

- (BOOL) shouldAutorotate {
    return TRUE;
}

Basierend auf den Antworten, die ich hier an anderer Stelle gesehen habe, scheint die Antwort zu sein, dass ich "application setStatusBarOrientation" verwenden sollte. Daher habe ich in die Methode, bei der der Benutzer den "Heads-up" -Modus ausgewählt hat, Folgendes aufgenommen:

    UIApplication *application = [UIApplication sharedApplication];
    [application setStatusBarOrientation:UIInterfaceOrientationPortrait
                                animated:YES];

Dies scheint jedoch einfach nichts zu bewirken. Ich kann das Gerät zwar physisch bewegen, um es in ein Hochformat zu verwandeln, dies geschieht jedoch nicht automatisch.

Wenn ich im Querformatmodus bin, nachdem ich den obigen Code ausgeführt habe, um zu versuchen, die Ausrichtung programmgesteuert festzulegen, bleibt die Anwendung "statusBarOrientation" mit dem folgenden Code bei "4" für Querformat:

UIApplication *application = [UIApplication sharedApplication];
int orientation = [application statusBarOrientation];
self.movesTextView.text = [NSString stringWithFormat:@"ORIENTATION %d", orientation];

Es schien, als würde mit der setStatusBarOrientation kein Autolayout ausgelöst, also habe ich versucht, diesen Code nachher ohne Erfolg hinzuzufügen:

    [super updateViewConstraints];
    [self.view updateConstraints];

Mir ist klar, dass Apple die Geräteorientierung in den Händen des Benutzers lassen möchte. Ich möchte jedoch den Querformatmodus unterstützen können, wenn ich mich nicht im "Heads-up" -Modus befinde.

Vermisse ich etwas, um eine Orientierungsänderung erzwingen zu können?

John Stewart
quelle

Antworten:

195

Für iOS 7 & 8:

Ziel c:

NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];

Swift 3+:

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")

Ich rufe es an - viewDidAppear:.

Sunny Shah
quelle
1
Das funktioniert! Jetzt mache ich mir Sorgen, dass dies einen Konformitätsfehler erzeugt, und die Leute scheinen objc_msgSend (siehe stackoverflow.com/questions/17263354/… ) mächtig zu missbilligen , aber das funktioniert! Ich werde versuchen zu sehen, ob es einen Weg gibt, dies sauberer zu machen.
John Stewart
4
Wenn ich das versuche, funktioniert es NUR auf dem iPad. Sowohl das iPad Mini (iOS 7.1) als auch das iPhone 5s (iOS 7.1) funktionieren nicht. Der Simulator für alle Geräte funktioniert
Jeef
1
@Jeef Stellen Sie sicher, dass Sie alle Modi zur Orientierung für Ihre Anwendung eingestellt haben,
Sunny Shah
4
Gibt es eine Möglichkeit, dies ohne die Animation zum Laufen zu bringen?
Jiho Kang
4
Dies ist Gold wert, insbesondere wenn Sie UIViewController.attemptRotationToDeviceOrientation () und stackoverflow.com/questions/24928057/… verwenden , können Sie jede gewünschte Ansicht einrichten !
DogCoffee
21

Benutze das. Perfekte Lösung für Orientierungsprobleme..ios7 und früher

[[UIDevice currentDevice] setValue:
    [NSNumber numberWithInteger: UIInterfaceOrientationPortrait]
        forKey:@"orientation"];
Arpan Dixit
quelle
1
Funktioniert sogar unter iOS 8. Perfekt.
Josef Rysanek
1
Dies wird vom Apple App Review Team (vorerst) akzeptiert, aber ich würde nicht sagen, dass es "von Apple akzeptiert" wird. Das heißt, es ist ein Hack, Schlüsselwertcodierung zu verwenden, und möglicherweise funktioniert es in zukünftigen iOS-Versionen nicht mehr.
Adam Kaplan
@Arpan Wo sollte dieser Code hinzugefügt werden, habe ich in viewwillapear hinzugefügt, aber nicht funktioniert, bitte erklären.
Ram S
@arpan_techisavy: Ich habe es mit allen versucht, didload, willapear, didapear. Eines meiner Projekte steckt aufgrund eines Orientierungsproblems in ios8 fest. Ich möchte alle Ansichten im Hochformat bis auf eine Ansicht (Videoplayer) im Querformat. Ich habe das Hochformat aktiviert und den Querformatmodus im Storyboard verlassen. Ich habe den ganzen Code ausprobiert, aber er funktioniert nicht. Ich glaube, mir fehlt etwas. Bitte beraten Sie und wenn Sie Demo haben, bitte Hase.
Ram S
@ Josef Rysanek: Ich habe es mit allen versucht, didload, willapear, didapear. Eines meiner Projekte steckt aufgrund eines Orientierungsproblems in IOS-8 fest. Ich möchte alle Ansichten im Hochformat bis auf eine Ansicht (Videoplayer) im Querformat. Ich habe das Hochformat aktiviert und den Querformatmodus im Storyboard verlassen. Ich habe den ganzen Code ausprobiert, aber er funktioniert nicht. Ich glaube, mir fehlt etwas. Bitte beraten Sie und wenn Sie Demo haben, bitte Hase.
Ram S
10

Sie müssen anrufen attemptRotationToDeviceOrientation (UIViewController), damit das System Sie anruft, supportedInterfaceOrientationswenn sich die Bedingung geändert hat.

Schlange
quelle
In meinem Code habe ich versucht, die Anwendungsmethode setStatusBarOrientation durch [UIViewController tryRotationToDeviceOrientation] zu ersetzen, wie Sie vorschlagen. Es scheint jedoch leider keine Wirkung zu haben.
John Stewart
Ich finde diesen Thread auch dort, wo Ihre Vorschläge zwar die "richtige Antwort" zu sein scheinen, aber einfach nicht funktionieren: stackoverflow.com/questions/19125263/…
John Stewart
2
Dies funktioniert nur, wenn die UIViewController
Geräteorientierung von der
7
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]; [[UIDevice currentDevice] setValue:value forKey:@"orientation"];

funktioniert, aber Sie müssen zurückkehrenAutorotate mit YES in Ihrem View Controller

- (BOOL)shouldAutorotate
{
    return self.shouldAutoRotate;
}

Aber wenn Sie das tun, wird Ihr VC automatisch drehen, wenn der Benutzer das Gerät dreht ... also habe ich es geändert in:

@property (nonatomic, assign) BOOL shouldAutoRotate;

- (BOOL)shouldAutorotate
{
    return self.shouldAutoRotate;
}

und ich rufe an

- (void)swithInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    self.rootVC.shouldAutoRotate = YES;

    NSNumber *value = [NSNumber numberWithInt: orientation];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}

um eine neue Ausrichtung mit einem Knopfdruck zu erzwingen. Um shouldAutoRotate auf NO zurückzusetzen, habe ich meine rootVC hinzugefügt

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    self.shouldAutoRotate = NO;
}

PS: Diese Problemumgehung funktioniert auch in allen Simulatoren.

Rikco
quelle
1
Witzig .. Ich habe nach so etwas gesucht und was habe ich gefunden? Die einzige Lösung für iOS 11 und ich habe sie vor 1,5 Jahren selbst geschrieben. Und ich habe es realisiert, als ich eine +1 für die Lösung geben wollte: D
Rikco
wow ... Sir, Sie verdienen eine positive Bewertung, dies ist bei weitem die sauberste Lösung, die ich je gesehen habe
daisura99
4

Das funktioniert bei mir am Xcode6 & 5.

- (BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return (UIInterfaceOrientationMaskPortrait);
}
SamSmart
quelle
3

Die einzige Möglichkeit, die für mich funktioniert hat, ist die Präsentation des Dummy-Modal-View-Controllers.

UIViewController* dummyVC = [[UIViewController alloc] init];
dummyVC.view = [[UIView alloc] init];
[self presentModalViewController:dummyVC animated:NO];
[self dismissModalViewControllerAnimated:NO];

Ihr VC wird nach aktualisierten Schnittstellenausrichtungen gefragt, wenn der Modal View Controller deaktiviert wird.

Merkwürdig ist, dass UINavigationController genau dies tut, wenn untergeordnete View-Controller mit unterschiedlichen unterstützten Schnittstellenausrichtungen (getestet unter iOS 6.1, 7.0) verschoben / geöffnet werden.

kjam
quelle
Wie an anderer Stelle erwähnt, funktioniert dies nicht mehr unter iOS 8.
Mnemia
3

Wenn Sie einen UIViewController haben, der im Hochformat bleiben soll, fügen Sie einfach diese Überschreibung hinzu und schon sind Sie fertig.

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}

Das Beste daran ist, dass es keine Animation gibt, wenn diese Ansicht angezeigt wird. Sie befindet sich nur bereits in der richtigen Ausrichtung.

Torre Lasley
quelle
3

Mit dieser Lösung können Sie eine bestimmte Schnittstellenausrichtung erzwingen, indem Sie den Wert von vorübergehend überschreiben UIDevice.current.orientationund das System dann auffordern, die Schnittstelle entsprechend der Drehung des Geräts zu drehen:

Wichtig: Dies ist ein Hack und kann jederzeit aufhören zu arbeiten

Fügen Sie dem Root View Controller Ihrer App Folgendes hinzu:

class RootViewController : UIViewController {
    private var _interfaceOrientation: UIInterfaceOrientation = .portrait
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return UIInterfaceOrientationMask(from: _interfaceOrientation) }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return _interfaceOrientation }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Register for notifications
        NotificationCenter.default.addObserver(self, selector: #selector(RootViewController.handleInterfaceOrientationChangeRequestedNotification(_:)), name: .interfaceOrientationChangeRequested, object: nil)
    }

    deinit { NotificationCenter.default.removeObserver(self) }

    func handleInterfaceOrientationChangeRequestedNotification(_ notification: Notification) {
        guard let interfaceOrientation = notification.object as? UIInterfaceOrientation else { return }
        _interfaceOrientation = interfaceOrientation
        // Set device orientation
        // Important:
        // • Passing a UIDeviceOrientation here doesn't work, but passing a UIInterfaceOrientation does
        // • This is a hack, and could stop working at any moment
        UIDevice.current.setValue(interfaceOrientation.rawValue, forKey: "orientation")
        // Rotate the interface to the device orientation we just set
        UIViewController.attemptRotationToDeviceOrientation()
    }
}

private extension UIInterfaceOrientationMask {

    init(from interfaceOrientation: UIInterfaceOrientation) {
        switch interfaceOrientation {
        case .portrait: self = .portrait
        case .landscapeLeft: self = .landscapeLeft
        case .landscapeRight: self = .landscapeRight
        case .portraitUpsideDown: self = .portraitUpsideDown
        case .unknown: self = .portrait
        }
    }
}

extension Notification.Name {
    static let interfaceOrientationChangeRequested = Notification.Name(rawValue: "interfaceOrientationChangeRequested")
}

Stellen Sie sicher, dass alle Schnittstellenausrichtungen unter "Bereitstellungsinformationen" überprüft sind:

Schnittstellenausrichtungen

Fordern Sie Änderungen an der Ausrichtung der Benutzeroberfläche an, wo Sie sie benötigen:

NotificationCenter.default.post(name: .interfaceOrientationChangeRequested, object: UIInterfaceOrientation.landscapeRight)
Niels
quelle
2

Wenn Sie die Hauptansicht Ihrer App für Hochformat sperren möchten, aber Popup-Ansichten im Querformat öffnen möchten und tabBarController wie ich als rootViewController verwenden, können Sie diesen Code in Ihrem AppDelegate verwenden.

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UITabBarController *tabBarController;

@end

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Create a tab bar and set it as root view for the application
    self.tabBarController = [[UITabBarController alloc] init];
    self.tabBarController.delegate = self;
    self.window.rootViewController = self.tabBarController;

    ...
}

- (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController
{
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)tabBarControllerPreferredInterfaceOrientationForPresentation:(UITabBarController *)tabBarController
{
    return UIInterfaceOrientationPortrait;
}

Es funktioniert sehr gut.

In Ihrem viewController, der im Querformat dargestellt werden soll, verwenden Sie einfach Folgendes:

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate {
    return YES;
}
Carlos Eugênio Torres
quelle
Interessant. Ich werde damit herumspielen, wenn ich meine App umstrukturiert habe. Derzeit gibt es keinen Controller für die Containeransicht, aber ich habe trotzdem vor, ihn in einen zu packen. Vielen Dank.
John Stewart
Für mich hat der letzte Codeblock gut funktioniert! Aber ich habe auch keine UINavigationControlleroder UITabBarControllerintegrierte, also ist es vielleicht ein kleiner Unterschied.
Alex Cio
2
  1. Fügen Sie diese Anweisung hinzu AppDelegate.h

    //whether to allow cross screen marker 
    @property (nonatomic, assign) allowRotation BOOL;
    
  2. Schreiben Sie diesen Codeabschnitt in AppDelegate.m

    - (UIInterfaceOrientationMask) application: (UIApplication *) supportedInterfaceOrientationsForWindow: application (UIWindow *) window {
        If (self.allowRotation) {
            UIInterfaceOrientationMaskAll return;
        }
        UIInterfaceOrientationMaskPortrait return;
    }
    
  3. Ändern Sie die allowRotationEigenschaft der Delegaten-App

niuNaruto
quelle
Willkommen bei StackOverflow. Könnten Sie den Code bitte etwas besser formatieren, um ihn besser lesbar zu machen?
Slfan
1

Der Basis-UINavigationController sollte den folgenden Rückruf haben, damit die untergeordneten Elemente entscheiden können, welche Ausrichtung sie wünschen.

-(NSUInteger)supportedInterfaceOrientations {
    UIViewController *topVC = self.topViewController;
    return topVC.supportedInterfaceOrientations;
}

-(BOOL)shouldAutorotate {
   UIViewController *topVC = self.topViewController;
   return [topVC shouldAutorotate];
}
Ekra
quelle
1

Wenn Sie nur den Porträtmodus möchten, können Sie in iOS 9 (Xcode 7):

  • Gehen Sie zu Info.plist
  • Wählen Sie das Element "Unterstützte Schnittstellenausrichtungen"
  • Löschen Sie "Landschaft (linke Home-Taste)" und "Landschaft (rechte Home-Taste)".

Geben Sie hier die Bildbeschreibung ein

Danilo Raspa
quelle
1

Ich hatte ein ähnliches Problem wie Sie. Ich muss die Geräteorientierung für einige Bildschirme (wie Login) sperren und die Drehung in anderen zulassen.

Nach ein paar Änderungen und den folgenden Antworten habe ich es gemacht durch:

  • Aktivieren aller Ausrichtungen in der Info.plist des Projekts.

Geben Sie hier die Bildbeschreibung ein

  • Deaktivieren der Ausrichtung in den ViewControllern, in denen sich das Gerät nicht drehen soll, wie in meinem Fall im Anmeldebildschirm. Ich musste die shouldAutorotateMethode in dieser VC überschreiben :

-(BOOL)shouldAutorotate{ return NO; }

Hoffe das wird für dich funktionieren.

David de Tena
quelle
1

Hier ist es ein VOLLSTÄNDIGES ARBEITSbeispiel für iOS 7, 8, 9, 10, wie die App-Ausrichtung auf das aktuelle Gegenteil geändert wird

Ziel c

- (void)flipOrientation
{
    NSNumber *value;
    UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if(UIInterfaceOrientationIsPortrait(currentOrientation))
    {
        if(currentOrientation == UIInterfaceOrientationPortrait)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown];
        }
        else //if(currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
        }
    }
    else
    {
        if(currentOrientation == UIInterfaceOrientationLandscapeRight)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
        }
        else //if(currentOrientation == UIInterfaceOrientationLandscapeLeft)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
        }
    }
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
}

Swift 3

func flipOrientation() -> Void
{
    let currentOrientation : UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
    var value : Int = 0;
    if(UIInterfaceOrientationIsPortrait(currentOrientation))
    {
        if(currentOrientation == UIInterfaceOrientation.portrait)
        {
            value = UIInterfaceOrientation.portraitUpsideDown.rawValue
        }
        else //if(currentOrientation == UIInterfaceOrientation.portraitUpsideDown)
        {
            value = UIInterfaceOrientation.portrait.rawValue
        }
    }
    else
    {
        if(currentOrientation == UIInterfaceOrientation.landscapeRight)
        {
            value = UIInterfaceOrientation.landscapeLeft.rawValue
        }
        else //if(currentOrientation == UIInterfaceOrientation.landscapeLeft)
        {
            value = UIInterfaceOrientation.landscapeRight.rawValue
        }
    }
    UIDevice.current.setValue(value, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation()
}
ddb
quelle
Diese Lösung verwendet Reflexionstechnik, da iOS keine öffentliche Methode hat / anzeigt, um dieses Ziel zu erreichen, also müssen wir es gegen Reflexion tun
ddb
Nun, letztendlich ändern wir die Dinge, die Apple nicht von uns will: UIDevice's orientationEigentum ist nur fertig. Wurde die App für diesen Code jemals abgelehnt?
Sunil Chauhan
Entschuldigung, aber ich kann nicht antworten, da die App, in der ich diesen Code verwende, außerhalb von iTunes veröffentlicht wurde. Haben Sie versucht, einen solchen Code einzureichen?
ddb
1

Für diejenigen wie mich, die Schwierigkeiten hatten, @Sunny Shah zu bekommen, akzeptierte sie die Antwort, um an iPads zu arbeiten. Sie müssen das Kontrollkästchen "Vollbild erforderlich" in den Projekteinstellungen aktivieren. Beachten Sie, dass dies verhindert, dass Ihre App im Multitasking-Modus arbeitet, der möglicherweise akzeptabel ist oder nicht.

Geben Sie hier die Bildbeschreibung ein

Leandrodemarco
quelle
0

Versuchen Sie dies zusammen mit Ihrem Code.

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

Sobald der Benutzer eine Option ausgewählt hat, rufen Sie diese Methode auf, da sich der Benutzer im Querformat befinden kann und er dann nur den Hochformatmodus in derselben Ansichtssteuerung einstellen kann. Daher sollte die Ansicht automatisch in den Hochformatmodus verschoben werden, damit diese Schaltfläche dies aufruft

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
Charan Giri
quelle
Ich bin mir nicht ganz sicher, was Sie vorschlagen, aber shouldAutorotateToInterfaceOrientation scheint in iOS7 zugunsten von shouldAutorotate, das ich implementiert habe, veraltet zu sein.
John Stewart
Entschuldigung, mein Fehler. Verwenden Sie sollte nurAutorotate und - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation Dauer: (NSTimeInterval) Dauer
Charan Giri
Ich [self willRotateToInterfaceOrientation:UIInterfaceOrientationPortrait duration:ROTATE_VIEW_DELAY];habe die Methode hinzugefügt , bei der der Heads-up-Modus ausgewählt ist, und die objc_msgSend-Antwort von Sunny Shah oben ersetzt. Leider immer noch keine Auswirkung auf das Display. Nach meinem Verständnis wird "willrotate" aufgerufen, wenn sich das Display als Reaktion auf das sich physisch bewegende Gerät des Benutzers dreht.
John Stewart
@ John Stewart, wir können diese Methode unabhängig aufrufen ... Aber sagen Sie mir, welchen Code Sie in dieser Methode geschrieben haben. Wenn möglich, können Sie Ihre Frage mit dem gesamten Code aktualisieren, den Sie zur Orientierung geschrieben haben (alle von Ihnen verwendeten Methoden)
Charan Giri
Charan Giri - Ich habe ein einfaches Testprojekt erstellt, um dies zu demonstrieren: github.com/johnstewart/RotateTest . Das Projekt ist eine einfache iPad-App mit einer ImageView und einem Schalter zum Ein- und Ausschalten von "Nur Porträt". Ich begann mit einem leeren Nur-Ansicht-Projekt und fügte diese Eigenschaft hinzu:@property (nonatomic) bool modePortraitOnly;
John Stewart
0

Das hat mir perfekt geklappt ....

NSNumber *value = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
Prasanna
quelle
0

2020 Swift 5:

override var supportedInterfaceOrientations:UIInterfaceOrientationMask {
    return .portrait
}
Xys
quelle