Jak wymusić orientację kontrolera widoku w iOS 8?

149

Przed iOS 8 używaliśmy poniższego kodu w połączeniu z metodami supportedInterfaceOrientations i shouldAutoRotate , aby wymusić orientację aplikacji na dowolną konkretną orientację. Użyłem poniższego fragmentu kodu, aby programowo obrócić aplikację do żądanej orientacji. Po pierwsze, zmieniam orientację paska stanu. A następnie samo przedstawienie i natychmiastowe odrzucenie widoku modalnego powoduje obrócenie widoku do żądanej orientacji.

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];

Ale to zawodzi w iOS 8. Ponadto widziałem kilka odpowiedzi w przepełnieniu stosu, gdzie ludzie sugerowali, że powinniśmy zawsze unikać tego podejścia od iOS 8.

Mówiąc dokładniej, moja aplikacja jest aplikacją uniwersalną. W sumie są trzy kontrolery.

  1. Kontroler pierwszego widoku - powinien obsługiwać wszystkie orientacje na iPadzie i tylko portret (przycisk ekranu głównego w dół) na iPhonie.

  2. Kontroler Second View - powinien obsługiwać tylko krajobraz we wszystkich warunkach

  3. Kontroler Third View - powinien obsługiwać tylko krajobraz w każdych warunkach

Używamy kontrolera nawigacji do nawigacji po stronach. Z pierwszego kontrolera widoku, po kliknięciu przycisku, przesuwamy drugi na stos. Tak więc, gdy nadejdzie drugi kontroler widoku, niezależnie od orientacji urządzenia, aplikacja powinna zablokować się tylko w orientacji poziomej.

Poniżej znajduje się moje shouldAutorotatei supportedInterfaceOrientationsmetody w kontrolerze drugiego i trzeciego widoku.

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
    return NO;
}

Czy jest jakieś rozwiązanie dla tego lub lepszego sposobu blokowania kontrolera widoku w określonej orientacji dla iOS 8. Proszę o pomoc!

Rashmi Ranjan Mallick
źródło
Implementacja metod, o których wspomniałeś w przedstawionym VC, generalnie powinna zadziałać (przynajmniej takie mam doświadczenie w iOS 8). Być może Twoja konkretna konfiguracja powoduje problemy?
Vladimir Gritsenko
Być może mogę trochę wyjaśnić to pytanie. Zmienię nieznacznie pytanie.
Rashmi Ranjan Mallick
@VladimirGritsenko: Sprawdź teraz. Edytowałem to.
Rashmi Ranjan Mallick
Kod w pytaniu nie korzysta ze stosu kontrolera nawigacji, ale raczej z prezentacji modalnej, więc nadal nie jest jasne, co dokładnie robisz. Powiem, że w naszym kodzie zwracanie YES z shouldAutoRotate i zwracanie żądanych orientacji z supportedInterfaceOrientations w przedstawionym VC odpowiednio orientuje to VC.
Vladimir Gritsenko
Cóż, to naprawdę nie jest porażka, to po prostu duża zmiana koncepcji. Czy to dobra zmiana, to zupełnie inny temat.
Kegluneq,

Odpowiedzi:

315

IOS 7–10:

Cel C:

[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];

Swift 3:

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

Po prostu wywołaj to - viewDidAppear:z przedstawionego kontrolera widoku.

Sunny Shah
źródło
61
To nie jest prywatny interfejs API. To jest niezabezpieczone użycie interfejsów API. Nie używasz żadnych prywatnych metod, ale używasz „wewnętrznych rzeczy”. Jeśli Apple zmieni wartość „orientacji”, nie powiedzie się. Gorzej: jeśli Apple zmieni znaczenie wartości „orientacji”, nie wiesz, co zmieniasz, ustawiając wartość na sztywno.
sonxurxo
4
Aby wyłączyć animację, umieścić [UIView setAnimationsEnabled:NO];w viewWillAppeari [UIView setAnimationsEnabled:YES];w viewDidAppear, related: stackoverflow.com/a/6292506/88597
ohho
3
Niezły hack, ale problem z tym polega na tym, że nie odpala didRotateFromInterfaceOrientation po ustawieniu nowej wartości orientacji (westchnienie)
loretoparisi
2
W iOS 9.2 to tylko zmienia orientację, ale nie blokuje.
Mark13426
2
Dla każdego, kto zachowuje się dobrze w 90% przypadków, a przez pozostałe 10% buggy, zadzwoń do tego po ustawieniu klucza orientacji:[UINavigationController attemptRotationToDeviceOrientation];
Albert Renshaw
93

Obrót orientacji jest nieco bardziej skomplikowany, jeśli znajdujesz się wewnątrz UINavigationControllerlub UITabBarController. Problem polega na tym, że jeśli kontroler widoku jest osadzony w jednym z tych kontrolerów, nawigacja lub kontroler paska kart ma pierwszeństwo i podejmuje decyzje o autorotacji i obsługiwanych orientacjach.

Używam następujących 2 rozszerzeń w UINavigationController i UITabBarController, aby kontrolery widoku, które są osadzone w jednym z tych kontrolerów, mogły podejmować decyzje.

Daj kontrolerom widoku moc!

Swift 2.3

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> Int {
        return visibleViewController.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

extension UITabBarController {
    public override func supportedInterfaceOrientations() -> Int {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Szybki 3

extension UINavigationController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
    }
}

extension UITabBarController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate
        }
        return super.shouldAutorotate
    }
}

Teraz możesz nadpisać supportedInterfaceOrientationsmetodę lub możesz zastąpić shouldAutoRotatew kontrolerze widoku, który chcesz zablokować, w przeciwnym razie możesz pominąć nadpisania w innych kontrolerach widoku, które chcesz odziedziczyć domyślne zachowanie orientacji określone w pliku plist aplikacji

Wyłącz rotację

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

Zablokuj do określonej orientacji

class ViewController: UIViewController {
    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
}

Teoretycznie powinno to działać dla wszystkich złożonych hierarchii kontrolerów widoku, ale zauważyłem problem z UITabBarController. Z jakiegoś powodu chce użyć domyślnej wartości orientacji. Zobacz następujący post na blogu, jeśli chcesz dowiedzieć się, jak obejść niektóre problemy:

Zablokuj obrót ekranu

Korey Hinton
źródło
1
Wiem, że to bardzo stary post, ale gdzie umieściłbyś kod rozszerzenia?
dwinnbrown
7
Zaznaczanie w dół dla szybkiego tylko odpowiedzi na pytanie o celu C.
Charlie Scott-Skinner
17
To fajne rozwiązanie dla Swift i ObjC. Nie rozumiem, dlaczego ludzie odrzucają głosowanie. Masz pomysł, a następnie napisanie kodu jest łatwe. Dlaczego narzekać?
Zhao
2
@Krodak, w rozszerzeniach spróbuj zmienić „-> Int” na „-> UIInterfaceOrientationMask”. Zrobił dla mnie sztuczkę.
the_pantless_coder
2
Pomysł i kod są doskonałe, uwielbiam to, ale pojawia się problem podczas używania UINavigationController i powrotu z UIViewController wyświetlanego w poziomie do takiego, który musi być wyświetlany w pionie. Jakieś pomysły?
kryzysGriega
24

Oto, co zadziałało dla mnie:

https://developer.apple.com/library//ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/clm/UIViewController/attemptRotationToDeviceOrientation

Nazwij to w swojej metodzie viewDidAppear:.

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [UIViewController attemptRotationToDeviceOrientation];
}
Vincil Bishop
źródło
3
Bardzo przydatne, jeśli urządzenie jest już obrócone i musisz obrócić kontroler widoku
avdyushin
2
Świetna odpowiedź. W połączeniu z zaakceptowaną odpowiedzią działa to dobrze za każdym razem w Swift i iOS 9+.
AndyDunn
1
Chociaż myślę, że to nie jest odpowiedź na zadane pytanie, ale naprawdę mi pomogło.
Abdurrahman Mubeen Ali
21

Odkryłem, że jeśli jest to kontroler widoku prezentowanego, możesz go przesłonić preferredInterfaceOrientationForPresentation

Szybki:

override func supportedInterfaceOrientations() -> Int {
  return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
  return UIInterfaceOrientation.LandscapeLeft
}

override func shouldAutorotate() -> Bool {
  return false
}
Zipme
źródło
4
Wydaje się, że przyjęta odpowiedź po prostu powoduje zmianę orientacji na poziomą. Pokazałeś tutaj, jak zmusić kontroler widoku, aby był tylko w poziomie i nie wpływał na kontroler widoku prezentacji (co jest dokładnie tym, czego potrzebuję). Dzięki.
Clifton Labrum
1
@CliftonLabrum czy zrobiłeś coś innego, aby wymusić tylko krajobraz? Używając tego samego kodu, kontroler widoku nadal może obracać się do innej orientacji.
Crashalot
Później zdałem sobie sprawę, że - masz rację. Nie znalazłem jeszcze rozwiązania.
Clifton Labrum
1
To zadziałało dla mnie. Udało mi się utrzymać pojedynczy kontroler widoku w trybie portretowym, używając tego kodu (zastępując krajobraz z portretem). +1
Mark Barrasso
Tak! To działało również dla mnie w Xcode 7.3 i Swift 2.2. Ale nadal trzeba zdefiniować aplikację func dla supportedInterfaceOrientationsForWindow w AppDelegate.
charles.cc.hsu
15

W ten sposób działa dla mnie w Swift 2 iOS 8.x:

PS (ta metoda nie wymaga nadpisywania funkcji orientacji, takich jak shouldautorotate na każdym viewController, tylko jedna metoda w AppDelegate)

Zaznacz „Wymaga pełnego ekranu” w ogólnych informacjach o projekcie. wprowadź opis obrazu tutaj

Tak więc w AppDelegate.swift utwórz zmienną:

var enableAllOrientation = false

Więc umieść również tę funkcję:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
        if (enableAllOrientation == true){
            return UIInterfaceOrientationMask.All
        }
        return UIInterfaceOrientationMask.Portrait
}

Tak więc w każdej klasie w projekcie możesz ustawić tę zmienną w viewWillAppear:

override func viewWillAppear(animated: Bool)
{
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        appDelegate.enableAllOrientation = true
}

Jeśli musisz dokonać wyboru na podstawie typu urządzenia, możesz to zrobić:

override func viewWillAppear(animated: Bool)
    {
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        switch UIDevice.currentDevice().userInterfaceIdiom {
        case .Phone:
        // It's an iPhone
           print(" - Only portrait mode to iPhone")
           appDelegate.enableAllOrientation = false
        case .Pad:
        // It's an iPad
           print(" - All orientation mode enabled on iPad")
           appDelegate.enableAllOrientation = true
        case .Unspecified:
        // Uh, oh! What could it be?
           appDelegate.enableAllOrientation = false
        }
    }
Alessandro Ornano
źródło
bardzo ładne wyjaśnienie
Mihir Oza
11

Po pierwsze - to zły pomysł, ogólnie rzecz biorąc, coś jest nie tak z architekturą Twojej aplikacji, ale sh..t happensjeśli tak, możesz spróbować zrobić coś takiego jak poniżej:

final class OrientationController {

    static private (set) var allowedOrientation:UIInterfaceOrientationMask = [.all]

    // MARK: - Public

    class func lockOrientation(_ orientationIdiom: UIInterfaceOrientationMask) {
        OrientationController.allowedOrientation = [orientationIdiom]
    }

    class func forceLockOrientation(_ orientation: UIInterfaceOrientation) {
        var mask:UIInterfaceOrientationMask = []
        switch orientation {
            case .unknown:
                mask = [.all]
            case .portrait:
                mask = [.portrait]
            case .portraitUpsideDown:
                mask = [.portraitUpsideDown]
            case .landscapeLeft:
                mask = [.landscapeLeft]
            case .landscapeRight:
                mask = [.landscapeRight]
        }

        OrientationController.lockOrientation(mask)

        UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
    }
}

Niż w AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // do stuff
    OrientationController.lockOrientation(.portrait)
    return true
}

// MARK: - Orientation

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return OrientationController.allowedOrientation
}

A kiedy chcesz zmienić orientację, wykonaj następujące czynności:

OrientationController.forceLockOrientation(.landscapeRight)

Uwaga: czasami urządzenie może nie aktualizować się po takim połączeniu, więc może być konieczne wykonanie następujących czynności

OrientationController.forceLockOrientation(.portrait)
OrientationController.forceLockOrientation(.landscapeRight)

To wszystko

gbk
źródło
9

Powinno to działać od iOS 6 i nowszych, ale przetestowałem to tylko na iOS 8. Podklasa UINavigationControlleri nadpisanie następujących metod:

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

- (BOOL)shouldAutorotate {
    return NO;
}

Lub zapytaj widoczny kontroler widoku

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return self.visibleViewController.preferredInterfaceOrientationForPresentation;
}

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

i zastosuj tam metody.

Mojo66
źródło
Właśnie przetestowałem to w mojej aplikacji w Xcode 8.2.1 z systemem iOS 10.0 i działa. Moje menu gry to pierwszy ekran, który się nie obraca; wszystkie inne widoki są obracane. Idealny! Dałem mu jeden. Potrzebowałem tylko funkcji „ferredInterfaceOrientationForPresentation ”podanej przez Mojo66.
Philip Borges,
9

To jest opinia na temat komentarzy w odpowiedzi Sida Shaha , dotyczących wyłączania animacji za pomocą:

[UIView setAnimationsEnabled:enabled];

Kod:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:NO];
    [UIView setAnimationsEnabled:NO];

    // Stackoverflow #26357162 to force orientation
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:NO];
    [UIView setAnimationsEnabled:YES];
}
ohho
źródło
Jak mogę to wykorzystać do portretu.
Muju
6

Jeśli używasz navigationViewController, powinieneś utworzyć własną nadklasę dla tego i nadpisać:

- (BOOL)shouldAutorotate {
  id currentViewController = self.topViewController;

  if ([currentViewController isKindOfClass:[SecondViewController class]])
    return NO;

  return YES;
}

spowoduje to wyłączenie rotacji w SecondViewController, ale jeśli wypchniesz SecondViewController, gdy urządzenie jest w orientacji pionowej, SecondViewController pojawi się w trybie pionowym.

Załóżmy, że używasz scenorysu. Musisz stworzyć ręczne przejście ( jak to zrobić ) w metodzie „onClick”:

- (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender {
  NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
  [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
  [self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];
}

To wymusi orientację poziomą, zanim twoja superklasa wyłączy funkcję automatycznego obracania.

Lau
źródło
6

Na Xcode 8metodach są konwertowane na właściwości, więc następujące działa z Swift:

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIInterfaceOrientation.portrait
}

override public var shouldAutorotate: Bool {
    return true
}
Ali Momen Sani
źródło
Czy to blokuje orientację?
bnussey
1
@bnussey Chciałem zapobiec automatycznemu obracaniu i byciu zawsze w portraittrybie bez względu na orientację na iPadzie, z shouldAutorotatepowrotem do prawdy, osiągnąłem to.
Ali Momen Sani,
4

Wypróbowałem wiele rozwiązań, ale ten, który zadziałał, jest następujący:

Nie ma potrzeby edytowania info.plistw iOS 8 i 9.

- (BOOL) shouldAutorotate {
    return NO;
}   

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);
}

Możliwe orientacje z dokumentacji Apple :

UIInterfaceOrientationUnknown

Nie można określić orientacji urządzenia.

UIInterfaceOrientationPortrait

Urządzenie jest w trybie portretowym, z urządzeniem trzymanym pionowo i przyciskiem Home na dole.

UIInterfaceOrientationPortraitUpsideDown

Urządzenie jest w trybie portretowym, ale do góry nogami, z urządzeniem trzymanym pionowo i przyciskiem Home u góry.

UIInterfaceOrientationLcapesLeft

Urządzenie jest w trybie poziomym, z urządzeniem trzymanym pionowo i przyciskiem Home po lewej stronie.

UIInterfaceOrientationLcapesRight

Urządzenie jest w trybie poziomym, z urządzeniem trzymanym pionowo i przyciskiem Home po prawej stronie.

hoogw
źródło
3

Połączenie odpowiedzi Sids i Koreys zadziałało dla mnie.

Rozszerzenie kontrolera nawigacji:

extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

Następnie wyłączanie obracania w pojedynczym widoku

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

I obracanie do odpowiedniej orientacji przed przejściem

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "SomeSegue")
    {
        let value = UIInterfaceOrientation.Portrait.rawValue;
        UIDevice.currentDevice().setValue(value, forKey: "orientation")
    }
}
Misha
źródło
Czy wiesz, czy może to pomóc w moim problemie, w którym kontroler widoku, który jest wyświetlany, jest przez sekundę w orientacji poziomej, zanim obróci się do pionu, tak jak powinien zawsze? pytanie jest tutaj: stackoverflow.com/questions/30693964/…
dwinnbrown
3

Najlepsze rozwiązanie powyżej:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

nie zadziałało dla mnie, gdy wywołałem go w viewDidAppear prezentowanego kontrolera widoku. Jednak zadziałało, gdy wywołałem go w preparatForSegue w kontrolerze widoku prezentacji.

(Przepraszamy, za mało punktów reputacji, aby skomentować to rozwiązanie, więc musiałem to dodać w ten sposób)

guido
źródło
Mam to działające, ale kiedy przechodzę od kontrolera widoku poziomego do kontrolera widoku pionowego, kontroler widoku, który powinien wyświetlać w pionie, jest krótko pokazany w poziomie, zanim obróci się sam. jeśli ktoś wie, jak pomóc, moje pytanie jest tutaj: stackoverflow.com/questions/30693964/…
dwinnbrown
@dwinnbrown at viewDidAppear, kontroler widoku jest już widoczny. Może zamiast tego spróbuj z viewDidLoad?
Crashalot
@ Crashalot Teraz to naprawiłem. Zobacz odpowiedź, którą oznaczyłem jako poprawną.
dwinnbrown
@dwinnbrown link do twojego pytania jest martwy. aktualizacja? dzięki!
Crashalot
@Crashalot społeczność usunęła go, ale głosowałem za przywróceniem go. Dam ci znać wkrótce
dwinnbrown
3

[iOS9 +] Jeśli ktoś przeciągnął tutaj całą drogę w dół, ponieważ żadne z powyższych rozwiązań nie zadziałało, i jeśli przedstawiasz widok, który chcesz zmienić przez przejście, możesz to sprawdzić.

Sprawdź typ prezentacji swojego segue. Mój był „poza obecnym kontekstem”. Kiedy zmieniłem go na pełny ekran, zadziałało.

Dzięki @GabLeRoux znalazłem to rozwiązanie.

  • Ta zmiana działa tylko w połączeniu z powyższymi rozwiązaniami.
Goh
źródło
2

Moje wymagania są

  1. zablokuj wszystkie widoki w trybie pionowym
  2. użyj AVPlayerViewControllerdo odtwarzania wideo

Podczas odtwarzania wideo, jeśli jest to krajobraz, pozwól ekranowi obracać się poziomo w prawo i poziomo w lewo. Jeśli jest to portret, zablokuj widok tylko w trybie portretowym.

Najpierw zdefiniuj supportedInterfaceOrientationsForWindowwAppDelegate.swift

var portrait = true
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if portrait {
        return .Portrait
    } else {
        return .Landscape
    }
}

Po drugie, w głównym kontrolerze widoku zdefiniuj następujące funkcje

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    print("\(#function)")
    return .Portrait
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return .Portrait
}

override func shouldAutorotate() -> Bool {
    return false
}

Następnie musisz przejść do podklasy AVPlayerViewController

class MyPlayerViewController: AVPlayerViewController {

    var size: CGSize?

    var supportedOrientationMask: UIInterfaceOrientationMask?
    var preferredOrientation: UIInterfaceOrientation?

    override func viewDidLoad() {
        super.viewDidLoad()

        if let size = size {
            if size.width > size.height {
                self.supportedOrientationMask =[.LandscapeLeft,.LandscapeRight]
                self.preferredOrientation =.LandscapeRight
            } else {
                self.supportedOrientationMask =.Portrait
                self.preferredOrientation =.Portrait
            }
        }
    }

Zastąp te trzy funkcje w programie MyPlayerViewController.swift

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return self.supportedOrientationMask!
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return self.preferredOrientation!
}

Ponieważ użytkownik może obrócić urządzenie poziomo w lewo lub w prawo, musimy ustawić automatyczne obracanie

override func shouldAutorotate() -> Bool {
    return true
}

Na koniec utwórz MyPlayerViewControllerwystąpienie w kontrolerze widoku i ustaw wartość rozmiaru właściwości.

let playerViewController = MyPlayerViewController()

// Get the thumbnail  
let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......)

let size = thumbnail?.size
playerViewController.size = size

Zainicjuj swojego gracza odpowiednim videoUrl, a następnie przypisz go do playerViewController. Miłego kodowania!

charles.cc.hsu
źródło
2
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [UIViewController attemptRotationToDeviceOrientation];
}
Roman Solodyashkin
źródło
1

Wydaje się, że nadal trwa debata na temat najlepszego wykonania tego zadania, więc pomyślałem, że podzielę się moim (roboczym) podejściem. Dodaj następujący kod do swojej UIViewControllerimplementacji:

- (void) viewWillAppear:(BOOL)animated
{
    [UIViewController attemptRotationToDeviceOrientation];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

-(BOOL)shouldAutorotate
{
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeLeft;
}

W tym przykładzie będziesz również musiał ustawić dozwolone orientacje urządzeń na „Poziomo po lewej” w ustawieniach projektu (lub bezpośrednio w info.plist). Po prostu zmień konkretną orientację, którą chcesz wymusić, jeśli chcesz czegoś innego niżLandscapeLeft.

Kluczem dla mnie było attemptRotationToDeviceOrientationwezwanie viewWillAppear- bez tego widok nie obracałby się prawidłowo bez fizycznego obracania urządzenia.

ViperMav
źródło
Przestarzała metoda.
Saqib Omer
1

Według Korey Hinton

Swift 2.2:

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return visibleViewController!.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController!.shouldAutorotate()
    }
}


extension UITabBarController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Wyłącz rotację

override func shouldAutorotate() -> Bool {
    return false
}

Zablokuj do określonej orientacji

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}
phnmnn
źródło
1

Wypróbowałem tutaj kilka rozwiązań i ważne jest, aby zrozumieć, że to główny kontroler widoku określi, czy będzie się obracał, czy nie.

Stworzyłem następujący projekt z celem-c github.com/GabLeRoux/RotationLockInTabbedViewChild z działającym przykładem, w TabbedViewControllerktórym jeden widok podrzędny może się obracać, a drugi widok podrzędny jest zablokowany w pionie.

Nie jest doskonały, ale działa i ten sam pomysł powinien działać w przypadku innych rodzajów widoków głównych, takich jak NavigationViewController. :)

Widok dziecka blokuje orientację rodzica

GabLeRoux
źródło
0

Zgodnie z rozwiązaniem pokazanym przez @ sid-sha musisz wszystko umieścić w viewDidAppear:metodzie, inaczej nie dostaniesz didRotateFromInterfaceOrientation:zwolnienia, więc coś takiego:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
        interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
        NSNumber *value = [NSNumber numberWithInt:interfaceOrientation];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    }
}
loretoparisi
źródło
0

Moje rozwiązanie

W AppDelegate:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if let topController = UIViewController.topMostViewController() {
        if topController is XXViewController {
            return [.Portrait, .LandscapeLeft]
        }
    }
    return [.Portrait]
}

XXViewController to ViewController, który chcesz obsługiwać w trybie poziomym.

Wtedy rozwiązanie Sunny Shah działałoby w Twojej XXViewControllerna dowolnej wersji iOS:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

Jest to funkcja narzędziowa do znajdowania najwyższego poziomu ViewController.

extension UIViewController {

    /// Returns the current application's top most view controller.
    public class func topMostViewController() -> UIViewController? {
        let rootViewController = UIApplication.sharedApplication().windows.first?.rootViewController
        return self.topMostViewControllerOfViewController(rootViewController)
    }



    /// Returns the top most view controller from given view controller's stack.
    class func topMostViewControllerOfViewController(viewController: UIViewController?) -> UIViewController? {
        // UITabBarController
        if let tabBarController = viewController as? UITabBarController,
           let selectedViewController = tabBarController.selectedViewController {
            return self.topMostViewControllerOfViewController(selectedViewController)
        }

        // UINavigationController
        if let navigationController = viewController as? UINavigationController,
           let visibleViewController = navigationController.visibleViewController {
            return self.topMostViewControllerOfViewController(visibleViewController)
        }

        // presented view controller
        if let presentedViewController = viewController?.presentedViewController {
            return self.topMostViewControllerOfViewController(presentedViewController)
        }

        // child view controller
        for subview in viewController?.view?.subviews ?? [] {
            if let childViewController = subview.nextResponder() as? UIViewController {
                return self.topMostViewControllerOfViewController(childViewController)
            }
        }

        return viewController
    }

}
duan
źródło
0

Użyj tego, aby zablokować orientację kontrolera widoku, przetestowaną na IOS 9:

// Zablokuj orientację poziomą w prawo

-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController {
    return UIInterfaceOrientationMaskLandscapeRight;
}
robegamesios
źródło
0

Mam ten sam problem i tracę na to tyle czasu. Więc teraz mam swoje rozwiązanie. Moje ustawienie aplikacja jest właśnie wsparcie portret only.However niektóre ekrany do mojego app musi mieć krajobraz only.I naprawić go mieć zmienną isShouldRotate na AppDelegate . I funkcja w AppDelegate :

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
    if isShouldRotate == true {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

I wreszcie, gdy ViewControllerA potrzebuje stanu poziomego. Po prostu zrób to: przed wypchnięciem / prezentacją do ViewControllerA przypisz isShouldRotate do true . Nie zapomnij, kiedy wyskakuje / odrzuca przypisanie kontrolera isShouldRotate do false at viewWillDisappear .

zawietrzny
źródło
0

Dla mnie VC najwyższego poziomu potrzebował zaimplementować zastąpień orientacji. Używanie VC w dół stosu nie przyniesie efektu, jeśli górny VC nie jest implementowany.

VC-main
    |
    -> VC 2
        |
        -> VC 3

Słuchany jest tylko VC-Main, zasadniczo w moich testach.

bduhbya
źródło
0

Wygląda na to, że nawet ty tutaj jest tyle odpowiedzi, że nikt nie był dla mnie wystarczający. Chciałem wymusić orientację, a następnie wrócić do orientacji urządzenia, ale[UIViewController attemptRotationToDeviceOrientation]; po prostu nie zadziałało. To, co również skomplikowało całą sprawę, to to, że dodałem shouldAutorotate do false na podstawie jakiejś odpowiedzi i nie mogłem uzyskać pożądanych efektów, aby poprawnie obrócić z powrotem we wszystkich scenariuszach.

Oto co zrobiłem:

Przed wypchnięciem kontrolera w wywołaniu w jego konstruktorze init:

_userOrientation = UIDevice.currentDevice.orientation;
[UIDevice.currentDevice setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
[self addNotificationCenterObserver:@selector(rotated:)
                               name:UIDeviceOrientationDidChangeNotification];

Zapisuję więc ostatnią orientację urządzenia i rejestruję się na zdarzenie zmiany orientacji. Zdarzenie zmiany orientacji jest proste:

- (void)rotated:(NSNotification*)notification {
    _userOrientation = UIDevice.currentDevice.orientation;
}

I na widok odrzucenia po prostu wymuszam powrót do dowolnej orientacji, którą mam jako użytkownik.

- (void)onViewDismissing {
    super.onViewDismissing;
    [UIDevice.currentDevice setValue:@(_userOrientation) forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
}

I to też musi tam być:

- (BOOL)shouldAutorotate {
    return true;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

A także kontroler nawigacji musi delegować do shouldAutorotate i obsługiwaneInterfaceOrientations, ale wierzę, że większość ludzi już wie.

PS: Przepraszam, używam niektórych rozszerzeń i klas bazowych, ale nazwy są dość znaczące, więc koncepcja jest zrozumiała, stworzy jeszcze więcej rozszerzeń, ponieważ teraz nie jest zbyt ładna.

Renetik
źródło