In this article, I will introduce and implement the Widget function of iOS14.
environment OS:Catlina 10.15.4 Xcode:12.2 beta2
Apple has provided an API that allows you to access apps with simple functions such as calendars and timers to the notification center that appears when you swipe left from the home screen from ʻiOS 8, but with ʻiOS 14
the home screen It is now possible to display that. The functionality has been further increased, and the display contents can be updated along the timeline.
**! Important: ** Since the Widget
function can only be implemented in SwiftUI
, the essence of Widget
can be said to be a SwiftUI
view that changes depending on the timeline.
After creating a new multi-platform project in
SwiftUI
, addWidget Extension
. By the way, you can add multipleWidget Extension
s in one app.
Xcode → File → New → Target → Widget Extension If the widgetDemoW target
is added successfully, the time view will be displayed by default.
In the widgetDemoW.swift class, the @ main
mark is the entrance to the Widget
.
struct widgetDemoWEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.date, style: .time)
}
}
@main
struct widgetDemoW: Widget {
let kind: String = "widgetDemoW"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
widgetDemoWEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
.supportedFamilies([.systemMedium])
}
}
--configurationDisplayName
is the display name of the Widget
when the user adds or edits the Widget
of the app (because there can be multiple Widget
s in one app).
-- description
is the description of Widget
--supportedFamilies
is the surpot size, and the default is large, medium, and small all surpots.
** provider ** is the object that determines the timeline for updating the Widget
. The system can optimize the update process by providing future time to update the Widget
.
** EntryView ** of widgetDemoWEntryView is the view that is actually displayed.
You can start this application by clicking Widget
. Press and hold Widget
to start editing.
Updates to Widget
are fully controlled by WidgetCenter
. Developers cannot actively update the Widget
view via the API. You can only notify Widget Center that you need to update the timeline.
On the system, it provides two ways to drive the reload of the timeline. System Reloads
and ʻApp-Driven Reloads`.
** Note: ** Reloading the timeline does not update Widget
directly, but WidgetCenter
reclaims Widget
for the next stage of data. The Provider
in the timeline is the object that provides this data.
There are two parts to the data provided by Provider
in the timeline. One is TimelineEntry
and the other is ReloadPolicy
.
TimelineEntry
is the view information and point in time that Widget
should display under a particular time node.
ReloadPolicy
is the timeline update policy during the next period. There are three types.
** atEnd **: Indicates that the timeline will be updated when the last time slice is reached. ** atAfter **: Refers to regular updates after a certain period of time. ** never **: That means it doesn't need to be updated in the future. When the last ʻEntry
is displayed, it will stop at the last ʻEntry
without updating.
In addition to launching the app, Widget
can also contain multiple buttons that transition to different pages (medium / large size Widget
only).
Static display
struct widgetDemoWEntryView : View {
var entry: Provider.Entry
var body: some View {
HStack {
Text("In this article, I will introduce and implement the Widget function of iOS14.-20201017")
.foregroundColor(.black)
VStack {
Image("widgetPic")
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.purple, lineWidth: 3))
.shadow(radius: 10)
.cornerRadius(10.0)
Text(entry.date, style: .time)
}
.padding()
}
.padding()
}
}
Display along the timeline
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), text: "There is coming from far away. It's not fun again.", configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), text: "There is coming from far away. It's not fun again.", configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
//Generates a timeline consisting of 60 entries at hourly intervals, starting from the current date
let currentDate = Date()
var textArray = [String]()
for i in 0..<60 {
textArray.append("There is coming from far away. It's not fun again. timeline: \(i)")
}
for minOffset in 0 ..< 60 {
let entryDate = Calendar.current.date(byAdding: .minute, value: minOffset, to: currentDate)!
let entryText = textArray[minOffset]
let entry = SimpleEntry(date: entryDate, text: entryText, configuration: configuration)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let text: String
let configuration: ConfigurationIntent
}
struct widgetDemoWEntryView : View {
var entry: Provider.Entry
var body: some View {
HStack {
Text(entry.text)
.foregroundColor(.black)
VStack {
Image("widgetPic")
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(Color.purple, lineWidth: 3))
.shadow(radius: 10)
.cornerRadius(10.0)
Text(entry.date, style: .time)
}
.padding()
}
//On the application side, handle the URL through onOpenURL and realize the action to transition to a specific screen by tapping Widget
.widgetURL(URL(string: "widgetdemo://go2secondview"))
.padding()
}
}
@main
struct widgetDemoW: Widget {
let kind: String = "widgetDemoW"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
widgetDemoWEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
.supportedFamilies([.systemMedium])
}
}
struct widgetDemoW_Previews: PreviewProvider {
static var previews: some View {
widgetDemoWEntryView(entry: SimpleEntry(date: Date(), text: "There is coming from far away. It's not fun again.", configuration: ConfigurationIntent()))
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
}
The Goku icon is statically displayed and updates the content and time of the analects in 1 minute increments.
The idea of WidgetKit
is very good, but due to OS performance and power consumption, Widget
cannot display video and dynamic images. However, I think that if you use it well, it will surely lead to the improvement of the quality of the application, so I hope that a better Widget
will come out!
Recommended Posts