I am attempting to layout a tableView using SwiftUI and WidgetKit and would like to achieve a similar result of that as the Apple's Notes widget.
My current implementation succeeds in laying out the view in the .systemLarge
widget, but not in the .systemMedium
widget. I would like to pin the view to the top of the widget, such that the header of "FAVOURITES" is visible in the .systemMedium
.
struct PlacesWidgetEntryView : View {
var entry: Provider.Entry
let places = [
Place(name: "Place 1", imageName: "baseline_star_black_24pt"),
Place(name: "Place 2", imageName: "baseline_star_black_24pt"),
Place(name: "Place 3", imageName: "baseline_star_black_24pt"),
Place(name: "Place 4", imageName: "baseline_star_black_24pt"),
Place(name: "Place 5", imageName: "baseline_star_black_24pt"),
]
var body: some View {
VStack {
//Header
HStack {
Text("FAVOURITES")
.bold()
.frame(height: 8)
Spacer()
}
.padding()
.background(Color.blue)
//TableView
LazyVStack {
ForEach(places, id: \.self) { place in
PlaceRow(place: place)
}
}
Spacer()
}
}
}
struct PlaceRow: View {
let place: Place
var body: some View {
HStack {
Text(place.name)
.font(.body)
Spacer()
Image(place.imageName)
.resizable()
.frame(width: 28, height: 28, alignment: .center)
}
.padding(.horizontal)
.padding(.vertical, 4)
}
}
Implementation outcome:
The above is .systemLarge
, which is good, and as per what I'm expecting.
The above is .systemMedium
, which is not what I'm expecting. I would like to see "Favourites" anchored to the top of the widgetView, and potentially the tableView overflowing to the bottom.
Here is possible layout solution. Tested with Xcode 12.
var body: some View {
VStack(spacing: 0) {
HStack {
Text("FAVOURITES")
.bold()
.frame(height: 8)
Spacer()
}
.padding()
.background(Color.blue)
Color.clear
.overlay(
LazyVStack {
ForEach(places, id: \.self) { place in
PlaceRow(place: place)
}
},
alignment: .top)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
backup
So from playing around in WidgetKit, it seems like if there are too many views in a widget, it starts to push the upper views off the screen. If you add more places to the array, you'll see the same thing happen with your large widget. What you can do is create separate views: one for the medium and one for the large widget, and for the medium one, just use 1-3 of your place objects to populate it.
You can use a switch statement in your PlacesEntryWidgetView with the widgetFamily to decide what you want to show on the view. I also slightly reduced the height of the image from 28 to 24.
struct PlacesWidgetEntryView : View {
var entry: Provider.Entry
@Environment(\.widgetFamily) var family
let places = [
Place(name: "Place 1", imageName: "blackStar"),
Place(name: "Place 2", imageName: "blackStar"),
Place(name: "Place 3", imageName: "blackStar"),
Place(name: "Place 4", imageName: "blackStar"),
Place(name: "Place 5", imageName: "blackStar"),
Place(name: "Place 6", imageName: "blackStar"),
Place(name: "Place 7", imageName: "blackStar"),
Place(name: "Place 8", imageName: "blackStar")
]
@ViewBuilder
var body: some View {
switch family {
case .systemMedium:
// widget can only show so many views so I only took first 3 places
WidgetView(places: Array(places.prefix(3)))
case .systemLarge:
WidgetView(places: places)
// I only have it set to show system medium so you can ignore
// the last case
case .systemSmall:
Text("")
@unknown default:
Text("")
}
}
}
struct WidgetView: View {
let places: [Place]
var body: some View {
VStack {
//Header
HStack {
Text("FAVOURITES")
.bold()
.frame(height: 8)
Spacer()
}
.padding()
.background(Color.blue)
//TableView
LazyVStack {
ForEach(places, id: \.self) { place in
PlaceRow(place: place)
}
}
Spacer()
}
}
}
struct PlaceRow: View {
let place: Place
var body: some View {
HStack {
Text(place.name)
.font(.body)
Spacer()
Image(place.imageName)
.resizable()
.frame(width: 28, height: 24, alignment: .center)
}
.padding(.horizontal)
.padding(.vertical, 4)
}
}
This is the preview:
Keep in mind, that you might want to switch between the simulators to make sure your widgets look good on all devices.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With