SwiftUI: понимание декларативного программирования

Запутались декларативным программированием? Не волнуйтесь, это не так сложно понять.

Согласно Apple, SwiftUI - это потрясающая платформа декларативного программирования для создания пользовательских интерфейсов на iOS и других платформах Apple. Так написано прямо на коробке.

Но что значит «декларативный»?

Что ж, мы могли бы начать с обсуждения декларативного программирования и императивного программирования, но это только пинает дело в будущем, поскольку теперь нам нужно определить термин «императивное».

Вместо этого я предпочел бы просто заменить слово в исходном предложении.

SwiftUI - прекрасная функциональная среда программирования для создания пользовательских интерфейсов.

Итак, давайте рассмотрим это определение и посмотрим, к чему оно нас приведет.

Функциональное программирование

Некоторые люди видят слова «функциональное программирование» и начинают волноваться.

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

Не волнуйтесь, здесь мы этого делать не будем.

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

В SwiftUI эти функции возвращают части и части, которые определяют наш пользовательский интерфейс. Или, другими словами, мы используем эти функции для объявления нашего интерфейса.

Начинаешь понимать?

Просмотры

В SwiftUI большинство частей и частей, составляющих наш интерфейс, называются представлениями. Есть какой-нибудь текст? Это взгляд. Картинка? Другой вид. Это список? Ты угадал. Это представление, представляющее собой список представлений.

Но в отличие от сильно перегруженного UIKit, Objective-C диспетчеризации UIViews, представления SwiftUI на самом деле представляют собой всего лишь небольшие фрагменты информации, которые в сочетании друг с другом описывают наш интерфейс.

И как их совместить?

Вложенные функции для победы

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

Не уходи!

Это может показаться странным, но если вы остановитесь, чтобы подумать, вы уже привыкли думать такими терминами. Наш UINavigationController содержит UITableView, списки которого содержат UITableViewCells, которые могут содержать UIStackViews, содержащие UILabels и UIButtons.

Один элемент интерфейса содержит другие элементы, которые содержат другие элементы, пока не вуаля! Мы описали наш интерфейс, и теперь мы переходим к следующему экрану, где делаем то же самое.

В приведенном ниже примере SwiftUI интерфейс, возвращаемый телом представления, сводится к NavigationView, который содержит список, элементы которого являются представлениями HStack, которые, в свою очередь, содержат изображение и некоторый текст.

Синтаксис может выглядеть немного странно, но он поддерживается некоторыми новыми функциями Swift 5.1.

Бум. Мы только что объявили о нашем первом полностью функциональном интерфейсе SwiftUI всего в 15 строках кода.

Модификаторы

«Держись, спорт!» ты говоришь. "Не так быстро. Что, если я хочу, чтобы мой текст был красным? И мне там нужен заголовок, а не основной текст, и ... »

Понятно. Вы хотите изменить внешний вид по умолчанию.

Так как же нам это сделать?

Модификаторы.

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

С концептуальной точки зрения модификатор - это оператор в представлении… который возвращает другое представление…. который можно изменять до бесконечности (или до тех пор, пока у вас не закончится память 😉).

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

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

Это один элемент интерфейса с двумя модификаторами.

Text("Your mileage my vary.")
    .font(.footnote)
    .foregroundColor(.secondary)

Опишите приведенный выше интерфейс для SwiftUI, а он сделает все остальное, разместив текст с отступами и интервалами, соответствующими размеру шрифта и устройству, на котором работает ваше приложение.

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

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

Говоря о которых…

А как насчет наших данных?

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

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

И иногда эта информация определяется нашими данными. В следующем примере мы изменили представление списка, чтобы использовать данные, которые передаются ему при инициализации в другом месте приложения.

Первый параметр в списке сообщает ему, где именно искать данные.

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

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

Динамические данные

Иногда наша информация динамична, подвержена изменениям, и наш интерфейс должен соответствующим образом корректироваться.

Вышеупомянутая реализация практически идентична первой, но с большим отличием, оболочка свойства @Binding добавлена ​​к нашим переменным list и title.

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

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

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

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

Среда

Еще один аспект, о котором следует упомянуть, - это среда.

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

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

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

@EnvironmentObject var settings: UserSettings
@Environment(\.colorScheme) var colorScheme: ColorScheme

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

Под капотом

Итак, вы объявляете свой интерфейс и указываете SwiftUI на свои данные… а SwiftUI делает все остальное.

Каждое представление, состояние его модификаторов, его данные и текущее состояние среды объединяются и агрессивно сводятся к набору команд компоновки и рендеринга. Затем результат представляется пользователю.

Затем SwiftUI ждет изменений в данных. В случае их возникновения SwiftUI просматривает затронутые представления и строит новое дерево макета, сравнивает любые найденные изменения с текущим деревом, а затем визуализирует части представления, требующие обновления.

Это делает SwiftUI невероятно быстрым по сравнению с UIKit. Фактически, большая часть визуализации и визуализации анимации выполняется непосредственно Metal.

Анимации?

Да, в SwiftUI легко делать анимацию. Фактически, определение того, какие анимации могут потребоваться для переходов между состояниями просмотра, встроено непосредственно в процесс сравнения дерева.

Некоторые анимации, такие как вставка и удаление списка, а также анимации, относящиеся к изменениям состояния модификатора представления, встроены непосредственно в систему.

И все они при необходимости могут быть изменены и настроены.

Завершение блока

Итак, это все. Надеюсь, теперь вы понимаете, что Apple имеет в виду, когда говорит, что SwiftUI - это новая декларативная среда программирования для создания пользовательских интерфейсов на iOS и других платформах Apple.

И что модификатор удивительный [sic] хорошо заработан.

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

Серия SwiftUI продолжается: