Динамический NSStackView, размер которого изменяется в соответствии со своим содержимым

#xcode #macos #cocoa #xcode8 #macos-sierra

#xcode #macos #cocoa #xcode8 #macos-sierra

Вопрос:

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

Итак, этот NSStackView будет иметь:

  • Фиксированная высота
  • Динамическая ширина, основанная на его содержимом
  • Его точное положение в родительском представлении неизвестно во время разработки
  • Ряд кнопок NSButtons с фиксированными размерами

У меня есть файл nib, содержащий NSStackView и его кнопки, которые я загружаю во время выполнения:

 Bundle.main.loadNibNamed("ToolbarView", owner: self, topLevelObjects: nil)
toolbarView.detachesHiddenViews = true
toolbarView.setFrameOrigin(NSPoint(x: 100, y: 100))
  

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

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

Есть ли какой-либо способ заставить это работать или мне нужно вернуться к ручному вычислению ширины видимых кнопок (что делает использование NSStackView бессмысленным)?

Ответ №1:

Каково значение translatesAutoresizingMaskIntoConstraints в представлении стека после загрузки его из nib? Если true, то что autoresizingMask ? (по умолчанию должно быть .none).

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

Лучший способ справиться с этим — установить translatesAutoresizingMaskIntoConstraints значение false, а затем использовать ограничения для размещения его в родительском представлении во время выполнения.

Комментарии:

1. Если я установлю translatesAutoresizingMaskIntoConstraints значение false, мне нужно добавить ограничения вручную, чтобы родительское представление не жаловалось на отсутствующие ограничения. Знаете ли вы, какие ограничения я должен добавить в этом случае для достижения желаемого поведения?

2. Это зависит от того, чего именно вы пытаетесь достичь с помощью динамического позиционирования. Но stackView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 100).active = true; stackView.topAnchor.constraint(equalTo: parentView.topAnchor, constant: 100).active = true должно быть моральным эквивалентом источника фрейма, установленного выше. Если эта позиция может измениться позже, вы можете сохранить ссылку на ограничения и изменить их constant свойство.

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

Ответ №2:

В дополнение к уже упомянутым translatesAutoresizingMaskIntoConstraints и ведущим и верхним ограничениям, вы пробовали изменять приоритет охвата представления стека? Используйте значение 1000 (обязательно), чтобы полностью предотвратить расширение представления стека за пределы его содержимого:

 stackView.setHuggingPriority(1000, for: .horizontal)
  

Комментарии:

1. Спасибо. Я пробовал это. Но проблема заключалась в том, что ограничения, созданные из маски автоматического изменения размера, не позволяли NSStackView изменять размер самого себя, поэтому приоритет не помог. Думаю, теперь я более или менее понимаю, в чем была моя проблема (проблемы). (смотрите Мой комментарий к ответу @Taylor).

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