Автоматическая компоновка iOS 7 - Невозможно выровнять правое подзону справа от UIScrollView

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

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],

}

Screenshot of problem

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

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

Финальные макеты: Я надеюсь, что окончательный макет будет примерно таким: Final Layout Portrait

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

Ответ 1

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

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


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

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

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


Чистый подход к автоматическому макетированию

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

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


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

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


Обновление

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

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

scrollView требует ограничений, поэтому его ребра являются 0px из self.view.

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

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

  • contentView top to imageView top
  • imageView height == some value
  • Изображение снизу на этикетке сверху
  • снизу до нижнего уровня contentView внизу

Ответ 2

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

У вас всегда должны быть ограничения, указывающие, как выглядит представление как по горизонтали, так и по вертикали. Обычно это означает минимум четыре ограничения - 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 может работать с ней по высоте контента, но, скорее всего, это вызовет предупреждения в консоли.