WidgetKit Series 3 : Solve the padding issue contentMarginsDisabled
In this tutorial, we'll walk through the process of creating a basic widget for iOS using SwiftUI and WidgetKit. We'll build a widget that displays the current date and an emoji, updating every hour.
Setting Up the Widget
First, let's break down the main components of our widget:
- Provider: Manages the timeline of our widget's content.
- Entry: Defines the data structure for our widget's content.
- EntryView: Describes the layout and appearance of our widget.
- Widget: Configures and presents our widget.
The Provider
The Provider
struct conforms to TimelineProvider
and is responsible for generating the content timeline for our widget:
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> DateEntry {
DateEntry(date: Date(), emoji: "🤓")
}
func getSnapshot(in context: Context, completion: @escaping (DateEntry) -> ()) {
let entry = DateEntry(date: Date(), emoji: "🤓")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [DateEntry] = []
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = DateEntry(date: entryDate, emoji: "🤓")
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
This provider creates a timeline with five entries, each an hour apart.
The Entry
Our DateEntry
struct defines the data for each timeline entry:
struct DateEntry: TimelineEntry {
let date: Date
let emoji: String
}
The EntryView
The WidgetTestWidgetEntryView
struct defines how our widget looks:
struct WidgetTestWidgetEntryView : View {
var entry: DateEntry
var body: some View {
ZStack {
ContainerRelativeShape()
.fill(LinearGradient(
gradient: Gradient(colors: [.cyan, .green]),
startPoint: .topLeading,
endPoint: .bottomTrailing
))
VStack {
HStack {
Text("\(entry.emoji)")
Text(entry.date.formatted(.dateTime.weekday(.wide)))
}
}
}
}
}
This view creates a gradient background and displays the emoji and weekday.
The Widget
Finally, we define our widget using the Widget
protocol:
struct WidgetTestWidget: Widget {
let kind: String = "WidgetTestWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
if #available(iOS 17.0, *) {
WidgetTestWidgetEntryView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
} else {
WidgetTestWidgetEntryView(entry: entry)
.padding()
.background(Color.black)
}
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
.contentMarginsDisabled()
}
}
This configuration sets up our widget, handling both iOS 17+ and earlier versions.
Preview
To see how our widget looks, we can use the Preview
provider:
#Preview(as: .systemSmall) {
WidgetTestWidget()
} timeline: {
DateEntry(date: .now, emoji: "🤓")
DateEntry(date: .now, emoji: "😎")
}
This preview shows our widget in the small size with two different emojis.
Conclusion
And there you have it! We've created a simple yet functional widget that displays the current weekday and an emoji, updating every hour. This example demonstrates the basics of working with WidgetKit and SwiftUI to create engaging, glanceable experiences for iOS users.
Remember, widgets are meant to provide quick, relevant information. As you build upon this example, consider what information would be most useful to your users at a glance.
Happy coding!