В уроке Apple, на который вы указали, они использовали «.forEach» внутри замыкания Path, который является «нормальным» замыканием.SwiftUI, использует новую функцию swift под названием «построители функций».{ brackets }
после ZStack может выглядеть как обычное закрытие, но это не так!
См., Например.https://www.swiftbysundell.com/posts/the-swift-51-features-that-power-swiftuis-api для получения дополнительной информации о компоновщиках функций.
По сути, это "компоновщик функций" (точнее, ViewBuilder
, в данном случае; подробнее: https://developer.apple.com/documentation/swiftui/viewbuilder) получить массиввсех операторов в «замыкании», или, скорее, их значений. Ожидается, что в ZStack эти значения будут соответствовать протоколу View
.
Когда вы запустите someArray.forEach {...}
, он ничего не вернет, void, также известный как ()
. Но ViewBuilder ожидал чего-то, соответствующего протоколу View
! Другими словами:
Невозможно преобразовать значение типа '()' в закрывающий тип результата '_ '
Конечно, это не может! Тогда как мы можем сделать цикл / forEach, который возвращает то, что нам нужно?
Опять же, просматривая документацию SwiftUI, в разделе "Просмотр макета и представления "->" Списки и прокрутки ", мы получаем: ForEach , что позволяет нам описывать итерацию декларативно, вместо обязательного циклического перемещения по позициям: https://developer.apple.com/documentation/swiftui/foreach
Когда состояние представления изменяется, SwiftUI восстанавливаетсоздает структуру, описывающую представление, сравнивает ее со старой структурой, а затем только вносит необходимые исправления в реальный пользовательский интерфейс, чтобы сохранить производительность и обеспечить более привлекательную анимацию и т. д. Чтобы это можно было сделать, нужно уметьидентифицировать каждый элемент, например,ForEach (например, чтобы отличить вставку новой точки от просто изменения существующей).Таким образом, мы не можем просто передать массив CGPoints непосредственно в ForEach (по крайней мере, не добавив расширение в CGPoint, чтобы они соответствовали протоколу Identifiable).Мы могли бы создать структуру оболочки:
import SwiftUI
var positions: [CGPoint] = [
CGPoint(x: 100, y: 100),
CGPoint(x: 100, y: 200),
CGPoint(x: 100, y: 300),
]
struct Note: Identifiable {
let id: Int
let position: CGPoint
let text: String
}
var notes = positions.enumerate().map { (index, position) in
// using initial index as id during setup
Note(index, position, "Point \(index + 1) at position \(position)")
}
struct ContentView: View {
var body: some View {
ZStack {
ForEach(notes) { note in
Text(note.text)
.position(note.position)
}
}
}
}
Затем мы могли бы добавить возможность нажимать и перетаскивать заметки.При нажатии на заметку, мы можем захотеть переместить ее на вершину ZStack.Если бы на ноте воспроизводилась какая-либо анимация (например, при изменении ее положения во время перетаскивания), она обычно останавливалась (поскольку весь вид заметки был бы заменен), но поскольку структура заметки теперь равна Identifiable
, SwiftUI поймет, чтоон был только перемещен и вносил изменения, не мешая анимации.
См. https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-views-in-a-loop-using-foreach или https://medium.com/@martinlasek/swiftui-dynamic-list-identifiable-73c56215f9ff для более подробного урока:)
примечание: код не был проверен (gah beta Xcode)