Layout 迅捷:Can';ForEach循环中的t调用.frame

Layout 迅捷:Can';ForEach循环中的t调用.frame,layout,foreach,swiftui,Layout,Foreach,Swiftui,TL;DR:如何修改ForEach结构中的内容 下面是一个自包含的游乐场,其中对frame()的调用在普通body方法中是可以的,但在ZStack/ForEach循环中包装时是语法错误 import UIKit import SwiftUI struct Item: Identifiable { var id = UUID() var name: String init(_ name:String) { self.name = name } } let items =

TL;DR:如何修改
ForEach
结构中的内容

下面是一个自包含的游乐场,其中对
frame()
的调用在普通
body
方法中是可以的,但在
ZStack/ForEach
循环中包装时是语法错误

import UIKit
import SwiftUI

struct Item: Identifiable {
    var id = UUID()
    var name: String
    init(_ name:String) { self.name = name }
}

let items = [
    Item("a"), Item("b"), Item("c")
]

struct ContentView: View {    
    var body: some View {
        return Image("imageName")
            .resizable()
            .frame(width:0, height:0)          // This compiles.
    }

   var body2: some View {
        ZStack {
            ForEach(items) { item -> Image in   // Return type required...
            let i = item                        // ...because of this line.
            return Image(i.name)
                    .resizable()                // Parens required.
                    .frame(width: 0, height: 0) // Compile error.
            }
        }
    }
}

注意行
let i=item
。它代表着我的应用程序中执行一些计算的代码。
ForEach
闭包不是一个表达式,这一事实导致编译器抱怨

无法推断复杂的闭包返回类型;添加显式类型以消除歧义

这促使我添加了返回类型。但这就引出了这个问题的主题,编译器错误:

无法将“some View”类型的返回表达式转换为返回类型“Image”

frame()
调用的返回值似乎不是
Image
,而是
ModifiedContent


我发现(多亏了评论者!)我可以通过(以某种方式)将
ForEach
闭包简化为一个表达式来解决这个问题,这使得返回类型变得可推断,然后我可以删除它。这是我唯一的办法吗?

现在我要考虑网格。但要消除第一个错误,需要将与可识别项一致

struct Item: Identifiable {
  var id = UUID()
  var name: String
  var size: CGSize = .zero
}
我还必须编写一个自定义修改器,用于使用该项创建框架。我们可能希望使用CGRect来创建网格,但要想办法搞乱图像的原点

extension View {
  func frame(with size: CGSize) -> some View {
    frame(width: size.width, height: size.height)
  }
}
然后你的身体会像这样

var body: some View {
  ZStack {
    ForEach(items) { item in
      Image(item.name).resizable().frame(with: item.size)
    }
  }
}
这是这里

ForEach(items) { item -> Image in
您明确指定闭包返回
图像
,但
返回
某些视图
,因此类型不匹配和编译器错误

使用如下,它将工作

ForEach(items) { item in

据我所知,这可能只是Swift的一个限制,类似于或属于这种类型的推理问题:

我的解决方法是向
结构添加功能。现在,
ForEach
闭包中需要的每个计算都由
实例提供,与
图像
初始值设定项串联,如下所示:

var body3: some View {
        ZStack {
            ForEach(items) { item in        // No return type specified.

            // let (width, height) = ...    // Remove pre-calculations that
                                            // confused the compiler.
            Image(item.name)
                .resizable()
                .frame(
                    width : item.width,     // All needed data are
                    height: item.height     // provided in closure param.
                )
            }
        }
    }
}
我承认这在习惯上更实用,尽管我更喜欢在调用之前选择一些清晰的任务。(如果分配的计算没有副作用,则基本上是SSA样式,应该通过FP气味测试。)


所以我称之为“答案”,甚至是“解决方案”,尽管我仍然可以抱怨那些毫无帮助的错误信息。但这是一个众所周知的问题,比我更聪明的人已经在这个问题上了。

物品已经可以识别了。如果不是,则在前面三行中会出现完全不同的编译错误。我忘了提到我正在使用的视图扩展,与您的类似;我更新了问题。为了简化这个问题,我省略了原点变换。从闭包的签名中删除
->图像
(正如您所做的那样)会导致另一个错误:“无法推断复杂的闭包返回类型”,即使没有对
框架
的麻烦调用也是如此。我还注意到您在调用
resizeable
时忽略了paren。简言之,我认为你的答案无法编译。我在操场上复制了我的问题,并编辑了问题以提供代码。删除了不相关的扩展名。我可以确认1)调用
resizeable
时需要参数,2)编译错误仍然存在。但是,请参见我对@Asperi的回复。您的代码和我的代码在闭包的返回类型上有所不同,并且您的版本(省略返回类型)可以满足编译器的需要,而不是我的原始代码。所以你被证明是正确的。这应该是一个很好的线索。我认为要修复项类型的推断,您只需要向项数组添加显式类型。let items:[Item]=[…]请参阅@Asperi的进一步评论。谢谢你激励我使用游乐场。我理解类型不匹配。如果我接受您的建议并在实际代码中删除返回类型,我会得到错误“无法推断复杂的闭包返回类型;添加显式类型以消除歧义”。但是您的建议确实阻止了(简化)游乐场示例中的错误。所以现在我必须弄清楚有什么不同。我重新编辑了这个问题,把重点放在我发现的问题上:当存在多个表达式时,编译器在推断返回类型方面的问题。因此有一个解决方法:重写代码以避免多表达式体。也许这就足够了。