Reputation: 17241
One of the big strengths of SwiftUI is modularity: I don't need to build one large view (controller), I can easily compose my main view from many smaller view components. But developing these view components efficiently requires working live previews.
I noticed that within a Widget target, SwiftUI previews don't work – unless you append the viewContext
modifier to the view you want to preview as follows:
struct MyWidgetComponent_Previews: PreviewProvider {
static var previews: some View {
MyWidgetComponent()
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
}
With this modifier, I must choose between one of four fixed widget sizes:
.systemSmall
.systemMedium
.systemLarge
.systemExtraLarge
As a result, my preview will always be squeezed (or expanded) to match the respective widget size. While that makes sense for the main widget view, it's an annoying and useless limitation for widget components.
For example, if I want to create a widget that displays a list of my 5 favorite movies, I would typically create a view for a single movie row and then "for each" that in the widget's main view.
struct MainWidgetView: View {
let movies: [String]
var body: some View {
VStack(alignment: .leading, spacing: 12) {
ForEach(Array(zip(movies.indices, movies)), id: \.0) { movie in
MovieRow(rating: movie.0, title: movie.1)
}
}
.padding()
}
}
The MovieRow
might be implemented like this:
struct MovieRow: View {
let rating: Int
let title: String
var body: some View {
HStack(spacing: 16) {
Text("\(rating)")
.bold()
.overlay(
Circle()
.stroke(.blue, style: .init(lineWidth: 2))
.padding(-8)
)
Text(title)
}
}
}
When I now want to preview a single MovieRow
, I'll have to inject the previewContext
:
struct MovieRow_Previews: PreviewProvider {
static var previews: some View {
MovieRow(rating: 5, title: "Star Trek IV: The Voyage Home")
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
}
where I actually want a custom size or (even better:) self-sizing.
It might not be a big issue in this example where the text fits nicely into the widget size with only some padding around it, but I can think of many situations where squeezing or expanding a view component is completely impractical an distorts the outcome.
Is there a way to preview isolated views that are used to compose a widget without providing a view context and use a custom size (or self-sizing) instead?
If so, how?
Upvotes: 2
Views: 2295
Reputation: 1027
Yes, you can use .previewLayout
for that. It provides .fixed(width:height:)
and .sizeThatFits
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewLayout(.fixed(width: 300, height: 300))
ContentView()
.previewLayout(.sizeThatFits)
}
}
Upvotes: 1