My question is simply as the title states. I want to take some View, apply a view modifier to it, and still be able to keep it as that original type instead of it becoming a some View
.
Say we have a simple view like this:
struct SomeView: View {
let image: Image
var body: some View {
image
}
}
In the PreviewProvider, we can test this out with a system image from SF Symbols:
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
Group {
SomeView(image: Image.init(systemName: "pawprint"))
.previewDisplayName("pawprint")
}
.previewLayout(.sizeThatFits)
}
}
And it works:
Now let's try to add a second preview using the same SF Symbol but applying the .font
view modifier to increase the size:
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)
}
Yep, that's about right, because the view modifier .font
returns the opaque type some View
. But if we apply the "Fix-it button" suggestion to force-cast (sure, let's do that, for science), then it compiles, but crashes the previewer. Also crashes if we try to run in a simulator, so it's not just a Previewer bug.
And yet, there's no problem at all just displaying it as 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)
So, how can I do something like this on a View where I apply a view modifier, but I can still use the original View type?
CodePudding user response:
No. And because most of the view modifier types are unknown to us (such as whatever backs the font
modifier), you'll have to emulate
Tested with Xcode 13.2 / iOS 15.2
Alternate 1: If it is only about image mocking and SomeView
is restricted to Image
only, then it is possible to use configured UIImage
, like
SomeView(image: Image(uiImage: UIImage(systemName: "pawprint", withConfiguration: UIImage.SymbolConfiguration(pointSize: 64))!))
.previewDisplayName("SomeView bigPawprint")
Alternate 2: Use generics, like
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)
}
}