Array.forEach创建错误“无法将类型'()'的值转换为闭包结果类型'_'". [英] Array.forEach creates error "Cannot convert value of type '()' to closure result type '_'"

查看:151
本文介绍了Array.forEach创建错误“无法将类型'()'的值转换为闭包结果类型'_'".的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to loop through an array in SwuftUI to render multiple text strings in different locations. Going through the array manually works, but using forEach-loop produces an error. In the code sample below, I have commented out the manual approach (which works). This kind of approach worked in this tutorial for drawing lines (https://developer.apple.com/tutorials/swiftui/drawing-paths-and-shapes)

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

(As a bonus question: is there a way to get the index/key of the individual positions through this approach, without adding that key in to the positions-Array for each of the rows?)

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

I have tried various approaches like ForEach, adding identified() in there as well, and adding various type definitions for the closure, but I just end up creating other errors

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

推荐答案

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

In the Apple tutorial you pointed to, they used the ´.forEach´ inside the Path closure, which is a "normal" closure. SwiftUI, uses a new swift feature called "function builders". The { brackets } after ZStack might look like a usual closure, but it's not!

参见例如 https://www.swiftbysundell.com/帖子/the-swift-51-features-that-power-swiftuis-api ,以获取有关函数构建器的更多信息.

See eg. https://www.swiftbysundell.com/posts/the-swift-51-features-that-power-swiftuis-api for more about function builders.

本质上,函数构建器"(在这种情况下,更具体地说是ViewBuilder;请阅读更多信息:View协议.

In essence, the "function builder" (more specifically, ViewBuilder, in this case; read more: https://developer.apple.com/documentation/swiftui/viewbuilder) get an array of all the statements in the "closure", or rather, their values. In ZStack, those values are expected to be conforming to the View protocol.

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

When you run someArray.forEach {...}, it will return nothing, void, also known as (). But the ViewBuilder expected something conforming to the View protocol! In other words:

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

Cannot convert value of type '()' to closure result type '_'

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

Of course it can't! Then, how might we do a loop/forEach that returns what we want?

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

Again, looking at the SwiftUI documentation, under "View Layout and Presentation" -> "Lists and Scroll Views", we get: ForEach, which allows us to describe the iteration declaratively, instead of imperatively looping through the positions: https://developer.apple.com/documentation/swiftui/foreach

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

When a view's state changes, SwiftUI regenerates the struct describing the view, compares it with the old struct, and then only makes the necessary patches to the actual UI, to save performance and allow for fancier animations, etc. To be able to do this, it needs to be able to identify each item in eg. a ForEach (eg. to distinguish an insert of a new point from just a change of an existing one). Thus, we can't just pass the array of CGPoints directly to ForEach (at least not without adding an extension to CGPoint, making them conform to the Identifiable protocol). We could make a wrapper struct:

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将理解为它只是被移动了,并且在不干扰任何动画的情况下进行了更改.

We could then add the ability to tap-and-drag the notes. When tapping a note, we might want to move it to the top of the ZStack. If any animation was playing on the note (for instance, changing its position during drag), it would normally stop (because the whole note-view would be replaced), but because the note struct now is Identifiable, SwiftUI will understand that it's only been moved, and make the change without interfering with any animation.

请参见 https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-views-in-a-loop-using-foreach

See https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-views-in-a-loop-using-foreach or https://medium.com/@martinlasek/swiftui-dynamic-list-identifiable-73c56215f9ff for a more in depth tutorial :)

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

这篇关于Array.forEach创建错误“无法将类型'()'的值转换为闭包结果类型'_'".的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆