Вопрос или проблема
Я попробовал несколько различных способов создать сетку значков, теперь, когда я делаю это, вместо вызова многоразовых компонентов у меня есть многомерный массив Views.
Вот код, и я постараюсь объяснить ошибки, которые меня окружали и сделали невозможным решить эту проблему:
static let itemMatrix: [[any View]] = [
[ChargeBeam(), VariaSuit(), MorphBall(), HiJumpBoots(), Missiles()],
[IceBeam(), GravitySuit(), MorphBallBomb(), SpaceJump(), SuperMissiles()],
[WaveBeam(), EmptyCell(), SpringBall(), SpeedBooster(), PowerBombs()],
[Spazer(), GrappleBeam(), ScrewAttack(), EmptyCell(), EnergyTanks()],
[PlasmaBeam(), XRayScope(), EmptyCell(), EmptyCell(), ReserveTanks()]
]
Вы можете заметить скобки () после каждого представления. Потому что я подумал, что если я поставлю скобки здесь, разве я не должен ставить их, когда действительно запускаю представление в ContentView? Но если я не ставлю скобки здесь… появляется эта ошибка:
Тип выражения неясен без аннотации типа
И она исправляется только тогда, когда я ставлю скобки после каждого View. Теперь вы видите “any View” в объявлении вместо View… это потому, что когда это View, у меня появляется эта ошибка:
Использование протокола 'View' в качестве типа должно быть записано как 'any View'
Так что, когда я это делаю, тогда в ContentView.swift происходит следующее:
Тип 'any View' не может соответствовать 'View'
ContentView.swift:
private var itemGrid: some View {
return VStack(spacing: 4) {
ForEach(0..<5) { row in
HStack(spacing: 12) {
ForEach(0..<5) { col in
AppConstants.itemMatrix[row][col]
}
}
}
}
.padding(0)
}
У меня нет понятия, почему каждый раз, когда я делаю шаг, чтобы что-то исправить или сделать то, что говорит компилятор, в итоге появляется новая ошибка из-за моего исправления.
Ответ или решение
Вопрос с ошибками, возникающими при работе с многомерным массивом Views в Swift, может быть довольно запутанным. Давайте рассмотрим вашу проблему подробнее и предложим решение, чтобы вы могли успешно отобразить сетку иконок.
Понимание проблемы
Вы работаете с многомерным массивом, который представляет собой сетку компонентов пользовательского интерфейса в Swift, и сталкиваетесь с ошибками, связанными с использованием протокола View
. Основные проблемы, которые вы описали, связаны с использованием any View
и вызовом компонент просмотра с использованием скобок.
Ошибки и их причины
-
Ошибка с "Type of expression is ambiguous without a type annotation":
Это происходит, когда компилятор не может вывести тип, поэтому он требует явного указания типа. Добавление скобок после вашего View (например,ChargeBeam()
) позволяет Swift понять, что вы имеете в виду конкретные экземпляры этих View. -
Ошибка с "Use of protocol ‘View’ as a type must be written ‘any View’":
В Swift, начиная с версии 5.7, протоколы необходимо использовать с предшествующим словомany
, если имеется вероятность, что они будут ссылаться на различные типы. Это также объясняет необходимость использованияany View
вместоView
. -
Ошибка с "Type ‘any View’ cannot conform to ‘View’" в
ContentView.swift
:
Эта ошибка возникает, поскольку компилятору не известен точный типany View
. Компилятор не может обрабатыватьany View
в контекстеForEach
, который ожидает конкретные типы.
Решение проблемы
Рассмотрим правильный способ работы с многомерными массивами Views и использования их в SwiftUI:
-
Создайте массив с конкретными типами Views:
Вместо использованияany View
, вы можете попробовать использовать конкретные типы. В вашем случае можно попробовать создать массив, который содержит определенные типы Views. -
Используйте
Identifiable
или модели данных:
Вместо прямой работы сany View
, вы можете создать структуру данных, которая будет представлять каждую ячейку вашей сетки и будет реализовывать протоколIdentifiable
. Это упростит работу сForEach
.
Пример реализации
Вот обновленная версия вашего кода:
import SwiftUI
struct AppConstants {
static let itemMatrix: [[AnyView]] = [
[AnyView(ChargeBeam()), AnyView(VariaSuit()), AnyView(MorphBall()), AnyView(HiJumpBoots()), AnyView(Missiles())],
[AnyView(IceBeam()), AnyView(GravitySuit()), AnyView(MorphBallBomb()), AnyView(SpaceJump()), AnyView(SuperMissiles())],
[AnyView(WaveBeam()), AnyView(EmptyCell()), AnyView(SpringBall()), AnyView(SpeedBooster()), AnyView(PowerBombs())],
[AnyView(Spazer()), AnyView(GrappleBeam()), AnyView(ScrewAttack()), AnyView(EmptyCell()), AnyView(EnergyTanks())],
[AnyView(PlasmaBeam()), AnyView(XRayScope()), AnyView(EmptyCell()), AnyView(EmptyCell()), AnyView(ReserveTanks())]
]
}
struct ContentView: View {
private var itemGrid: some View {
return VStack(spacing: 4) {
ForEach(0..<5, id: \.self) { row in
HStack(spacing: 12) {
ForEach(0..<5, id: \.self) { col in
AppConstants.itemMatrix[row][col]
}
}
}
}
.padding(0)
}
var body: some View {
itemGrid
}
}
Объяснение изменений
- AnyView: Я обернул каждый ваш View в
AnyView
, чтобы устранить проблему с неоднородностью типов. - Идентификатор для
ForEach
: Я добавилid: \.self
в вашForEach
, чтобы компилятор понимал, как один идентификатор может применяться для массивов индексов.
Эти изменения должны помочь вам избавиться от текущих ошибок. Не забывайте многократно тестировать свой код, так как SwiftUI может порой вызывать неожиданные проблемы, особенно при работе с динамически изменяемыми данными.
Заключение
Работа с многомерными массивами Views в Swift может быть сложной, особенно при использовании функций, ожидающих определенные типы. Использование AnyView
может решить вашу проблему в данном контексте. Если у вас возникнут дополнительные вопросы или проблемы, не стесняйтесь задавать!