Добавление и настройка NSLayoutConstraint через код

У меня проблемы с NSLayoutConstraint.

NSLayoutConstraint дает мне исключение, если я пытаюсь изменить constant с помощью метода setConstant:. У меня возникает эта проблема только тогда, когда я добавляю ограничение по высоте с помощью кода.

Во-первых, я получаю ограничение по высоте следующим образом:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d", NSLayoutAttributeWidth];
NSArray *filteredArray = [[self constraints] filteredArrayUsingPredicate:predicate];
return filteredArray[0];

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

Теперь у меня есть представление, куда я добавляю различные подпредставления во время выполнения. Эти подпредставления расположены в собственных NIB, что означает, что я не могу зафиксировать их ширину и высоту. Поэтому я подумал, что добавлю ограничения, как только представление будет добавлено в суперпредставление. Это выполняется в viewDidMoveToSuperview.

// Pin Width & Height
[self addConstraint:[NSLayoutConstraint constraintWithItem:self
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual
                                toItem:nil
                             attribute:NSLayoutAttributeNotAnAttribute
                            multiplier:1.0f
                              constant:self.frame.size.width]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                 attribute:NSLayoutAttributeHeight
                                                 relatedBy:NSLayoutRelationEqual
                                                    toItem:nil
                                                 attribute:NSLayoutAttributeNotAnAttribute
                                                multiplier:1.0f
                                                  constant:self.frame.size.height]];

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

"<NSLayoutConstraint:0x100605cc0 H:[ITControlPanelChildView:0x100616eb0(269)]>",
"<NSLayoutConstraint:0x100614410 V:[ITControlPanelChildView:0x100616eb0(317)]>"

Теперь, наконец, когда я пытаюсь изменить ограничение с помощью [constraint setConstant:somethingDifferent];, я получаю следующее исключение:

Unable to simultaneously satisfy constraints:
(
    "<NSLayoutConstraint:0x10192fcb0 V:[ITControlPanelChildView:0x10192e4f0(100)]>",
    "<NSAutoresizingMaskLayoutConstraint:0x10053ff10 h=--& v=&-- V:[ITControlPanelChildView:0x10192e4f0(317)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x10192fcb0 V:[ITControlPanelChildView:0x10192e4f0(100)]>

Это именно то ограничение, которое я пытаюсь изменить. Может ли кто-нибудь объяснить мне, почему это происходит?


РЕДАКТИРОВАТЬ

Я только что прочитал, что NSAutoresizingMaskLayoutConstraint добавляются автоматически, что можно отключить, если установить [self setTranslatesAutoresizingMaskIntoConstraints:NO];.

Если отключить, то работает.

Еще лучше было бы, если бы я мог получить доступ к созданным NSAutoresizingMaskLayoutConstraint. Кто-нибудь знает, как это работает?


person IluTov    schedule 19.12.2012    source источник
comment
Думаю, вы добавили это редактирование, пока я писал свой ответ! Включен ли автомакет в xib для подпредставления? Можете ли вы показать код, который вы используете для добавления подвида?   -  person jrturton    schedule 19.12.2012
comment
@jrturton Да, автомакет включен. Может быть, вы меня неправильно поняли, это работает. Я просто подумал, что было бы неплохо использовать NSAutoresizingMaskLayoutConstraint, которые уже созданы для меня, вместо того, чтобы отключать их и добавлять ограничения самостоятельно. Вы понимаете, что я имею в виду?   -  person IluTov    schedule 19.12.2012
comment
Я могу, но ты не можешь. Я удивлен, что они появляются на представлении, определенном на верхнем уровне в xib Autolayout, поэтому я и спросил о коде.   -  person jrturton    schedule 19.12.2012
comment
Я дошел до того, что создал категорию в UIView, которая возвращает новый экземпляр с установленным флагом no, просто для использования при создании представлений Autolayout в коде.   -  person jrturton    schedule 19.12.2012
comment
@jrturton О, хорошо. Я просто добавляю его, используя [self.view addSubview:subview]; в контроллере представления.   -  person IluTov    schedule 19.12.2012


Ответы (2)


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

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

self.translatesAutoresizingMaskIntoConstraints = NO;

Хотя, если вы создаете представление в отдельном наконечнике, откуда он берет свой размер? Разве у него уже нет ограничений по размеру, для которых вы можете просто создать выходы?

person jrturton    schedule 19.12.2012

У меня была эта проблема дважды.

Я использовал технику, описанную в Cocoa Programming for Mac OSX 4th Ed автора Hillegass & Preble. В книге рекомендуется использовать NSBox в качестве контейнера представления и переключать представления в него с помощью кода. В книгу также включен удобный код для ручного изменения размера внешнего окна, содержащего только что переключенное представление.

В первый раз, когда я попробовал их технику, я получил это исключение:

2013-01-05 22:56:05.103 MyApp[17717:303] Unable to simultaneously satisfy constraints:
(
    "<NSAutoresizingMaskLayoutConstraint:0x100141330 h=--& v=--& H:[NSView:0x10012fdf0(4)]>",
"<NSLayoutConstraint:0x1001316c0 H:[NSScrollView:0x100123ff0]-(NSSpace(20))-|   (Names: '|':NSView:0x10012fdf0 )>",
"<NSLayoutConstraint:0x1001315d0 H:|-(NSSpace(20))-[NSScrollView:0x100123ff0]   (Names: '|':NSView:0x10012fdf0 )>")

Will attempt to recover by breaking constraint 

Frustratingly enough I had to refactor the UI and in the rebuilt version (which is based on NSDocument rather than a simple window) I struck the same problem again.

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

Если я отключу AutoLayout в окне XIB на основе NSDocument верхнего уровня, то исключение также перестанет выдаваться. Но тогда макет для элементов верхнего уровня неправильный, и размер блока неправильный. Думаю, это не настолько сложно, чтобы я не мог вручную все это выложить.

Странно то, что в моей первой версии пользовательского интерфейса он работает без броска. В первой версии Autolayout установлен для представления верхнего уровня и всех подпредставлений.

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

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

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

Однако я нашел способ исправить это в случае номер два: отключить флажок «AutoResizes Subviews» для NSBox, используемого в качестве контейнера представления.

Почему это не требуется в первой версии пользовательского интерфейса (которая прекрасно работает с этим установленным флажком) — загадка.

В любом случае - задокументировано здесь на случай, если это поможет кому-то еще.

person Sez    schedule 05.01.2013