Array.forEach创建错误“无法将类型'()'的值转换为闭合结果类型'_'”

时间:2019-07-06 17:00:32

标签: foreach swiftui

我试图遍历SwuftUI中的数组以在不同位置呈现多个文本字符串。手动遍历数组是可行的,但是使用forEach-loop会产生错误。 在下面的代码示例中,我注释了手动方法(有效)。 这种方法在本教程中适用于绘制线条(https://developer.apple.com/tutorials/swiftui/drawing-paths-and-shapes

(作为一个额外的问题:是否有一种方法可以通过这种方法获取各个位置的索引/键,而无需将该键添加到每一行的positions-Array中?)

我尝试了诸如ForEach之类的各种方法,还在其中添加了identified(),并为闭包添加了各种类型定义,但是我最终还是制造了其他错误

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 个答案:

答案 0 :(得分:1)

并非所有内容在视图主体中都是有效的。如果要执行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])
            }
        }
    }
}

答案 1 :(得分:0)

在您指出的Apple教程中,他们在Path闭包(这是一个“常规”闭包)中使用了“ .forEach”。 SwiftUI使用了一个新的快速功能,称为“功能构建器”。 ZStack之后的{ brackets }看起来像是普通的闭包,但事实并非如此!

参见例如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 {...}时,它将不返回任何内容,也称为()。但是ViewBuilder期望符合View协议!换句话说:

  

无法将类型'()'的值转换为闭合结果类型'_'

当然不能!然后,我们如何做一个循环/ forEach,返回我们想要的东西?

再次,查看SwiftUI文档,在“视图布局和表示”->“列表和滚动视图”下,我们得到: ForEach ,它使我们可以声明性地描述迭代,而不是命令性地遍历以下职位:https://developer.apple.com/documentation/swiftui/foreach

当视图的状态更改时,SwiftUI会重新生成描述视图的结构,将其与旧结构进行比较,然后仅对实际的UI进行必要的修补,以节省性能并提供更精美的动画等。为此,它需要能够识别例如中的每个项目。 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-foreachhttps://medium.com/@martinlasek/swiftui-dynamic-list-identifiable-73c56215f9ff,以获取更深入的教程:)

注意:该代码尚未经过测试(Gah Beta Xcode)