iOS 6: Як я обмежую деякі погляди портретом і дозволяю іншим обертатись?


99

У мене є додаток для iPhone, який використовує UINavigationControllerінтерфейс для розгортання: спочатку один вид, потім інший, глибиною до чотирьох рівнів. Я хочу, щоб перші три погляди були обмежені орієнтацією на портрет, і лише останньому виду слід дозволяти обертатись пейзажу. Повертаючись з четвертого виду до третього і четвертого виду, було в ландшафтному орієнтації, я хочу, щоб все поверталося назад до портрету.

У iOS 5 я просто визначав shouldAutorotateToInterfaceOrientation:у кожному з своїх контролерів перегляду, щоб повернути ТАК для допустимих орієнтацій. Все працювало так, як описано вище, включаючи повернення до портретного зображення, навіть якщо пристрій повертався в альбомній орієнтації при поверненні з контролера №4 до №3.

У iOS 6 всі контролери перегляду обертаються до пейзажу, порушуючи ті, які не мали на меті. Ноти до випуску iOS 6 кажуть

Більше відповідальності переноситься на додаток та його делегата. Тепер контейнери для iOS (такі як UINavigationController) не консультуються зі своїми дітьми, щоб визначити, чи слід їх авторотувати. [...] Система запитує найпопулярніший повноекранний контролер перегляду (як правило, кореневий контролер перегляду) щодо підтримуваних орієнтацій інтерфейсу під час обертання пристрою або кожного разу, коли контролер подання представлений у повноекранному модальному стилі подання. Більше того, підтримувані орієнтації отримуються лише у тому випадку, якщо цей контролер подання повертає ТАК зі свого shouldAutorotateметоду. [...] Система визначає, чи підтримується орієнтація шляхом перетину значення, поверненого методом програми, supportedInterfaceOrientationsForWindow:зі значенням, що повертається supportedInterfaceOrientationsметодом найбільшого повноекранного контролера.

Тому я підкласифікував UINavigationController, надав MainNavigationControllerбулеву властивість landscapeOKі використав це для повернення допустимих орієнтацій supportedInterfaceOrientations. Тоді в кожному з моїх viewWillAppear:методів контролерів перегляду я маю такий рядок

    [(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

щоб сказати мою MainNavigationControllerбажану поведінку.

Тут виникає питання: Якщо я зараз перейду до свого четвертого перегляду в портретному режимі і переверну телефон, він повернеться до ландшафту. Тепер я натискаю кнопку назад, щоб повернутися до мого третього виду, який повинен працювати лише портретно. Але воно не обертається назад. Як змусити це зробити?

я намагався

    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]

в viewWillAppearметоді мого третього контролера перегляду, але він нічого не робить. Це неправильний метод для виклику або, можливо, неправильне місце для його виклику чи я повинен реалізовувати все цілком іншим способом?

Відповіді:


95

У мене була та сама проблема, і я знайшов рішення, яке працює для мене. Для того, щоб зробити його роботу, він НЕ досить для реалізації - (NSUInteger)supportedInterfaceOrientationsв вашому UINavigationController. Вам також потрібно реалізувати цей метод у своєму контролері №3, який перший повинен бути портретним лише після з'явлення контролера №4. Отже, у мене в UINavigationController є такий код:

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if (self.isLandscapeOK) {
        // for iPhone, you could also return UIInterfaceOrientationMaskAllButUpsideDown
        return UIInterfaceOrientationMaskAll;
    }
    return UIInterfaceOrientationMaskPortrait;
}

У контролер №3 перегляду додайте наступне:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

Вам не потрібно нічого додавати до своїх контролерів перегляду №1, №2 та №4. Це працює для мене, сподіваюся, це допоможе тобі.


8
ви вирішили щось, що люди намагалися вирішити місяцями! ви заслуговуєте на це велику суму! Я додав підтримуваніInterfaceOrientations до категорії на UIViewController, і вона ідеально працює як типова для тих контролерів, що працюють лише за портретом.
dwery

3
Серед 100 різних відповідей на SO, це той, який справді працює. Спасибі :-)
Крістер Нордвік

16
У мене це налаштовано і досі отримую неправильну орієнтацію, коли переходите НАЗАД від портретного до контролера перегляду, обмеженого лише пейзажем. Це не автоматично повертається до ландшафту - хтось ще його бачить?
Одід Бен Дов

1
що робити, якщо потрібний контролер краєвидного вигляду подається з виступу, а навігаційного контролера немає?
jesses.co.tt

1
Відредагуйте останній, щоб він був NSUInteger як тип повернення.
абсесив

68

Додайте CustomNavigationController

Замініть в ній ці методи:

-(BOOL)shouldAutorotate
{
    return [[self.viewControllers lastObject] shouldAutorotate];
}

-(NSUInteger)supportedInterfaceOrientations
{
    return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}

Тепер додайте всі орієнтації в пліст

введіть тут опис зображення

У контролер подання додайте лише необхідні:

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

ці методи переосмислюють методи керування навігацією


5
Ви справді прибили це цією відповіддю! Дуже дякую. Ви дійсно багато допомогли. Це була для мене велика проблема протягом певного часу. Чудова робота.
K2Digital

Дякую! Це знадобило мені стільки часу і енергії, щоб розібратися!
bkbeachlabs

Ви рятуєте життя .... Це працює і в моєму додатку. Насправді те, що я хочу у своїй програмі, це те, що я маю всі ViewControllerв режимі «Портрет», а мій ViewController- у режимі «Пейзаж та портрет». Я також надаю підтримку як в iOS 5.0, так і в iOS 6.0. Тому я заплутався в роботі, але це чудове рішення. я додаю CustomNavigationController у свій контролер кореневого перегляду та надаю підтримку відповідно до моїх потреб. Ще раз дякую
Bhavin_m

Фантастичний. Саме те, що мені було потрібно. Дякую.
mszaro

що робити, якщо потрібний контролер краєвидного вигляду подається з виступу, а навігаційного контролера немає?
jesses.co.tt

9

Переглянувши кожну відповідь у незліченних подібних запитаннях про SO, жодна з відповідей не працювала для мене, але вони дали мені кілька ідей. Ось як я вирішив проблему:

По-перше, переконайтеся, що підтримувані орієнтації інтерфейсу в цільовому проекті містять усі орієнтації, необхідні для вашого обертового подання.

введіть тут опис зображення

Далі, складіть категорію UINavigationController(оскільки Apple каже, що не підкласифікувати):

@implementation UINavigationController (iOS6AutorotationFix)

-(BOOL)shouldAutorotate {
    return [self.topViewController shouldAutorotate];
}

@end

Імпортуйте ту категорію та контролер перегляду, який ви хочете мати можливість обертати (до якого я зателефоную RotatingViewController), на ваш контролер вищого рівня, який повинен містити ваш навігаційний контролер. У цьому контролері подання виконайте shouldAutorotateнаступне. Зауважте, що це не повинен бути той самий контролер виду, який потрібно обертати.

-(BOOL)shouldAutorotate {

    BOOL shouldRotate = NO;

    if ([navigationController.topViewController isMemberOfClass:[RotatingViewController class]] ) {
        shouldRotate = [navigationController.topViewController shouldAutorotate];
    }

    return shouldRotate;
}

Нарешті, у вашій RotatingViewController, здійснювати shouldAutorotateі supportedInterfaceOrientationsтаким чином :

-(BOOL)shouldAutorotate {
    // Preparations to rotate view go here
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAllButUpsideDown; // or however you want to rotate
}

Причина, що вам потрібно це зробити, полягає в тому, що iOS 6 дає керування обертанням контролеру кореневого виду замість контролера подання зверху. Якщо ви хочете, щоб обертання окремого представлення поводилося інакше, ніж інші погляди в стеці, вам потрібно написати конкретний випадок для цього в контролері кореневого перегляду.


2
Це відмінна відповідь, яка заслуговує більшої любові. Ось інформація, яку вона містить, яку я не могла знайти більше ніде, і яка є критичною: Якщо ви хочете, щоб обертання окремого представлення поводилось інакше, ніж інші погляди в стеку, вам потрібно написати конкретний випадок для нього в корені диспетчер перегляду
шмім

Я вважаю, що це спрацює, якщо у вас є початковий контролер навігаційного керування в якості початкового контролера, але що робити з випадком, коли початковий контролер просто ViewController?
Качка

4

У мене недостатньо репутації, щоб коментувати відповідь @ Брайана, тому я додам свою замітку тут.

Брайан зазначив, що iOS6 дає контроль обертання rootViewController - це може бути не лише UINavigationController, як згадувалося, але й UITabBarController, який він був для мене. Моя структура виглядає так:

  • UITabBarController
    • UINavigationController
      • UIViewControllers ...
    • UINavigationController
      • UIViewControllers ...

Тому я додав методи спочатку у користувацький UITabBarController, потім у користувацький UINavigationController, а потім нарешті у певний UIViewController.

Приклад з UITabBarController та UINavigationController:

- (BOOL)shouldAutorotate {
    return [self.viewControllers.lastObject shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.viewControllers.lastObject supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.viewControllers.lastObject shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.viewControllers.lastObject preferredInterfaceOrientationForPresentation];
}

Я фактично використовував UITabBarController в якості змінної примірника в моєму контролері кореневого перегляду, тому що ви не повинні підклас UITabBarController у версіях iOS до 6.0, і мені потрібно було підтримати назад до iOS 5.0.
Брайан

@micmdk У мене така ж проблема. будь ласка, порадьте, як це вирішити, я скопіював ваш код і помістив у користувальницький контролер навігації та customUabbarController та встановив інший контролер подання як Керований Брайан. але все ще не отримує фіксований контролер перегляду в портреті
Рам S

@Brian У мене така ж проблема. будь ласка, порадьте, як це вирішити, я скопіював ваш код і помістив у користувальницький навігаційний контролер та customUabbarController та встановив інший контролер перегляду як керований Micmdk. але все ще не отримується фіксований контролер перегляду в портреті на ios8.
Рам S

3

Я хотів би дати часткову відповідь на власне запитання. Я знайшов для роботи наступний рядок коду, який використовується в viewWillAppearмоєму третьому методі UIViewController:

[[UIDevice currentDevice] 
      performSelector:NSSelectorFromString(@"setOrientation:") 
           withObject:(id)UIInterfaceOrientationPortrait];

Але мені не дуже подобається таке рішення. Використовуючи хитрість, потрібно призначити властивість лише для читання, яка згідно документації Apple представляє фізичну орієнтацію пристрою. Це як сказати iPhone перейти до правильної орієнтації в руці користувача.

Я дуже спокушаюсь залишити це у своєму додатку, оскільки це просто працює. Але це не так, тому я хотів би залишити питання відкритим для чистого рішення.


4
Ваш додаток, швидше за все, буде відхилено, якщо ви залишите його там. Я також шукаю рішення для цього, але виглядає, що це неможливо. Все, що я міг знайти, - це відповідь на це запитання: stackoverflow.com/questions/7196300/… це щось працює, але чомусь він порушує керування сенсорними кнопками в одному з моїх контролерів
Lope

Я ризикнув і подав Apple. Ще чекаю на огляд ... Я буду тримати вас у курсі.
Харальд Бегехолц

Apple тільки що схвалила моє додаток, коли цей хак був на місці, так! Я сподіваюся, що він не матиме негативної реакції на майбутнє оновлення iOS. Тим часом я все ще відкритий для пропозицій щодо чистого рішення!
Харальд Бьогехольц

1
@ HaraldBögeholz Програма Grt. але коли я працюю в новому xCode з ARC, ніж я не можу використовувати ans. я отримую "Cast NSInteger (він же int) для ідентифікатора заборонений. ARC PerformSelector може спричинити витік, оскільки його селектор невідомий", як я можу це вирішити? я дуже вам вдячний, якщо ми отримали рішення. Коли я вимикаю ARC для perticular View, ніж аварія програми через деякі дані про витік. Будь ласка,
підкажіть

4
Використання приватного API НІКОЛИ не рішення.
Хав'єр Сото

2

Це я використовую для підтримки орієнтації в ios 6.0

-(BOOL)shouldAutorotate{
    return YES;
}  

 - (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskAll;
}


- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{  
  return UIInterfaceOrientationPortrait;
}

1

Будучи, що це дуже переглянута нитка. Я думав, що додам те, що, на мою думку, найлегша відповідь. Це працює для ios8 і вище

-(BOOL)shouldAutorotate
{
    return YES;
}

і

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

Це воно. Насолоджуйтесь!

О, і мої ViewControllers вбудовані в навігаційний контролер, який мені не потрібно було підкласифікувати чи налаштовувати.


0

Це може працювати не для всіх, але це чудово працює для мене. Замість реалізації ...

[(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

зважаючи на те, що з'явиться у всіх моїх контролерах, я вирішив централізувати цей процес всередині мого підкласу UINavigationController, замінивши метод UINavigationControllerDelegatenavigationController:willShowViewController:animated:

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {

    self.previousVCLandscapeOK = self.isLandscapeOK; // Store the current VC's orientation preference before pushing on the new VC so we can set this again from within the custom "back" method
    self.isLandscapeOK = NO; // Set NO as default for all VC's
    if ([viewController isKindOfClass:[YourViewController class]]) {
        self.isLandscapeOK = YES;
    }
}

Я виявив, що цей метод делегата не викликається, коли вискакує VC зі складу стека nav. Це не було проблемою для мене, тому що я обробляю функцію назад з мого підкласу UINavigationController, щоб я міг встановити належні кнопки навігації та дії для конкретних VC, як це ...

if ([viewController isKindOfClass:[ShareViewController class]]) {

    UIButton* backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 57, 30)];
    [backButton setImage:[UIImage imageNamed:@"back-arrow"] forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem* backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    viewController.navigationItem.leftBarButtonItem = backButtonItem;

    UIImageView* shareTitle = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"share-title"]];
    [shareTitle setContentMode:UIViewContentModeScaleAspectFit];
    [shareTitle setFrame:CGRectMake(0, 0, shareTitle.frame.size.width - 10, shareTitle.frame.size.height - 10)];
    viewController.navigationItem.titleView = shareTitle;

} else if(...) {
    ...
}

Ось як backвиглядає мій метод, щоб обробити вискоку VC зі стека та встановити відповідні переваги обертання ...

- (void)back {
    self.isLandscapeOK = self.previousVCLandscapeOK;
    self.previousVCLandscapeOK = NO;
    [self popViewControllerAnimated:YES];
}

Отже, як ви бачите, в основному все, що відбувається, це спершу встановити мені два властивості ...

@property (nonatomic) BOOL isLandscapeOK;
@property (nonatomic) BOOL previousVCLandscapeOK;

в navigationController:willShowViewController:animated:якому буде визначено, які підтримувані орієнтації знаходяться в межах того ж ВК, який збирається представити. Коли з'являється VC, викликається мій користувальницький метод "назад", і тоді я встановлюю isLandscapeOK на те, що було збережено за допомогою попереднього значення VCLandscapeOK.

Як я вже говорив, це може працювати не для всіх, але це чудово працює для мене, і мені не потрібно турбуватися про додавання коду до кожного з моїх контролерів перегляду, мені вдалося зберегти все це централізовано в підкласі UINavigationController.

Сподіваюсь, це допомагає комусь, як і я. Спасибі, Джеремі.



0

Ви хочете змусити портрет програми iOS 6 лише тоді ви можете додати до підкласу UIViewController нижче методи

- (BOOL)shouldAutorotate {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return YES;
    } else {
        return NO;
    }
}


- (NSUInteger)supportedInterfaceOrientations {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return UIInterfaceOrientationMaskAll;
    } else {
        return UIInterfaceOrientationMaskPortrait;
    }
}

2
Проблема полягає в тому, що, якщо цей погляд не є кореневим видом (про що він не в цьому питанні), ви не повинні викликатиAutorotate.
Брайан

з будь-якої причини тип повернення NSUIntegerкидає попередження, незважаючи на те, що він є правильним типом var. якщо ви UIInterfaceOrientationMaskзамість цього використовуєте OCD .
Кріс Дж

Крім того, це рішення прекрасно працювало для контролера модального перегляду (перегляду камери) у мене, не маючи потреби в підкласифікації UINavigationController. Я здогадуюсь, модалі розглядаються незалежно. Не стосується оригінального питання, але корисна інформація, щоб мати розумний дизайн.
Кріс Дж

0

Я хотів, щоб усі мої відеомагнітофони були заблоковані до портретної орієнтації, крім одного. Це те, що працювало для мене.

  1. Додайте підтримку всіх орієнтацій у файл plist.
  2. У контролері кореневого перегляду виявіть тип контролера перегляду, який знаходиться у верхній частині вікна, і відповідно встановіть орієнтацію програми у підтримуваному методіInterfaceOrientations. Наприклад, мені потрібен додаток, щоб обертатися лише тоді, коли веб-перегляд знаходився на вершині стека. Ось що я додав у свій rootVC:

    -(NSUInteger)supportedInterfaceOrientations
    {
        UIViewController *topMostViewController = [[Utils getAppDelegate] appNavigationController].topViewController;
        if ([topMostViewController isKindOfClass:[SVWebViewController class]]) {
            return UIInterfaceOrientationMaskAllButUpsideDown;
        }
        return UIInterfaceOrientationMaskPortrait;
    }

Трунал. Це кілька місяців, але чи можете ви детальніше розповісти про те, як ви визначаєте тип контролера перегляду, який знаходиться на вершині. Зокрема, я не впевнений, що ви тут збираєтеся [[Utils getAppDelegate] appNavigationController]. Утиліти я здогадуюсь, який клас у вас є, але я втрачаю те, що ви робите в ньому. Будь-яка допомога була б чудовою!
Бен

0

У мене недостатньо репутації, щоб відповісти на питання @Ram S під відповіді @micmdk, тому я додам свою замітку тут.

Коли ви використовуєте UITabbarController , спробуйте змінити self.viewControllers.lastObject у коді @ micmdk на self.selectedViewController, як це:

- (BOOL)shouldAutorotate {
    return [self.selectedViewController shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.selectedViewController supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.selectedViewController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}

0

Ви можете реалізовувати та переосмислювати папкаAArorotate () та підтримувануInterfaceOrientations var у всіх своїх класах контролерів перегляду, які повинні бути представлені в інших орієнтаціях, ніж ті, які визначені в PLIST програми.

Однак у нетривіальному користувальницькому інтерфейсі ви можете зіткнутися з проблемою, щоб додати його в десятках класів, і ви не хочете робити всі підкласи з декількох загальних, що його підтримують (MyBaseTableViewController, MyBaseNavigationController і MyBaseTabBarController).

Оскільки ви не можете безпосередньо замінити цей метод / var на UIViewController, ви можете зробити це на його підкласах, які типово базові ваші класи, такі як UITableViewController, UINavigationController і UITabBarController.

Таким чином, ви можете реалізувати кілька розширень і все-таки налаштувати MyPreciousViewController для показу в інших орієнтаціях, ніж усі інші, як цей фрагмент коду Swift 4:

extension UITableViewController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    if let last = self.navigationController?.childViewControllers.last,
        last != self {
            return last.supportedInterfaceOrientations
    } else {
        return [.portrait]
    }
    }
}

extension MyPreciousViewController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    return [.portrait,.landscape]
    }
}


extension UINavigationController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        return [.portrait]
    }
}


extension UITabBarController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        return [.portrait]
    }
}

0

Я вирішив таке ж питання.

Якщо ви використовуєте UINavigationControllerдля натискання контролерів перегляду, вам слід встановити методи нижче.

extension UINavigationController{

    override open var shouldAutorotate: Bool {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return true
        }
        return false
    }

    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return .portrait
        }
        return .landscapeRight

    }
    override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return .portrait
        }
        return .landscapeRight
    }
}

Замість LoginViewControllerвикористання, яке UIViewControllerви хочете показати. У моєму випадку, я хочу , щоб показати LoginViewControllerв Portraitрежимі одного ViewControllersв landscapeрежимі.


-1

Будь ласка, використовуйте наступний метод для вирішення цієї проблеми

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

поверніть лише потрібну орієнтацію !!!


3
Я це робив, як писав. Це запобігає обертанню мого контролера зору, але він не примушує його повертатися до єдиної підтримуваної орієнтації, коли я повертаюся до нього.
Харальд Бегехольц

Це не працює для мене. Навіть включивши це до мого контролера перегляду, подання все одно обертається. Що дає?
shim

навіщо підтримувати maxfiresolutions? Це працювало для мене, і той самий метод, поданий @Flo, має багато відгуків.
ViruMax
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.