1

我的问题很简单,正如标题所述。我想获取一些视图,对其应用视图修饰符,并且仍然能够将其保持为原始类型,而不是变成some View.

假设我们有一个像这样的简单视图:

struct SomeView: View {
    let image: Image

    var body: some View {
        image
    }
}

在 PreviewProvider 中,我们可以使用来自 SF Symbols 的系统映像进行测试:

struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            SomeView(image: Image.init(systemName: "pawprint"))
                .previewDisplayName("pawprint")
        }
        .previewLayout(.sizeThatFits)
    }
}

它有效:

掌印

现在让我们尝试使用相同的 SF Symbol 添加第二个预览,但应用.font视图修改器来增加大小:

    static var previews: some View {
        Group {
            SomeView(image: Image.init(systemName: "pawprint"))
                .previewDisplayName("SomeView pawprint")

            // Error: Cannot convert value of type 'some View' to expected argument type 'Image'
            SomeView(image: Image.init(systemName: "pawprint").font(.system(size: 64)))
                .previewDisplayName("SomeView bigPawprint")
        }
        .previewLayout(.sizeThatFits)
    }

是的,这是正确的,因为视图修饰符.font返回不透明类型some View。但是,如果我们将“修复按钮”建议应用于强制转换(当然,为了科学,让我们这样做),那么它会编译,但会使预览器崩溃。如果我们尝试在模拟器中运行也会崩溃,所以这不仅仅是一个预览器错误。

然而,将其显示为完全没有问题some View

        Group {
            SomeView(image: Image.init(systemName: "pawprint"))
                .previewDisplayName("SomeView pawprint")

            // Crashes
            // SomeView(image: Image.init(systemName: "pawprint").font(.system(size: 64)) as! Image)
                .previewDisplayName("SomeView big pawprint")

            // Works
            Image.init(systemName: "pawprint").font(.system(size: 64))
                .previewDisplayName("View Modifier big pawprint")
        }
        .previewLayout(.sizeThatFits)

视图修饰符大爪印

那么,我怎样才能在我应用视图修饰符的视图上做这样的事情,但我仍然可以使用原始视图类型?

4

2 回答 2

1

不。而且因为我们不知道大多数视图修饰符类型(例如支持font修饰符的任何内容),所以您必须ModifiedContent使用闭包而不是Modifier实例来模拟。

struct SomeView<ModifiedImage: View>: View {
  let image: Image
  let modify: (Image) -> ModifiedImage

  var body: some View {
    modify(image)
  }
}

extension SomeView where ModifiedImage == Image {
  init(image: Image) {
    self.init(image: image) { $0 }
  }
}
SomeView(image: .init(systemName: "pawprint"))
  .previewDisplayName("SomeView pawprint")

SomeView(image: .init(systemName: "pawprint")) {
  $0.font(.system(size: 64))
}
.previewDisplayName("SomeView bigPawprint")
于 2022-02-18T05:47:41.807 回答
1

.font修饰符应用于所有子视图,因此可能的方法是将其向上移动一个级别,例如

struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            SomeView(image: Image(systemName: "pawprint"))
                .previewDisplayName("SomeView pawprint")

            SomeView(image: Image(systemName: "pawprint"))
                .font(.system(size: 64))                     // << here !!
                .previewDisplayName("SomeView bigPawprint")
        }
        .previewLayout(.sizeThatFits)
    }
}

演示

使用 Xcode 13.2 / iOS 15.2 测试

替代1:如果它只是关于图像模拟并且SomeView仅限于Image,那么可以使用已配置UIImage的,比如

SomeView(image: Image(uiImage: UIImage(systemName: "pawprint", withConfiguration: UIImage.SymbolConfiguration(pointSize: 64))!))
    .previewDisplayName("SomeView bigPawprint")

备选方案 2:使用泛型,例如

struct SomeView<V: View>: View {
    let image: V

    var body: some View {
        image
    }
}

struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            SomeView(image: Image.init(systemName: "pawprint"))
                .previewDisplayName("SomeView pawprint")

            // this works
            SomeView(image: Image.init(systemName: "pawprint").font(.system(size: 64)))
                .previewDisplayName("SomeView bigPawprint")
        }
        .previewLayout(.sizeThatFits)
    }
}
于 2022-02-18T05:53:25.097 回答