NSSplitView и автомакет

Как мне использовать ограничения автоматического макета внутри подвида NSSplitView?

У моего подвида NSSplitView есть 3 подвида: topPane, tableContainer и bottomPane, и я установил ограничения следующим образом:

NSDictionary* views = NSDictionaryOfVariableBindings(topPane, tableContainer, bottomPane);

for (NSView* view in [views allValues]) {
    [view setTranslatesAutoresizingMaskIntoConstraints:NO];
}

[myView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[topPane(34)][tableContainer][bottomPane(24)]|"
                                                               options:0 
                                                               metrics:nil 
                                                                 views:views]];

[mySplitView addSubview:myView];

И получил это в консоли:

Unable to simultaneously satisfy constraints:
(
    "<NSLayoutConstraint:0x7fd6c4b1f770 V:[NSScrollView:0x7fd6c4b234c0]-(0)-[CPane:0x7fd6c4b2fd10]>",
    "<NSLayoutConstraint:0x7fd6c4b30910 V:[CPane:0x7fd6c4b2f870(34)]>",
    "<NSLayoutConstraint:0x7fd6c4b30770 V:|-(0)-[CPane:0x7fd6c4b2f870]   (Names: '|':NSView:0x7fd6c4b22e50 )>",
    "<NSLayoutConstraint:0x7fd6c4b212f0 V:[CPane:0x7fd6c4b2fd10]-(0)-|   (Names: '|':NSView:0x7fd6c4b22e50 )>",
    "<NSLayoutConstraint:0x7fd6c4b2f910 V:[CPane:0x7fd6c4b2f870]-(0)-[NSScrollView:0x7fd6c4b234c0]>",
    "<NSLayoutConstraint:0x7fd6c4b21290 V:[CPane:0x7fd6c4b2fd10(24)]>",
    "<NSAutoresizingMaskLayoutConstraint:0x7fd6c3630430 h=--& v=--& V:[NSView:0x7fd6c4b22e50(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fd6c4b1f770 V:[NSScrollView:0x7fd6c4b234c0]-(0)-[CPane:0x7fd6c4b2fd10]>

Я думаю, что это вызвано <NSAutoresizingMaskLayoutConstraint:0x7fd6c3630430 h=--& v=--& V:[NSView:0x7fd6c4b22e50(0)]>, но я не могу сбросить маску автоматического изменения размера, потому что ее устанавливает NSSplitView.

Как лучше всего использовать автоматическую компоновку в разделенном представлении? И есть ли способ обрабатывать минимальный/максимальный размер подвида разделенного представления с автоматической компоновкой без NSSplitViewDelegate?


person Dmitry    schedule 29.06.2012    source источник
comment
Та же проблема здесь. Я выложил все в IB, а не программно, но вывод отладки аналогичен, в том числе NSAutoresizingMaskLayoutConstraint.   -  person Kristopher Johnson    schedule 01.07.2012
comment
Это кажется исправленным в 10.8, но не работает, как вы заметили в 10.7. В 10.8 вы можете установить минимальную высоту и ширину представлений содержимого разделенного представления в Xcode (в любом случае 4.5.2). Невозможно сделать это в версии 10.7, и приложения, созданные в версии 10.8, по-прежнему не работают в версии 10.7.   -  person Dad    schedule 20.01.2013
comment
В целом работает с 10.8+, но между подпредставлениями для большинства представлений должны быть указаны ограничения, а не над ними, иначе вы получите ошибку Невозможно одновременно удовлетворить.   -  person Jay    schedule 04.01.2014


Ответы (9)


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

splitView:constrainMinCoordinate:ofSubviewAt:   
splitView:constrainMaxCoordinate:ofSubviewAt:
splitView:shouldAdjustSizeOfSubview:

Решение было найдено в прикреплении панели инструментов к окну в windowDidLoad.

person Al Zonke    schedule 05.11.2012
comment
У меня была такая же проблема с NSTabView, сняв флажок Visible at Launch на панели инструментов, а затем сделав его видимым в windowDidLoad, я избавился от глупых предупреждений. Спасибо за подсказку. - person DarkDust; 10.05.2013
comment
В разделенном представлении на основе автоматического макета с минимальными/максимальными ограничениями ширины у меня была та же проблема. Удаление всех этих трех методов делегата разрешило это. - person Lukas Kubanek; 08.02.2014
comment
Много дезинформации по этому вопросу. Этот ответ находится на правильном пути, но бизнес с панелями инструментов - отвлекающий маневр. Проблема задокументирована в примечаниях к выпуску AppKit (developer.apple.com/library/content/releasenotes/AppKit/); некоторые методы NSSplitViewDelegate плохо работают с автомакетом и не должны использоваться. Вместо этого установите соответствующие ограничения для NSView непосредственно под NSSplitView, и все должно работать нормально без ведения журнала. - person bhaller; 25.04.2017

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

Мое решение состоит в том, чтобы вообще не использовать NSSplitView с AutoLayout. Итак, либо NSSplitView без Autolayout, либо Autolayout без NSSplitView: это не так сложно, как кажется: просто разместите свои подпредставления рядом друг с другом и добавьте NSLayoutConstraints как IBOutlets. Константы этих ограничений могут быть затем установлены и изменены из контроллера в коде. При таком подходе вы можете установить исходную точку (отрицательное смещение, чтобы сдвинуть его за пределы окна), ширину и отношения с другими подпредставлениями, а также очень легко анимировать ограничения с помощью аниматора представления (когда-либо пытались анимировать NSSplitView?)

Единственное, чего не хватает, так это перетаскивания мышью разделителей, но это можно реализовать парой строк, отслеживая события mouseEvents в вашем пользовательском «SplitView».

Есть пример autolayout «splitview» от Apple (к сожалению, только вертикальный), и в последнее время я видел как минимум один новый проект на github. Хотя для меня я подумал, что будет проще начать с моего собственного решения для конкретных потребностей моего приложения, чем пытаться создать что-то очень универсальное (что делает его слишком сложным для обработки).

Редактировать: я завершил свой собственный splitView, который загружает свои подпредставления из отдельных nibs. Нет проблем с ограничениями, нет предупреждений автомакета. По сравнению с целым месяцем, когда я пытался заставить его работать с NSSplitView, теперь у меня есть работающий собственный splitView, основанный на ограничениях, легко анимируемый, созданный всего за один вечер. Однозначно рекомендую этот маршрут!

person auco    schedule 18.08.2012
comment
@Ben-Uri: у меня есть небольшой проект в моем ответе, который может быть вам полезен. - person sudo rm -rf; 20.12.2012
comment
@Jay - Хотя этот ответ был составлен, когда я работал в версии 10.7. это все еще в силе. Как я уже говорил ранее, этого достаточно для простых макетов, но как только вы используете более сложные автоматические макеты, определенно возникают проблемы с NSSplitView. Просто попробуйте добавить изменение размера и свертывание видов, а затем удачи в их анимации. - person auco; 03.01.2014
comment
@auco - только что реализовал представление, похожее на Xcode Inspector, с использованием исключительно стандартного NSSplitView и автоматического макета. Там нет проблем, сворачивание/расширение/добавление анимированных подпредставлений бесплатно с использованием автоматического макета.. как я уже сказал, по крайней мере, с 10.8+ это работает как ветер - person Jay; 04.01.2014
comment
Не пытаюсь ныть, но меня действительно интересуют эти постоянные анонимные отрицательные голоса (по крайней мере четыре с тех пор, как я написал этот ответ в 2012 году). Я бы счел более полезным, если бы вы присоединились к обсуждению с аргументами и опытом и / или добавили или проголосовали за другое решение, чего явно не происходит, поскольку количество голосов за другие ответы не меняется. Тот факт, что эта тема существует и что есть очень предвзятые мнения, указывает на то, что есть некоторые проблемы с NSSplitView и Autolayout, по крайней мере, при совместимости с Mac OS 10.7. имеет значение. - person auco; 18.06.2015

Для тех, кто наткнется на это в будущем и ищет рывок к замене NSSplitView на основе ограничений, я написал здесь небольшой проект, который пытается воссоздать часть функций NSSplitView с помощью Auto Layout:

https://github.com/jwilling/JWSplitView

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

person sudo rm -rf    schedule 20.12.2012

В версии 10.8 эта проблема устранена, см. примечания к выпуску.

Вот мое решение для 10.7 (настраиваемое разделенное представление): https://github.com/benuri/HASplitView.git

person Yoav    schedule 16.10.2012

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

Правильный способ установить минимальную или максимальную (или постоянную, если уж на то пошло) ширину/высоту в разделенном представлении — установить эти вещи в представлениях по отдельности. В частности, если вы делаете это в коде, вам нужно будет использовать 2 отдельных вызова limitedsWithVisualFormat, потому что в противном случае язык визуального формата создаст ограничения между представлениями.

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

person David Beck    schedule 17.08.2012
comment
Было бы интересно услышать, как вы предлагаете сделать это все в IB с включенным AutoLayout.... - person Dad; 20.01.2013
comment
на самом деле невозможно в 10.7 - элемент управления для установки ограничения ширины в представлении содержимого NSSplitView отключен в Xcode 4.5.2 до 10.7, и если вы установите его в Xcode на 10.8, а затем запустите приложение на 10.7, это нарушит это ограничение в пользу NSAutoresizingMaskLayerConstraint в 10.7 и так не работает. - person Dad; 20.01.2013

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

Фактическое решение проблемы, упомянутой здесь, довольно простое: Auto Layout представил новый «Holding Priorities API» в NSSplitView. И, как говорится в документации: установка более низких значений для приоритета удержания подпредставления повысит вероятность того, что он займет ширину раньше. Все это можно настроить в ИБ и программно без всякого отчаяния. Объем необходимой работы: 20 секунд прибл.

person Jacque    schedule 09.10.2012
comment
Этот ответ был бы более полезным, если бы он просто отвечал на вопрос, а не демонстрировал такое презрение к другим отвечающим и спрашивающим. Я дам вам +1 за ответ и -1 за грубость, так что они компенсируют друг друга. - person Kristopher Johnson; 09.10.2012
comment
@Jacque: Если бы вы также читали документацию, вы бы увидели, что сохранение приоритетов работает только в OS X 10.8+. Нет необходимости быть невнимательным по отношению к другим, когда, возможно, они думали об обратной совместимости, а вы нет. - person sudo rm -rf; 23.10.2012
comment
@Jacque: Возможно, вы правы, если имеете дело с действительно простым макетом, например, с двумя текстовыми представлениями в качестве подпредставлений одного splitView. Но как только вам нужно иметь дело со сложными макетами подпредставлений, установка приоритетов удержания не сильно поможет: содержимое в разделенном представлении, которое использует автомакет, создает ошибки автомакета, потому что конфликтует с масками автоматического изменения размера. Анимация SplitView — это головная боль, потому что вам нужно вычислять кадры, что невозможно при использовании автоматического макета. На самом деле проблем гораздо больше, чем вы можете решить, просто удерживая приоритеты. - person auco; 10.11.2012

Я загружаю все файлом пера и setTranslatesAutoresizingMaskIntoConstraints:NO потом.

Так что, возможно, вам следует сначала добавить [mySplitView addSubview:myView]; свои представления и затем отключить перевод маски авторазмера в ограничения, а после этого добавить свое ограничение в myView.

ИЗМЕНИТЬ:

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

[topPane addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topPane(34)]" options:0 metrics:nil views:views]];

[bottomPane addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[bottomPane(24)]" options:0 metrics:nil views:views]];

Вам не нужно добавлять краевые ограничения («|» в «V:|[topPane(34)]»), потому что размеры подвидов в NSSplitView уже автоматически изменяются.

Это приводит к этому, например. для ограничения topPane:

Screenshout

ПРИМЕЧАНИЕ: игнорируйте содержимое подпредставления, это просто заполнители.

person Stephan    schedule 15.07.2012
comment
Если я установлю translatesAutoresizingMaskIntoConstraints на NO для myView, разделенный вид не будет обрабатывать его размер - он не работает с автомакетом. - person Dmitry; 16.07.2012

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

Мой макет выглядит так:

RootView
|--1-й NSSplitView (3 вертикальных подвида)
|----UIView (слева)
|----2-й NSSplitView (в центре и 2 горизонтальных подвида)
|---UIView (сверху)
|---3rd NSSplitView (снизу и 3 вертикальных подвида)
|---UIView ( слева)
|---UIView (в центре)
|---UIView (справа)
|----UIView (справа)

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

Оба представления (левое и правое) имели ограничение ширины с «шириной >= 200», а центральное представление (второе разделенное представление) не имело ограничений (поскольку его минимальная ширина и максимальная ширина обрабатывались его подпредставлениями).

Предупреждения показали мне, что autolayout хочет уменьшить мой IB-UI-Layout, потому что рассчитанная минимальная ширина меньше, чем мой макет, но я не хотел сжимать его в IB.

Я добавил фиксированное ограничение «ширина = 200» к обоим внешним подвидам моего первого разделенного представления и отметил «удалить во время сборки».

Теперь мой макет свободен от предупреждений и все работает как надо.

Мой вывод:

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

Таким образом, ширины ‹= xxx && width >= xxx не существует. Autolayout может обрабатывать только один из них, и мы получаем предупреждения в IB. Вы можете решить эту проблему с помощью временного ограничения в IB, которое будет удалено перед выполнением.

Я надеюсь, что то, что я написал, имеет смысл, но в моем проекте это сработало нормально.

PS: я не мог найти никакого решения до сегодняшнего дня, когда я нашел эту тему ... так что, думаю, ваши сообщения вдохновили меня :-)

person Rikco    schedule 09.02.2015

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

@interface FBSplitPaneView : NSView

@end

@implementation FBSplitPaneView

- (void)setFrame:(NSRect)frame
{
  for (NSView *subview in self.subviews) {
    subview.frame = self.bounds;
  }
  [super setFrame:frame];
}

@end
person Mathieu Tozer    schedule 22.04.2015