Разделение представлений для большей функциональности

Многие приложения macOS, такие как Safari, Finder, Mail и Xcode, используют разделенные представления для разделения своего содержимого на отдельные области. Это позволяет пользователю самостоятельно определять размер различных частей приложения.

Исходя из опыта работы с iOS, я был удивлен, насколько меньше ссылок, статей и документации существует для разработки для macOS; поэтому я решил записать свои эксперименты по использованию NSSplitView.

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

Добавление NSSplitView

Начнем с добавления NSSplitView в нашу раскадровку. Как вы можете видеть на левом изображении ниже, при запуске нового проекта вы увидите контроллер окна и контроллер представления в раскадровке. Чтобы добавить разделенное представление, просто найдите его в библиотеке (доступной через кнопку + в верхнем правом углу Xcode или нажав Shift + Cmd + L) и перетащите его на существующий контроллер представления.

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

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

Снова найдите в библиотеке представления контейнера и добавьте их в качестве подпредставлений к разделенному представлению. Вы можете увидеть окончательную настройку в правой части изображения ниже. Я также добавил к панелям надписи с текстом «Левая панель» и «Правая панель», чтобы у них был какой-то контент для отображения.

Вот как сейчас выглядит приложение:

Добавление дополнительных панелей

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

Глядя на иерархию представлений, мы видим, что наше разделенное представление содержит два настраиваемых представления. Мы можем просто взять еще один из библиотеки (+ кнопка или Shift + Cmd + L) и перетащить его внутрь нашего разделенного представления. Это добавит новую область, которую мы можем настроить так же, как две предыдущие выше.

Программное отображение или скрытие панелей

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

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

Давайте попробуем воссоздать сегмент управления панелью Xcode. Для этого нам нужно добавить сегментированный элемент управления к контроллеру представления. Я также воссоздал значки, которые использует Xcode, чтобы они выглядели лучше. Мы хотим, чтобы первый сегмент управлял левой панелью, а правый сегмент - правой, чтобы средняя часть всегда была видна. Чтобы разрешить множественный выбор в сегменте, нам нужно установить его режим на Select Any ,, а поскольку при запуске все панели должны быть видны, нам также необходимо установить оба сегмента для выбора. Обе настройки можно выполнить, выбрав сегментированный элемент управления и открыв его инспектор атрибутов.

Вот как теперь выглядят раскадровка и приложение:

Давайте посмотрим, как обрабатывать события в сегментированном элементе управления:

// 1 - Во-первых, нам нужно добавить IBAction для обработки, когда пользователь щелкает сегмент. Это действие предоставляет нам отправителя в качестве аргумента.

// 2 - Мы можем переключить свойство сегмента selectedSegment, чтобы узнать, какой сегмент был нажат. В зависимости от этой информации мы изменим видимость левой или правой панели. Нам также нужно знать, был ли сегмент выбран или не выбран. В первом случае мы хотим показать соответствующую панель - в противном случае мы хотим ее скрыть.

// 3 - Если был нажат первый сегмент, мы хотим изменить видимость левой панели. Если он должен быть показан, мы устанавливаем 100 точек в качестве новой ширины и вызываем метод разделения представления setPosition(_:ofDividerAt:). Это переместит разделитель в индексе 0 на расстояние 100 пунктов от левой границы представления. В противном случае, если левая панель должна быть свернута, мы установим новое положение на 0 и, таким образом, скроем панель. Наконец, нам нужно вызвать splitView.layoutSubtreeIfNeeded(), чтобы обновить макет разделенного представления.

// 4 - Так же, как мы сделали с левой панелью, нам нужно установить новую позицию для разделителя в индексе 1. Но поскольку это правая панель с правой стороны представления, на этот раз мы установим новую позицию на view.frame.width — 100 если панель должна быть развернута, и до view.frame.width, если она должна быть свернута.

Теперь вы можете запустить приложение, щелкнуть сегментированный элемент управления, и вы увидите, как панели сворачиваются и расширяются - круто!

Анимация раскрытия и сворачивания

Мы можем использовать анимацию, чтобы сделать переход более плавным:

// 1 - Для этого нам нужно изменить тело changeLeftPanelVisibility(visible:) и changeRightPanelVisibility(visible:). Вместо обновления положения разделителей мы вызываем небольшой вспомогательный метод, который делает то же самое внутри анимации.

// 2 - Этот новый метод получит новую позицию и индекс разделителя для обновления.

// 3 - Мы можем использовать анимацию, вызвав runAnimationGroup, и использовать данный контекст анимации для настройки анимации - в этом примере анимация займет 0,75 секунды. Наконец, внутри замыкания нам также нужно обновить разделенное представление.

Этот GIF показывает, как он должен выглядеть в действии:

Обнаружение изменения размера панели вручную

При использовании Xcode мы видим, что сегменты обновляются, когда пользователь перетаскивает разделитель и закрывает навигатор, инспектор или область отладки. Далее мы посмотрим, как мы можем воссоздать это поведение.

// 1 - Контроллер представления, содержащий разделенное представление, должен реализовать протокол NSSplitViewDeletate, чтобы получать уведомления всякий раз, когда позиция разделителя обновляется.

// 2 - Нам также нужны выходы для сегментированного управления и разделенного представления.

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

// 4 - Один из методов в NSSplitViewDelegate - splitViewDidResizeSubviews(_:). Он вызывается всякий раз, когда перемещается разделитель.

// 5 - этот метод получает Notification в качестве параметра. Это уведомление имеет свойство userInfo, которое представляет собой словарь, содержащий индекс перемещенного разделителя для ключа NSSplitViewDividerIndex.

// 6 - Нам нужны два бита информации для обновления сегментированного элемента управления: индекс сегмента, который нужно обновить, и должен ли он быть выбран или не выбран. Мы можем получить первую информацию, проверив индекс перетаскиваемого разделителя. Чтобы получить состояние выбора, нам нужно проверить, имеет ли панель теперь ширину 0 точек. В этом случае соответствующий сегмент не должен быть выделен - в противном случае его нужно выбрать.

// 7 - Наконец, мы можем установить правильное состояние для сегментированного элемента управления.

Заключение

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

Если вам нужна дополнительная информация о контроллерах представления в macOS, вы можете прочитать эту статью. Он не касается конкретно NSSplitView, но объясняет, как использовать представления контейнера, как выглядит жизненный цикл NSViewController и как использовать NSStoryboardSegue.

Спасибо за прочтение!

Ресурсы