Tehnografi.com - Технологические новости, обзоры и советы

Работа со списком в SwiftUI

Следующая статья поможет вам: Работа со списком в SwiftUI

Вернуться в блог

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

Вот во что мы вникнем:

  • Как использовать List и ForEach для создания динамических пользовательских интерфейсов на основе списков
  • Важные аспекты списка, такие как идентифицируемый и его источник данных.
  • Использование List в сочетании с ForEach для создания более качественных списков
  • Удаление объектов из списка с помощью ForEach и onDelete()
  • Создание секционированного списка с вложенными ForEach и Раздел

Давайте посмотрим на представление списка в SwiftUI. Самая базовая настройка списка на самом деле довольно проста.

List(movies, id: \.id) { фильм в
Текст(фильм.название)
}

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

В приведенном выше коде мы работаем с массивом объектов Movie. Вот код структуры Movie:

struct Movie: Опознаваемый {
вар идентификатор = UUID()
вар заголовок:Строка
}
Структура Movie соответствует протоколу Identificable. Любой тип, использующий протокол Identifying, должен иметь свойство id, которое обеспечивает «стабильную» идентификацию объекта. По сути это означает, что свойство id должно быть уникальным для каждого объекта. Для этого мы используем UUID, но вы также можете использовать, например, целочисленный индексный ключ из базы данных.

Это массив объектов Movie, на которых основано представление списка:

@State var фильмы = [
Movie(title: “Episode IV – A New Hope”),
Movie(title: “Episode V – The Empire Strikes Back”),
Movie(title: “Episode VI – Return of the Jedi”),

]
Давайте подробнее рассмотрим код, который мы ранее использовали для представления списка:

List(movies, id: \.id) { фильм в
Текст(фильм.название)
}

Инициализатор представления списка имеет 3 параметра:

  1. Данные безымянного параметра, которые мы хотим отображать в представлении списка. В нашем примере этими данными является массив фильмов.
  2. Ключевой путь для параметра id, который указывает свойство Movie, которое мы хотим использовать для уникальной идентификации объектов в массиве фильмов. (Параметр id здесь является избыточным, вы можете использовать оба параметра: id и Identifying.)
  3. Замыкание параметра rowContent; содержимое отдельных строк списка. Поскольку это последний параметр, мы можем использовать синтаксис замыкания.

Последний параметр является наиболее интересным. Это замыкание, которое предоставляет содержимое для каждой строки в списке. Подобно карте(_:), она вызывается для каждого элемента в фильмах и должна возвращать представление для отображения. В приведенном выше коде мы просто возвращаем текстовое представление. Мы также используем предоставленный объект фильма.

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

Представление «Список» по сути является контейнером для подпредставлений, которые можно прокручивать. Он похож на такие представления, как VStack и HStack, а также UITableViewController для приложений на основе раскадровки.

Фактически, вы можете создать простое статическое представление списка из нескольких подпредставлений:

Список {
Текст («Братство Кольца»)
Текст («Две башни»)
Текст («Возвращение короля»)
}

Что делает List особенным, так это то, что он может динамически создавать подпредставления, используя источник данных и небольшой код, который предоставляет подпредставления для отдельных элементов. А в SwiftUI вы можете комбинировать List с ForEach, чтобы сделать еще больше.

Вот пример:

Список {
ForEach(movies, id: \.id) {фильм в
Текст(фильм.название)
}
}

В приведенном выше коде мы отделили предыдущие параметры для List и добавили их в структуру ForEach.

Структура ForEach вычисляет представления по запросу из коллекции фильмов, аналогично тому, как это делал List. Эти представления являются подпредставлениями List, что означает, что вы получите тот же результат, что и при использовании только List.

Так в чем же преимущество использования ForEach? Прежде всего, ForEach больше похож на генератор или поставщик данных, чем на представление. Использование ForEach также дает вам доступ к таким действиям с данными, как onDelete(), onInsert() и onMove(), чего List не может.

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

HStack {
Для каждого([“File”, “Edit”, “View”]id: \.hash) { заголовок в
Текст (название)
}
}

Вы также можете использовать ForEach с onDelete() для удаления элементов из источника данных, лежащего в основе списка. Вот пример:

Список {
ForEach(movies, id: \.id) {фильм в
Текст(фильм.название)
}
.onDelete { indexSet в
для индекса в indexSet {
фильмы.удалить(по адресу: index)
}
}
}

Модификатор onDelete() вызывается, когда вы проводите по элементу списка вправо, как если бы вы удаляли элемент в iOS. Это возможность, которая также встроена в повсеместное табличное представление, и, конечно же, в List она тоже есть.

Однако, чтобы это работало, вам необходимо пометить источник данных как состояние. В нашем примере это означает, что нам придется добавить оболочку свойства @State к свойству Movies.

@State var фильмы =

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

Вам не нужно кодировать представление для строк списка внутри списка. Вы также можете выделить их в отдельное представление и ссылаться на представление в списке. Что-то вроде List() { MovieRow() }.

Если List будет просто отображать несколько строк в списке, а ForEach будет генерировать представления на основе источника данных… это означает, что мы можем использовать оба для создания секционированных списков. Давайте узнаем как!

Во-первых, мы собираемся внести несколько изменений в объекты Movie. Так:

struct Movie: Опознаваемый {
вар идентификатор = UUID()
вар заголовок:Строка
вар период:Период
}

Структура Movie теперь имеет свойство period, которое мы будем использовать для создания секционированного списка. Каждому периоду фильма соответствует свой раздел списка.

Вот перечисление периода:

период перечисления: String, CaseIterable {
case original = «Оригинальная трилогия»
case prequel = «Трилогия приквела»
продолжение дела = «Продолжение трилогии»
случай автономный = «Автономный»
}

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

Дальше, конечно же, нам понадобятся фильмы!

вар фильмы = [
Movie(title: “Episode IV – A New Hope”, period: .original),
Movie(title: “Episode V – The Empire Strikes Back”, period: .original),
Movie(title: “Episode VI – Return of the Jedi”, period: .original),

Movie(title: “Star Wars: The Clone Wars”, period: .standalone),
Movie(title: “Rogue One”, period: .standalone),
Movie(title: “Solo”, period: .standalone),
Movie(title: “The Mandalorian”, period: .standalone)
]

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

Список {
ForEach(Period.allCases, id: \.rawValue) {период в
Раздел (заголовок: Текст (период.rawValue)) {
ForEach(movies.filter { $0. period == period }) { фильм в
ВСтек {
Текст(фильм.название)
Текст(movie. period.rawValue)
}
}
}
}
}

Как это работает?

  • С высоты птичьего полета вы можете видеть, что мы создали список с двумя вложенными структурами ForEach. Сначала мы перебираем разделы и для каждого раздела перебираем фильмы в нем.
  • Структура ForEach для Period.allCases будет циклически перебирать все случаи Period. Они однозначно идентифицируются по необработанному значению, т. е. по строке. (Из-за отсутствия лучшей альтернативы это не сработает для повторяющихся строк!)
  • Представление «Раздел» обеспечивает группировку одного заголовка и нескольких строк. Вы можете видеть, что мы используем текстовое представление, которое содержит точку в качестве заголовка. Имейте в виду, что эти заголовки создаются для каждого случая перечисления Period.
  • Внутри каждого раздела мы создаем еще одну структуру ForEach. В качестве источника данных используется Movies.filter { }, который будет возвращать элементы из фильмов, для которых их период совпадает с периодом раздела.
  • Наконец, на самом внутреннем уровне мы создаем два простых текстовых представления для отображения названия фильмов и периода, к которому они относятся. Аккуратный!

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

Несмотря на то, что мы используем динамические конструкции, такие как ForEach, представления фиксированы и статичны до тех пор, пока данные не изменятся. С таким же успехом мы могли бы объединить все эти фильмы, разделы и стеки в одну большую иерархию кода. ForEach предназначен для нас, программистов, поэтому мы можем меньше писать и больше создавать. Потрясающий!

В этом уроке мы обсудили, как работает List и как вы можете использовать его для создания макетов в виде строк/столбцов в SwiftUI. Вот что вы узнали:

  • Как вы можете использовать List для создания пользовательских интерфейсов
  • Почему объекты источника данных списка должны быть идентифицируемыми
  • Использование списка вместе с ForEach
  • Как удалить объекты из списка и ответить @State
  • Создание секционированного списка с помощью вложенного ForEach