Array.forEach创建错误“;无法转换类型为';()';关闭结果类型'_'&引用;

Array.forEach创建错误“;无法转换类型为';()';关闭结果类型'_'&引用;,foreach,swiftui,Foreach,Swiftui,我试图在SwuftUI中循环一个数组,以呈现不同位置的多个文本字符串。手动遍历数组是可行的,但使用forEach循环会产生错误。 在下面的代码示例中,我已经注释掉了手动方法(它是有效的)。 这种方法在本教程中用于绘制直线() (作为一个额外的问题:有没有一种方法可以通过这种方法获取各个职位的索引/关键字,而不必将该关键字添加到每行的职位数组中?) 我尝试过各种方法,比如ForEach,在其中添加identified(),以及为闭包添加各种类型定义,但最终我只创建了其他错误 import Swif

我试图在SwuftUI中循环一个数组,以呈现不同位置的多个文本字符串。手动遍历数组是可行的,但使用forEach循环会产生错误。 在下面的代码示例中,我已经注释掉了手动方法(它是有效的)。 这种方法在本教程中用于绘制直线()

(作为一个额外的问题:有没有一种方法可以通过这种方法获取各个职位的索引/关键字,而不必将该关键字添加到每行的职位数组中?)

我尝试过各种方法,比如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

视图主体中并非所有内容都有效。如果要执行for each循环,则需要使用特殊视图ForEach:

视图需要一个可识别数组,而可识别数组需要元素符合Hashable。您的示例需要重写如下:

导入快捷界面
扩展点:可散列{
公共func散列(放入散列程序:inout散列程序){
hasher.combine(x)
hasher.combine(y)
}
}
变量位置:[CGPoint]=[
CGPoint(x:100,y:100),
CGPoint(x:100,y:200),
CGPoint(x:100,y:300),
]
结构ContentView:View{
var body:一些观点{
ZStack{
ForEach(positions.identified(by:\.self)){position in
文本(“你好,世界”)
.职位(职位)
}
}
}
}
或者,如果您的阵列无法识别,您可以通过以下方式解决:

var位置:[CGPoint]=[
CGPoint(x:100,y:100),
CGPoint(x:100,y:200),
CGPoint(x:100,y:300),
]
结构ContentView:View{
var body:一些观点{
ZStack{

在你提到的苹果教程中,他们在路径闭包中使用了´.ForEach',这是一个“普通”闭包。SwiftUI使用了一个新的swift特性,称为“函数生成器”。ZStack之后的
{brates}
看起来像是一个普通的闭包,但事实并非如此

有关函数生成器的更多信息,请参见

本质上,“函数生成器”(更具体地说,在本例中为
ViewBuilder
;阅读更多:)获取“闭包”中所有语句的数组,或者更确切地说,获取它们的值。在ZStack中,这些值应符合
视图
协议

当您运行
someArray.forEach{…}
时,它将不返回任何内容,void,也称为
()
。但是ViewBuilder期望符合
视图
协议!换句话说:

无法将类型“()”的值转换为闭包结果类型“\u1”

当然不能!那么,我们怎么做一个返回我们想要的东西的循环/forEach呢

再次查看SwiftUI文档,在“视图布局和表示”->“列表和滚动视图”下,我们得到:ForEach,它允许我们以声明的方式描述迭代,而不是强制循环位置:

当视图的状态发生变化时,SwiftUI会重新生成描述视图的结构,将其与旧结构进行比较,然后仅对实际UI进行必要的修补,以节省性能并允许更精彩的动画等。要做到这一点,SwiftUI需要能够识别ForEach中的每个项目(例如,区分新点的插入和现有点的更改)。因此,我们不能直接将CGPoints数组传递给ForEach(至少不能不向CGPoint添加扩展,使其符合可识别协议)。我们可以制作一个包装结构:

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的顶部。如果在注释上播放任何动画(例如,在拖动过程中更改其位置),它通常会停止(因为整个注释视图将被替换),但由于note结构现在是可识别的,SwiftUI将理解它只是被移动了,并且在不干扰任何动画的情况下进行更改

有关更深入的教程,请参见或:)


注意:代码还没有经过测试(gah beta Xcode)

谢谢,这很有效!…所以我在Apple教程()中尝试的方法之所以有效,是因为循环在一个路径中?(你必须向下滚动到步骤4才能看到.forEach的用法)是的,你是对的。在布置视图时,有很多限制。它不是真正的swift。谢谢!我自己也测试了它,这很有效。必须学习更多基础知识…
code
struct ContentView:View{var body:some View{Path{Path in Path.move(to:positions[2])positions.forEach{(position)在path.addLine(to:position)}}.fill(Color.black)}
code
我添加了另一种方法,它不需要Hashable或可识别的一致性。还要注意,您的数组尚未声明为bindable,如果以后发生更改,SwiftUI无法知道它,并且您的视图将不会更新。如果您希望视图随数组中的更改而更新,则需要创建BindableObject、 状态或环境对象。所有这些都在WWDC2019会话226-通过SwiftUI的数据流中进行了详细解释。强烈建议您观看。感谢您提供了非常清晰易懂的答案!注意,我尝试为.forEach循环返回的内容添加了一些定义,但这些会导致更混乱的错误…现在我明白了为什么,我应该改用ForEach。再次感谢!