Array.forEach создает ошибку «Невозможно преобразовать значение типа '()' в закрывающий тип результата '_'" - PullRequest
0 голосов
/ 06 июля 2019

Я пытаюсь перебрать массив в SwuftUI, чтобы отобразить несколько текстовых строк в разных местах.Работа с массивом вручную работает, но использование цикла forEach приводит к ошибке.В приведенном ниже примере кода я прокомментировал ручной подход (который работает).Этот подход использовался в этом руководстве для рисования линий (https://developer.apple.com/tutorials/swiftui/drawing-paths-and-shapes)

(В качестве дополнительного вопроса: есть ли способ получить индекс / ключ отдельных позиций с помощью этого подхода, без добавления этого ключа вк позициям-массиву для каждой из строк?)

Я пробовал различные подходы, такие как ForEach, добавлял в нем также определенные () и добавляя различные определения типов для замыкания, но я просто заканчиваю тем, что создалдругие ошибки

import SwiftUI

var positions: [CGPoint] = [
    CGPoint(x: 100, y: 100),
    CGPoint(x: 100, y: 200),
    CGPoint(x: 100, y: 300),
]

struct ContentView : View {
    var body: some View {
        ZStack {
            positions.forEach { (position) in
                Text("Hello World")
                    .position(position)
            }
/* The above approach produces error, this commented version works
           Text("Hello World")
                .position(positions[0])
            Text("Hello World")
                .position(positions[1])
            Text("Hello World")
                .position(positions[2]) */
        }
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

Ответы [ 2 ]

1 голос
/ 06 июля 2019

Не все действительно в теле представления.Если вы хотите выполнить цикл for-each, вам нужно использовать специальное представление ForEach:

https://developer.apple.com/documentation/swiftui/foreach

Для представления требуется идентифицируемый массив, а для идентифицируемого массива -элементы, чтобы соответствовать Hashable.Ваш пример должен быть переписан так:

import SwiftUI

extension CGPoint: Hashable {
    public func hash(into hasher: inout Hasher) {
        hasher.combine(x)
        hasher.combine(y)
    }
}
var positions: [CGPoint] = [
    CGPoint(x: 100, y: 100),
    CGPoint(x: 100, y: 200),
    CGPoint(x: 100, y: 300),
]

struct ContentView : View {
    var body: some View {
        ZStack {
            ForEach(positions.identified(by: \.self)) { position in
                Text("Hello World")
                    .position(position)
            }
        }
    }
}

В качестве альтернативы, если ваш массив не идентифицируем, вы можете сойти с него:

var positions: [CGPoint] = [
    CGPoint(x: 100, y: 100),
    CGPoint(x: 100, y: 200),
    CGPoint(x: 100, y: 300),
]

struct ContentView : View {
    var body: some View {
        ZStack {
            ForEach(0..<positions.count) { i in
                Text("Hello World")
                    .position(positions[i])
            }
        }
    }
}
0 голосов
/ 06 июля 2019

В уроке 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)

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