Сбой вложенного ForEach внутри LazyHStack
У меня есть два вложенных s внутри
LazyHStack
LazyHStack {
ForEach(items) { item in
ForEach(item.urls, id: \.self) {
Text($0.absoluteString)
}
}
}
Этот фрагмент компилируется, но сразу вылетает со следующей ошибкой
Неустранимая ошибка: каждый элемент макета может возникнуть только один раз: файл SwiftUI, строка 0
Я читал в Интернете, что это может быть связано с неправильным различением элементов коллекции, даже если все они идентифицируются. В моем случае
items
во внешнем все идентифицируемы, в то время как внутренние
ForEach
проходит через массив необязательных
URL?
объекты. Я пытался сделать
URL
можно идентифицировать с помощью своей абсолютной строки (которая, я думаю, должна быть уникальной), но это не сработало.
extension URL: Identifiable {
var id: String? { absoluteString }
}
Я должен добавить, что тот же фрагмент кода отлично работает со стандартным
HStack
. Как я могу решить эту проблему?
2 ответа
Вам решать, как именно будет построен пользовательский интерфейс, но этот ответ создает горизонтально прокручиваемый список URL-адресов, которые вертикально уложены как часть каждого элемента.
Рабочий код:
ScrollView(.horizontal) {
LazyHStack {
ForEach(items) { item in
VStack {
ForEach(item.urls) {
Text($0.absoluteString)
}
}
}
}
}
Изменения:
- Завернутый в
ScrollView
, чтобы элементы за пределами экрана можно было прокрутить для просмотра. - Между s вставлен a, чтобы определить макет для каждого
item
. - Удаленный
id: \.self
со второго, потому что вы уже создали кастом для URL. Либо используйтеid: \.id
или не включайтеid
параметр вForEach
.
Результат:
Другая возможность - установить уникальный идентификатор для каждого элемента. В принципе, если несколько URL-адресов одинаковы (следовательно, имеют одинаковый идентификатор), то
LazyHStack
проблема в том, что не все идентификаторы уникальны. Ссылка на аналогичный ответ здесь . Это альтернативное исправление, которое не требует
VStack
между:
Text($0.absoluteString)
.id(item.id.uuidString + ($0.id ?? ""))
Изменить для поддержки дополнительных URL-адресов
Структура данных (разница только в
URL
был заменен на
URLItem
поэтому мы можем хранить необязательное значение):
struct Item: Identifiable {
let id = UUID()
let urls: [URLItem]
}
struct URLItem: Identifiable {
let id = UUID()
let url: URL?
init(_ url: URL?) {
self.url = url
}
}
Новые данные примера:
let items: [Item] = [
Item(urls: [
URLItem(URL(string: "https://www.google.com")), URLItem(URL(string: "https://www.stackoverflow.com"))
]),
Item(urls: [
URLItem(URL(string: "https://www.stackoverflow.com")), URLItem(URL(string: ""))
])
]
Это означает, что теперь мы можем иметь
Identifiable
необязательные URL-адреса. Теперь код должен выглядеть так:
ForEach(item.urls) {
if let url = $0.url {
Text(url.absoluteString)
.id($0.id)
} else {
Text("Bad URL")
.id($0.id)
}
}
Теперь вы можете заниматься своими делами, когда
$0.url
является
nil
.
Он работает, у вас была ошибка в коде:
например, вы должны использовать
self
в вашем расширении
также вы использовали 2 ForEach внутри вместе без причины, я бы сказал, плохое кодирование, я не собираюсь советовать использовать 2 ForEach внутри вместе, это сбивает систему и ваше приложение, используйте 1 ForEach! Однако вот ваш ответ:
struct ContentView: View {
@State private var items: [[URL]] = [[URL(string: "www.apple.com")!, URL(string: "www.amazon.com")!, URL(string: "www.google.com")!], [URL(string: "www.Tesla.com")!]]
var body: some View {
LazyHStack {
ForEach(items, id: \.self) { item in
ForEach(item) { url in
Text(url.absoluteString)
}
}
}
}
}
extension URL: Identifiable {
public var id: String { self.absoluteString }
}