SwiftUI:无法在ForEach循环中调用.frame

时间:2020-01-30 22:46:27

标签: 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 = [
    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闭包不是单个表达式的事实导致编译器抱怨

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

这促使我添加了返回类型。但是带来了这个问题的主题,即编译器错误:

无法将“某些视图”类型的返回表达式转换为“图像”类型

似乎frame()调用的返回值不是Image,而是ModifiedContent<SwiftUI.Image, SwiftUI._FrameLayout>

我已经发现(感谢评论者!)我可以通过(以某种方式)将ForEach闭包简化为单个表达式来解决这个问题,这使返回类型变得难以理解,然后可以将其删除。这是我唯一的资源吗?

3 个答案:

答案 0 :(得分:1)

据我所知,这可能只是Swift的一种局限性,类似于此类型推断问题:Why can't the Swift compiler infer this closure's type?

我的解决方法是向Item结构添加功能。现在ForEach实例提供了item闭包内部所需的每个计算,它们直接插入Image初始化程序,如下所示:

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气味测试。)

所以我称其为“答案”,甚至是“解决方案”,尽管我仍然会抱怨无用的错误消息。但这是一个已知问题,比我聪明的人已经在解决这个问题。

答案 1 :(得分:0)

现在,我必须考虑一下。但是要消除第一个错误,需要使 Item 与可识别的内容保持一致。

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)
    }
  }
}

答案 2 :(得分:0)

这是这里

setproctitle(3)

您明确指定闭包返回ForEach(items) { item -> Image in ,但是Image返回frame,因此键入不匹配和编译器错误。

按如下所述使用,它将起作用

some View