24

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

В этом простом приложении у меня есть вид прокрутки и для его работы, я должен установить нижнее пространство представления для ограничения прокрутки в 0, как описано here, и он отлично работает. Я делаю это через 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, что вы не понимаете, обратитесь к документации по translatesAutoresizingMaskIntoConstraints собственности UIView) ( «», «» )

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

UPDATE:

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

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

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

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

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

ответ

56

На основе обзора кода, несколько комментариев:

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

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

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

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

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

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

    • Вы делаете жизнь намного тяжелее на себя, сохраняя свои взгляды в наконечниках. Это намного проще, если вы останетесь в мире раскадровки. Мы можем помочь вам сделать материал 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, он автоматически получит все ограничения вам нужно. Вероятно, вы не хотите добавлять ограничение программно (по крайней мере, не исключая существующего нижнего ограничения).

два подхода может быть проще:

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

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

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

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

+0

Привет Роб, спасибо за res Понс. Я обновил существующее ограничение и отлично работает. Однако теперь я пытаюсь загрузить представление из отдельного 'UIView', а затем его не работает. Должен ли я делать что-то другое в этом случае? Я обновил свой вопрос. – Isuru

+1

@ Isuru Я прошел через ваш проект и получил много комментариев, поэтому см. Мой пересмотренный ответ. Кроме того, если вы загружаете мои примеры проектов, которые упрощают выдачу исходной кодовой базы, вы увидите дополнительные комментарии в исходном коде. – Rob

+1

Привет, Роб, большое спасибо за все это. Это определенно один из лучших ответов, которые я получил за вопрос. Я прошел через оба проекта и многому научился. Мне 4 месяца в iOS, поэтому я новичок в некоторых вещах, но все это хорошо. Причина, по которой я хотел использовать xib-файлы, - это то, что я работаю над этим приложением на работе, и пожилые люди настаивали на использовании одного 'UIViewController' и имеют файлы типа UI и _templates_ nib для почтового типа, потому что их проще управлять, поскольку мы уже имеем много сцен, лежащих вокруг. – Isuru

12

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

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

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; 
     } 
    } 

Надеется, что это помогает

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

+0

Привет, Kunal, спасибо за ответ снова. Я обновил существующее ограничение, используя ваш метод, и он отлично работает. Однако теперь я пытаюсь загрузить представление из отдельного 'UIView', а затем его не работает. Должен ли я делать что-то другое в этом случае? Я обновил свой вопрос. – Isuru

+0

Извините за поздний ответ. В NewMailViewController следующий код вызывает ошибку if ([self.mailTypeName isEqualToString: @ "Email"]) { NSArray * nib = [[NSBundle mainBundle] loadNibNamed: @ «Email» владелец: self options: nil]; self.view = [nib objectAtIndex: 0]; } Здесь вы переназначаете self.view на email.xib view (и email.xib не имеет прокрутки). Надеюсь это поможет. – Kunal

+0

Его хорошо. :) Я вижу. Но я поставил 'UIScrollView' в _Email.xib_, и он все еще не работал. Что мне делать? – Isuru

21

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

enter image description here

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

enter image description here

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

+0

Это было очень полезно, отлично работало thx – Guy

2

Вы можете использовать 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],]]; 

всего несколько строк

Heres те же ограничения, созданный с использованием 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); 
}]; 

сделать все возможное, является своего рода;)

+0

'TinyConstraints' является моим fav прямо сейчас: https://github.com/roberthein/TinyConstraints – SushiHangover

Смежные вопросы