Подтвердить что ты не робот

Альтернативные макеты iOS для портрета и пейзажа, используя только один .xib файл

Используя построитель интерфейса в xcode и только один .xib файл, как я могу создавать альтернативные макеты при повороте между пейзажной и портретной ориентациями?

См. диаграмму различных макетов enter image description here

N.b. зеленый вид/область будет содержать 3 объекта, плавающие горизонтально по ландшафту, и в портрете эти 3 элемента будут проходить вертикально в пределах зеленого вида/области.

4b9b3361

Ответ 1

Способ сделать это состоит в том, чтобы иметь три представления в вашем .xib файле. Первый из них - это обычный вид вашего Viewcontroller без подсмотров.

Затем вы создаете представления для портретного и ландшафтного, как вам нужно. Все три должны быть представлениями на уровне корня (посмотрите на скриншот)

enter image description here

В вашем Viewcontroller создайте 2 IBOutlets, один для портрета и один для пейзажного вида, и соедините их с соответствующими представлениями в построителе интерфейса:

IBOutlet UIView *_portraitView;
IBOutlet UIView *_landscapeView;
UIView *_currentView;

Третий вид _currentView необходим для отслеживания того, какое из этих видов отображается в настоящее время. Затем создайте новую функцию следующим образом:

-(void)setUpViewForOrientation:(UIInterfaceOrientation)orientation
{
    [_currentView removeFromSuperview];
    if(UIInterfaceOrientationIsLandscape(orientation))
    {
        [self.view addSubview:_landscapeView];
        _currentView = _landscapeView;
    }
    else
    {
        [self.view addSubview:_portraitView];
        _currentView = _portraitView;
    }
}

Вам нужно будет вызвать эту функцию из двух разных мест, сначала для инициализации:

-(void)viewDidLoad
{
    [super viewDidLoad];
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    [self setUpViewForOrientation:interfaceOrientation];
}

И второй для изменений ориентации:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self setUpViewForOrientation:toInterfaceOrientation];
}

Надеюсь, что вам поможет!

Ответ 2

Нет никакого автоматического способа поддержать это, поскольку он против дизайна Apple. У вас должен быть один ViewController, поддерживающий обе ориентации.

Но если вы действительно этого хотите, вам нужно перезагрузить xib в событиях ротации и загрузить разные файлы nib.

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [[NSBundle mainBundle] loadNibNamed:[self nibNameForInterfaceOrientation:toInterfaceOrientation] owner:self options:nil];
    [self viewDidLoad];
}

- (NSString*) nibNameForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    NSString *postfix = (UIInterfaceOrientationIsLandscape(interfaceOrientation)) ? @"portrait" : @"landscape";
    return [NSString stringWithFormat:@"%@-%@", NSStringFromClass([self class]), postfix];
}

И вы создаете два файла nib, пост-фиксированных с "пейзажем" и "портретным".

Ответ 3

Мне нравится решение @MeXx, но у него есть накладные расходы на сохранение двух разных иерархий представлений в памяти. Кроме того, если какое-либо подзаголовок имеет состояние (например, цвет), которое изменяется, вам нужно сопоставить это, когда вы переключаете иерархии.

Другим решением может быть использование автоматической компоновки и замена ограничений для каждого поднабора для каждой ориентации. Это будет работать лучше всего, если у вас есть сопоставление 1:1 между подзонами в обеих ориентациях.

Понятно, что вы хотите использовать IB для визуального определения макета для каждой ориентации. По моему плану вам нужно будет делать то, что предписывает @MeXx, но затем создать механизм для хранения обоих наборов ограничений после загрузки nib (awakeFromNib) и повторного применения правильного набора в макете (viewWillLayoutSubviews). Вы можете выбросить вторичную иерархию представлений, как только вы очистите и сохраните свои ограничения. (Поскольку ограничения зависят от вида, вы, вероятно, будете создавать новые ограничения для применения к реальным областям).

Извините, у меня нет кода. Это просто план на этом этапе.

Заключительная записка - все это будет проще с xib, чем с раскадрой, поскольку в раскадровке больно описывать представления, которые живут вне основного представления контроллера представления (что желательно, поскольку в противном случае его можно редактировать PITA). Blach!

Ответ 4

Я могу сэр, конечно...

Мне приходилось делать это вручную, когда устройство вращается

Когда устройство вращается, - (void)viewWillLayoutSubviews получает вызов

для примера - возьмите UIButton *button в UIView,

- (void)viewWillLayoutSubviews
   {
       if (UIDeviceOrientationIsLandscape([self.view interfaceOrientation]))
       {
         //x,y as you want
         [ button setFrame:CGRectMake:(x,y,button.width,button.height)];

       }
       else
       {
         //In potrait
          //x,y as you want
         [ button setFrame:CGRectMake:(x,y,button.width,button.height)];


       }

   }

Таким образом, вы можете разместить его по своему усмотрению. Благодаря

Пожалуйста, посмотрите на эти изображения

Первое изображение моего xCode XIB UIView, которое я хочу на обоих экранах

enter image description here

Теперь, когда я хочу посмотреть этот вид в ландшафте, я решил изменить свой -(void)viewWillLayoutSubviews

enter image description here

Теперь результат выглядит следующим образом: Первое изображение экрана Potrait в Simulator

enter image description here

и это находится в Landscape

enter image description here

Ответ 5

Решение с раскадрой [ios7]. Внутри главного контроллера представления есть вид контейнера, который может включать контроллер панели вкладок. Внутри контроллера панели вкладок имеется 2 вкладки. Одна вкладка предназначена для контроллера с портретом, а другая для контроллера в ландшафтном режиме.

Контроллер главного представления реализует следующие функции:

#define INTERFACE_ORIENTATION() ([[UIApplication sharedApplication]statusBarOrientation])

@interface MainViewController ()
@property(nonatomic,weak) UITabBarController *embeddedTabBarController;
@end

@implementation MainViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"embedContainer"])
    {
        self.embeddedTabBarController = segue.destinationViewController;
        [self.embeddedTabBarController.tabBar setHidden:YES];
    }
}

- (void) adaptToOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
    {
        [self.embeddedTabBarController setSelectedIndex:1];
    }
    else
    {
        [self.embeddedTabBarController setSelectedIndex:0];
    }
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
    [self adaptToOrientation:interfaceOrientation];    
}

- (void) viewWillAppear:(BOOL)animated
{
    [self adaptToOrientation:INTERFACE_ORIENTATION()];
}

Ссылка segue в раскадровке должна называться "embedContainer".

Убедитесь, что просмотр контейнера изменяет размер, чтобы выполнить родительское представление, в обоих случаях - пейзаж и портрет.

Позаботьтесь о том, что во время выполнения 2 экземпляра одного и того же контроллера живут в одно и то же время.

Storyboard


Обновление: Документы Apple для альтернативного контроллера ландшафтного вида

Ответ 6

Я просто хотел бы добавить новый ответ, чтобы отразить новые возможности в ios8 и XCode 6.

В последнем новом программном обеспечении Apple представила классы размера, которые позволяют раскадровке разумно адаптироваться в зависимости от того, какой размер экрана и ориентация вы используете, Хотя я предлагаю вам взглянуть на документы выше или на сессию WWDC 2014 построить адаптивные приложения с UIKit, я попытаюсь перефразировать.

Убедитесь, что классы размера включены,.

Вы заметите, что ваши файлы раскадровки теперь квадратные. Здесь вы можете настроить базовый интерфейс. Интерфейс будет разумно изменен для всех разрешенных устройств и ориентации.

В нижней части экрана вы увидите кнопку с надписью yAny xAny. Нажимая на это, вы можете изменить только один класс размера, например, пейзаж и портрет.

Я предлагаю вам прочитать документы выше, но я надеюсь, что это поможет вам, поскольку использует только одну раскадровку.

Ответ 7

Вы можете использовать библиотеку GPOrientation. Вот ссылка

Ответ 8

Изменять размер программно

У меня есть приложение, где у меня точно такая же ситуация. В моем NIB я просто располагаю портретом. Другой макет выполняется программно. Не так сложно указать несколько размеров программным способом, поэтому не нужно поддерживать два NIB.

Обратите внимание, что выполнение изменения вида программно путем установки фреймов приведет к анимации (или может быть легко сделана с использованием анимации). Это даст пользователю ценную обратную связь о том, какой компонент перемещен в какую позицию. Если вы просто загрузите второе представление, вы потеряете это преимущество, и с точки зрения пользовательского интерфейса я считаю, что загрузка альтернативного представления не является хорошим решением.

Использовать два ViewControllers

Если вам нравится использовать два ViewControllers, Apple описывает, как это сделать в документации разработчика, то есть вы найдете ответ на свой вопрос здесь: RespondingtoDeviceOrientationChanges.

Если вы хотите сделать это программно, вы можете добавить код перестановки в методе didRotateFromInterfaceOrientation.

Пример

Я добавил метод в свой ViewController:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self adjustLayoutToOrientation];
}

Затем реализована настройка макета. Вот пример (код относится только к моему приложению, но вы можете читать размеры супервидов и вычислять фреймы подзаголовков из него.

- (void)adjustLayoutToOrientation
{
    UIInterfaceOrientation toInterfaceOrientation = self.interfaceOrientation;
    bool isIPhone = !([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);

    [self adjustViewHeight];
    [self adjustSubviewsToFrame: [mailTemplateBody.superview.superview frame]];

    CGRect pickerViewWrapperFrame = pickerViewWrapper.frame;
    if(isIPhone) {
        pickerViewWrapperFrame.origin.x = 0;
        pickerViewWrapperFrame.size.height = keyboardHeight;
        pickerViewWrapperFrame.origin.y = [self view].frame.size.height-pickerViewWrapperFrame.size.height;
        pickerViewWrapperFrame.size.width = [self view].frame.size.width;
    }
    else {
        pickerViewWrapperFrame = pickerView.frame;
    }

// MORE ADJUSTMENTS HERE

}