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

Настройка ограничений программно

Я экспериментирую с использованием UIScrollView. После долгих неприятностей я, наконец, понял это. Но теперь я, кажется, ударил еще одну загвоздку.

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

Теперь я столкнулся с сценарием, где я должен делать эту часть программно. Я добавил код ниже в методе viewDidLoad.

NSLayoutConstraint *bottomSpaceConstraint = [NSLayoutConstraint constraintWithItem:self.view
                                                                             attribute:NSLayoutAttributeBottom
                                                                             relatedBy:NSLayoutRelationEqual
                                                                                toItem:self.scrollView
                                                                             attribute:NSLayoutAttributeBottom
                                                                            multiplier:1.0
                                                                              constant:0.0];
[self.view addConstraint:bottomSpaceConstraint];

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

Невозможно одновременно удовлетворить ограничениям.   Вероятно, хотя бы одно из ограничений в следующем списке - это тот, который вы не хотите. Попробуйте это: (1) посмотрите на каждое ограничение и попытайтесь выяснить, чего вы не ожидаете; (2) найти код, который добавил нежелательные ограничения или ограничения и исправить его. (Примечание. Если вы видите NSAutoresizingMaskLayoutConstraints, которые вы не понимаете, обратитесь к документации для свойства UIView, переводитAutoresizingMaskIntoConstraints) (    ",   " " )

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

UPDATE:

Прежде всего спасибо за ответы. Используя способы, упомянутые в ответах, я смог заставить его работать. Однако в немного другом сценарии его нет. Теперь я пытаюсь загрузить представление на viewcontroller программно.

Если я объясню дальше. Есть 2 контроллера вида. Первый из них является UITableViewController, а второй a UIViewController. Внутри это a UIScrollView. Также есть несколько UIView, а высота некоторых видов превышает нормальную высоту экрана.

UITableViewController отображает список параметров. Основываясь на выборе пользователя, конкретный UIView из партии будет загружен в UIViewController с помощью UIScrollView.

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

Я загрузил демонстрационный проект здесь, чтобы вы могли видеть его в действии. См. Email.xib и выберите Email из списка таблиц.

4b9b3361

Ответ 1

Основываясь на обзоре вашего кода, несколько комментариев:

  • Обычно вам не нужно настраивать ограничения при появлении представления. Если вы это делаете, это часто означает, что вы неправильно настроили раскадровку (или, по крайней мере, неэффективно). Единственный раз, когда вам действительно нужно устанавливать/создавать ограничения, является либо (а), что вы добавляете представления программно (что я предлагаю - это больше работы, чем это стоит); или (b) вам нужно выполнить некоторую корректировку ограничений времени выполнения (см. третий маркер в пункте 3 ниже).

  • Я не хочу его любить, но у вашего проекта была куча избыточного кода. Например.

    • Вы устанавливали кадр для представления прокрутки, но это регулируется ограничениями, поэтому ничего не делает (например, когда применяются ограничения, любые установленные вручную настройки frame будут заменены). В общем, в макете авто, не пытайтесь напрямую изменить frame: отредактируйте ограничения. Но, во всяком случае, никакого изменения ограничений вообще не требуется, поэтому точка спорна.

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

    • Вы устанавливали ограничения для scrollview (которые уже были равны нулю), но тогда вы не добавляли представление из NIB в scrollview, также побеждая любое намерение. Первоначальный вопрос заключался в том, как изменить нижнее ограничение вида прокрутки. Но нижнее ограничение для этого уже равно нулю, поэтому я не вижу причин снова его устанавливать.

  • Я бы предложил более радикальное упрощение вашего проекта:

    • Вы делаете жизнь намного сложнее, сохраняя свои представления в NIB. Это намного проще, если вы останетесь в мире раскадровки. Мы можем помочь вам сделать материал NIB, если вам действительно нужно, но зачем так тяжело жить на себе?

    • Используйте прототипы соты, чтобы облегчить дизайн ячеек в вашей таблице. Вы также можете определить segues, чтобы перейти от ячеек к следующей сцене. Это устраняет необходимость в написании кода didSelectRowAtIndexPath или prepareForSegue. Ясно, что если у вас есть что-то, что нужно передать на следующую сцену, используйте prepareForSegue, но ничего, что вы до сих пор не представили, требует этого, поэтому я прокомментировал это в своих примерах.

    • Предполагая, что вы ищете практический пример программно изменяющихся ограничений, я настроил сцену так, чтобы текстовое представление изменило свою высоту программно, основываясь на тексте в текстовом представлении. Как всегда, вместо того, чтобы повторять с помощью ограничений, чтобы найти тот, о котором идет речь, при изменении существующего ограничения, созданного для меня IB, я считаю гораздо более эффективным создание IBOutlet для ограничения и редактирование constant свойство для ограничения напрямую, так что я сделал. Поэтому я настроил контроллер представления как делегат текстового представления и написал textViewDidChange, который обновил ограничение высоты текстового вида:

      #pragma mark - UITextViewDelegate
      
      - (void)textViewDidChange:(UITextView *)textView
      {
          self.textViewHeightConstraint.constant = textView.contentSize.height;
          [self.scrollView layoutIfNeeded];
      }
      

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


Когда вы добавляете scrollview в IB, он автоматически получит все необходимые вам ограничения. Вероятно, вы не хотите добавлять ограничение программно (по крайней мере, не исключая существующего нижнего ограничения).

Два подхода могут быть проще:

  • Создайте IBOutlet для существующего нижнего ограничения, скажем scrollViewBottomConstraint. Тогда вы можете просто сделать

    self.scrollViewBottomConstraint.constant = 0.0;
    
  • Или сначала создайте свое представление в IB, где нижнее ограничение равно 0.0, а затем вам вообще не нужно ничего делать программно. Если вы хотите расположить длинный прокрутки и его подпункты, выберите контроллер, задайте имитированные показатели с "выводимых" на "свободную форму". Затем вы можете изменить размер представления, установить верхние и нижние ограничения scrollview равными нулю, расставить все, что угодно, в окне прокрутки, а затем, когда представление будет представлено во время выполнения, представление будет соответствующим образом изменено и потому, что вы 'определял верхние и нижние ограничения scrollview равными 0.0, он будет правильно изменен. В IB это выглядит немного странно, но при работе приложения он работает как прелесть.

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

Но вы определенно не хотите просто добавлять новое ограничение.

Ответ 2

Возможно создание торговых точек для представления ограничений макета в вашем контроллере представления. Просто выберите ограничение, которое вы хотите в построителе интерфейса (например, с помощью "выбрать и отредактировать" на панели измерений создаваемого вами представления). Затем перейдите в панель выходов и перетащите "New Referencing Outlet" в свой файл кода (.h или .m). Это свяжет ограничение с экземпляром NSLayoutConstraint, с которым вы можете получить доступ с вашего контроллера и динамически настраиваться "на лету" (обычно с помощью свойства constant, которое плохо названо, потому что оно не является константой вообще).

enter image description here

(Обратите внимание, что в XCode 6 вы можете дважды щелкнуть ограничение, чтобы выбрать его для редактирования.)

enter image description here

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

Ответ 3

Глядя на информацию о консоли, я чувствую, что вы создаете двусмысленность при добавлении двух ограничений типа.

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

for(NSLayoutConstraint *constraint in self.view.constraints)
    {
        if(constraint.firstAttribute == NSLayoutAttributeBottom && constraint.secondAttribute == NSLayoutAttributeBottom &&
           constraint.firstItem == self.view && constraint.secondItem == self.scrollView)
        {
            constraint.constant = 0.0;
        }
    }

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

Даже ответ Роб будет работать!

Ответ 4

Вы можете использовать https://github.com/SnapKit/Masonry для программного программирования ограничений.

Это сила AutoLayout NSLayoutConstraints с упрощенным, цепным и выразительным синтаксисом. Поддерживает автоматическую компоновку iOS и OSX.

UIView *superview = self.view;

UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeTop
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeTop
                            multiplier:1.0
                              constant:padding.top],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeLeft
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeLeft
                            multiplier:1.0
                              constant:padding.left],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeBottom
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeBottom
                            multiplier:1.0
                              constant:-padding.bottom],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeRight
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeRight
                            multiplier:1
                              constant:-padding.right],]];

всего в нескольких строках

Имеет те же ограничения, которые создаются с помощью MASConstraintMaker

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
    make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
    make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];

Или даже короче

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

сделай все, что в ваших силах - это сортировка;)