Auto Layout iOS 7 - невозможно выровнять право подвида справа от UIScrollView

Я программно создаю представление и использую автомакет, вообще не создаю интерфейс. В пользовательском контроллере ScrollView я добавляю UILabel и UIButton в качестве подвидов. Я хочу выровнять метку по левому краю экрана, а кнопку по правому краю экрана. По какой-то причине моя кнопка выравнивается только слева от моего прокрутки. Я урезал свой код, так что это только эти две метки, и я не могу понять, почему он не будет выравниваться по правому краю.

HWScrollViewController.m (как я инициализирую основной вид прокрутки)

- (void)loadView
{
    self.scrollView = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];

    self.scrollView.delegate = self;

    self.view = self.scrollView;
}

HWListingDetailViewController.m

- (void)viewDidLoad
{
[super viewDidLoad];

UILabel *priceLabel = [[UILabel alloc] init];
UIButton *favouriteButton = [UIButton buttonWithType:UIButtonTypeContactAdd];

[priceLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[favouriteButton setTranslatesAutoresizingMaskIntoConstraints:NO];

[priceLabel setText:@"$125.00"];
[favouriteButton setTitle:@"Add to Favourites" forState:UIControlStateNormal];

[self.view addSubview:priceLabel];
[self.view addSubview:favouriteButton];

[self.view addConstraints:@[
    [NSLayoutConstraint constraintWithItem:priceLabel 
                                 attribute:NSLayoutAttributeCenterY
                                 relatedBy:NSLayoutRelationEqual 
                                    toItem:self.view 
                                 attribute:NSLayoutAttributeCenterY 
                                multiplier:1 
                                  constant:0],

    [NSLayoutConstraint constraintWithItem:priceLabel 
                                 attribute:NSLayoutAttributeLeft 
                                 relatedBy:NSLayoutRelationEqual 
                                    toItem:self.view 
                                 attribute:NSLayoutAttributeLeft 
                                multiplier:1 
                                 constant:5],

    [NSLayoutConstraint constraintWithItem:favouriteButton 
                                 attribute:NSLayoutAttributeCenterY 
                                 relatedBy:NSLayoutRelationEqual 
                                    toItem:self.view 
                                 attribute:NSLayoutAttributeCenterY 
                                multiplier:1 
                                  constant:0],

    [NSLayoutConstraint constraintWithItem:favouriteButton 
                                 attribute:NSLayoutAttributeRight 
                                 relatedBy:NSLayoutRelationEqual 
                                    toItem:self.view 
                                 attribute:NSLayoutAttributeRight 
                                multiplier:1 
                                  constant:5],

}

Скриншот проблемы

Как видите, зеленая ценовая метка выровнена правильно, а красная кнопка далеко от левой части экрана. (Я дал ему 5 пикселей смещения, чтобы показать, где он был.) Итак, почему правая сторона прокрутки на самом деле левая сторона? Как я могу правильно выровнять по правому краю экрана? Где я ошибся? Это сводит меня с ума!

Спасибо за любую помощь!

Окончательные изображения макета: я надеюсь, что окончательный макет будет примерно таким: Окончательный портрет макета

и я ожидаю, что это будет выглядеть так, если повернуть в альбомную ориентацию: Final Layout Landscape


person Wernzy    schedule 06.02.2014    source источник


Ответы (2)


Количество ограничений не является проблемой. И UILabel, и UIButton будут определять свой размер на основе их intrinsicContentSize, и, поскольку у вас есть ограничения для положения, у него должна быть вся информация, необходимая для макета.

Однако когда дело доходит до автомакета, UIScrollViews ведет себя уникальным образом. Лучшее описание взято из этой технической заметки. Доступны два варианта, включая примеры, но здесь приводится краткое описание каждого из них.


Смешанный подход

Вам просто нужно добавить UIView к UIScrollView, а затем добавить и расположить все ваши подпредставления в UIView. Для этого необходимо вручную установить frame для UIView и contentSize для UIScrollView.

Это, вероятно, проще всего использовать с макетом, которого вы пытаетесь достичь. Однако, если contentSize может измениться, вам придется вручную рассчитать и обновить размер.


Исключительно автоматический подход

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

Этот вариант сложнее использовать, так как вам нужно убедиться, что у вас достаточно ограничений. В вашем случае вы столкнетесь с проблемами, потому что нет ограничений на верх и низ UIScrollView и нет ограничений, которые можно использовать для определения ширины UIScrollView. Тем не менее, этот вариант удобен, когда вам приходится иметь дело с динамическим контентом, поскольку он изменит размер contentSize по мере необходимости.


Лично я бы выбрал подход Pure Auto Layout. Его способность обрабатывать динамические размеры содержимого оправдывает установку дополнительных ограничений.

Если вы опубликуете то, что хотите, чтобы окончательный макет был, я обновлю свой ответ, чтобы отразить это.


Обновить

Основываясь на изображениях, которые вы разместили, я бы организовал подвиды, используя подход Pure Auto Layout. Основное отличие состоит в том, что UIScrollView теперь является подпредставлением представления UIViewControllers.

- UIView                                            (self.view)
  - UIScrollView                                    (scrollView)
    - UIView                                        (contentView)
      - UIImageView, UIButtons, UILabels, etc.

scrollView нуждается в ограничениях, поэтому его края находятся на расстоянии 0 пикселей от self.view.

contentView нуждается в ограничениях, поэтому его края равны 0px от scrollView, а его ширина равна self.view. Это значит, что contentSize в scrollView обновляется при повороте устройства.

Затем просто расположите все ваши изображения и метки так, как вы хотите. Метка должна быть ограничена влево и вправо, чтобы она могла вычислить свою высоту. Важно отметить, что contentView будет определять свою высоту на основе ограничений для своих подпредставлений, поэтому вам понадобятся ограничения, «связывающие» верх и низ contentView. Простой пример будет выглядеть так.

  • contentView сверху до imageView сверху
  • высота изображения == некоторое значение
  • imageView снизу, чтобы пометить сверху
  • метка снизу до содержимогоView снизу
person Craig Siemens    schedule 07.02.2014
comment
Я согласен с тем, что подход Pure Auto Layout — это то, что я ищу, на экране будет много динамического контента, однако он будет динамическим только в вертикальном направлении. У меня действительно нет ничего, чтобы установить горизонтальные ограничения, кроме ширины экрана. Я отредактирую свой вопрос, чтобы добавить окончательные изображения макета. - person Wernzy; 07.02.2014

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

У вас всегда должны быть ограничения, определяющие поведение представления как по горизонтали, так и по вертикали. Обычно это означает как минимум четыре ограничения — x, y, ширину и высоту. Если вы этого не сделаете, iOS догадается за вас и, скорее всего, ошибется. Вы, вероятно, заметите, что в консоли Xcode появилось несколько предупреждений.

Для этой проблемы это действительно зависит от того, как вы хотите, чтобы ваше представление выглядело, но самое простое решение — просто установить ограничение ширины для кнопки. Вы можете сделать это следующим образом:

[NSLayoutConstraint constraintWithItem:favouriteButton 
                             attribute:NSLayoutAttributeWidth  
                             relatedBy:NSLayoutRelationEqual 
                             toItem:nil 
                             attribute:NSLayoutAttributeNotAnAttribute 
                             multiplier:1.0 
                             constant:64.0];

Где 64.0f, если ширина кнопки.

Если вы хотите, чтобы кнопка находилась справа от метки и имела одинаковую ширину (т.е. каждая из них занимала 50% экрана), вы можете сделать это:

    [NSLayoutConstraint constraintWithItem:favouriteButton
                                 attribute:NSLayoutAttributeLeading
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:priceLabel
                                 attribute:NSLayoutAttributeTrailing
                                multiplier:1.0f
                                  constant:0.0f];

    [NSLayoutConstraint constraintWithItem:favouriteButton
                                 attribute:NSLayoutAttributeWidth
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:priceLabel
                                 attribute:NSLayoutAttributeWidth
                                multiplier:1.0f 
                                  constant:0.0f];

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

Вы также должны добавить ограничения по высоте для обоих представлений (или ограничений сверху и снизу). Я знаю, что у вас есть ограничение вертикального центрирования, но это не определяет его размер. iOS может определить это по высоте контента, но есть вероятность, что в консоли появятся предупреждения.

person JRod    schedule 07.02.2014
comment
К сожалению, это не решает проблему. Это обеспечивает достаточно близкое приближение, если я вообще не поворачиваю вид, но если я перехожу в альбомный режим, кнопка просто выравнивается по левому краю метки. Еще одна проблема, с которой я столкнулся с этим ответом, заключается в том, что если бы там не было UILabel, я бы ожидал, что все равно смогу выровнять представление по правому краю. - person Wernzy; 07.02.2014