[SWIFT] Try to implement iOS14 Widget function

In this article, I will introduce and implement the Widget function of iOS14.

environment OS:Catlina 10.15.4 Xcode:12.2 beta2

1. What is Widget?

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.

2, mounting order

Widget Extension added

After creating a new multi-platform project in SwiftUI, add Widget Extension. By the way, you can add multiple Widget Extensions in one app.

Xcode → File → New → Target → Widget Extension If the widgetDemoW target is added successfully, the time view will be displayed by default.

スクリーンショット 2020-10-17 14.42.47.png スクリーンショット 2020-10-17 14.45.41.png

Widget entrance

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 Widgets 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.

2020-10-17 16.39.39.png 2020-10-17 16.39.39.png 2020-10-17 16.39.39.png You can start this application by clicking Widget. Press and hold Widget to start editing.

3, customize the display contents of Widget

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).

Implementation example 1

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()
    }
}

Actual display

スクリーンショット 2020-10-17 23.51.37.png

Implementation example 2

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.

Actual display

2020-10-17 16.39.39.png 2020-10-17 16.39.39.png 2020-10-17 16.39.39.png

4, the end

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

Try to implement iOS14 Widget function
Try to implement login function with Spring-Boot
Try to implement login function with Spring Boot
Try to implement tagging function using rails and js
Try to implement Yubaba in Ruby
[Java] Try to implement using generics
Try to implement Yubaba in Java
Try to implement n-ary addition in Java
[Swift] How to implement the countdown function
How to implement TextInputLayout with validation function
Try to implement UIAlertController by separating files
[Swift] How to implement the LINE login function
[swift5] How to implement the Twitter share function
How to implement the breadcrumb function using gretel
[Android] Implement a function to display passwords quickly
[For beginners] How to implement the delete function
Flow to implement image posting function using ActiveStorage
[Swift] How to implement the fade-in / out function
Try to implement TCP / IP + NIO with JAVA
Continued ・ Flow to implement image posting function using ActiveStorage
[Rails] Implement search function
Try to implement using Rakuten product search API (easy)
14 Corresponds to function expressions
Rails learning How to implement search function using ActiveModel
I tried to implement the like function by asynchronous communication
[iOS] [Objective-C] How to update a widget from an Objective-C app
I want to implement a product information editing function ~ part1 ~
[Swift] I tried to implement the function of the vending machine
[IOS] What you need to know before creating a widget
[Rails] I tried to implement "Like function" using rails and js
Implement application function in Rails
Try Spring Boot from 0 to 100.
Java to play with Function
How to add ActionText function
Implement follow function in Rails
Implement category function using ancestory
[iOS] How to read Carthage
[Rails] How to implement scraping
[Rails] Implement image posting function
[Java] How to implement multithreading
Implement search function with form_with
[Rails] A simple way to implement a self-introduction function in your profile
I tried to implement Ajax processing of like function in Rails
[Swift] How to implement the Twitter login function using Firebase UI ①
I tried to implement the image preview function with Rails / jQuery
[Behavior confirmed in December 2020] How to implement the alert display function
[Swift] How to implement the Twitter login function using Firebase UI ②