iOS14 Widget Development Stepping Pit (1) Überarbeitete Version – Erste Bekanntschaft und Aktualisierung

Vorwort

Überarbeitete Version vom 23. Dezember 2020, einige Beschreibungen und Fehler überarbeitet

Hier sind einige Fallstricke, auf die ich während des Entwicklungsprozesses gestoßen bin, in der Hoffnung, dass sie für die Entwicklung nützlich sind. Bei den in diesem Artikel enthaltenen Codes handelt es sich lediglich um Beispielcodes, die lediglich Ideen liefern und nicht direkt kopiert und verwendet werden können. Für den Vergleich sind einige Kenntnisse in der Entwicklung des Today Widgets erforderlich. Ein Teil des Inhalts dieses Artikels stammt aus dem Internet. Bei Verstößen wenden Sie sich bitte an, um ihn zu löschen.

Entwicklungshinweise

  1. WidgetExtension verwendet das neue WidgetKit, das sich vom Today Widget unterscheidet.Es kann nur mit SwiftUI entwickelt werden, daher sind SwiftUI und Swift Foundation erforderlich.
  2. Widget unterstützt nur 3 Größen systemSmall (2x2), systemMedium (4x2), systemLarge (4x4)
  3. Klicken Sie auf das Widget, um standardmäßig die Hauptanwendung zu öffnen
  4. Widget ähnelt TodayWidget, einem unabhängig laufenden Programm. Es muss App-Gruppen im Projekt festlegen , damit es mit dem Hauptprogramm kommunizieren kann. Dies wird später erläutert.
  5. Apple hat die Today-Erweiterung offiziell aufgegeben und Xcode12 bietet das Hinzufügen der Today-Erweiterung nicht mehr an. Anwendungen, die bereits über ein Today-Widget verfügen, werden in einem bestimmten Bereich zur Anzeige angezeigt.

Vorbereitung

Bereitstellungsumgebung

Für die Widget-Entwicklung müssen Xcode 12 und iOS 14 installiert sein. Offizieller Download-Link von Apple

Projekt erstellen

Der normale Prozess zum Erstellen eines Projekts, ich verwende die Swift-Sprache, die Schnittstelle Storyboard, kann auf die Konfiguration eingestellt werden, die Sie gewohnt sind: Erstellen Sie
ein neues Xcode-Projekt -> Geben Sie den Produktnamen ein -> Weiter -> Erstellen

Einführung der Widget-Erweiterung

  1. Datei -> Neu -> Ziel -> Widget-Erweiterung -> Weiter
  2. Da ein neues Ziel hinzugefügt wird, kann der Name des Widgets nicht mit dem Projektnamen identisch sein und es kann auch nicht „Widget“ heißen (da Widget ein vorhandener Klassenname ist). Beim Löschen können Sie die Datei nicht einfach löschen aber auch in den Zielen des Projekts. Löschen, der Name, der einmal gelöscht wurde, meldet einen Fehler, dass die Datei nicht gefunden werden kann.
  3. Wenn das Widget Benutzerkonfigurationsattribute unterstützt (z. B. die Wetterkomponente, der Benutzer kann eine Stadt auswählen), müssen Sie die Option „Konfigurationsabsicht einbeziehen“ aktivieren und nicht aktivieren, wenn sie nicht unterstützt wird. Es wird empfohlen, es zu überprüfen, wer weiß, ob in Zukunft um Unterstützung gebeten wird.
  4. Nach der Erstellung werden automatisch 5 Strukturen und ihre eigenen Methoden generiert

fang an zu schreiben

Kenne den Code

Vorschauansicht – Vorschauen

Die Vorschauansicht des ausgeführten Codes ist eine neue Funktion von SwiftUI. Sie zeigt die Ausführungsergebnisse in der rechten Ansicht an und unterstützt Hot-Updates, was für Entwickler praktisch ist, um SwiftUI-Ansichten zu debuggen und zu verwenden. Sie ist kein notwendiger Teil von Widget und kann gelöscht oder direkt kommentiert werden.

struct MainWidget_Previews: PreviewProvider {
    
    
    static var previews: some View {
    
    
        MainWidgetEntryView(entry: SimpleEntry(date: Date()))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}

Datenanbieter-Anbieter

Der Anbieter ist der wichtigste Teil des Widgets, der die Anzeige der drei Daten placeholder/getSnapshot/getTimeline des Widgets bestimmt. Wenn „Konfigurationsabsicht einschließen“ beim Erstellen des Projekts aktiviert ist, erbt der Anbieter von IntentTimelineProvider, um Benutzerkonfigurationsdaten zu unterstützen. Wenn die Option nicht aktiviert ist, erbt er von TimelineProvider und unterstützt keine Benutzerkonfigurationsdaten. Dies wird später besprochen.


struct Provider: TimelineProvider {
    
    
    func placeholder(in context: Context) -> SimpleEntry {
    
    
        SimpleEntry(date: Date())
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
    
    
        let entry = SimpleEntry(date: Date())
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    
    
        var entries: [SimpleEntry] = []

        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
    
    
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

Die getSnapshot- Methode dient dazu, Vorschaudaten bereitzustellen, die es Benutzern ermöglichen, eine allgemeine Situation der Komponente zu sehen, wie sie aussieht und welche Daten angezeigt werden. Sie können als feste Daten geschrieben werden. Ausländische Artikel nennen es „gefälschte Informationen“. Was auf
dieser Schnittstelle angezeigt wird, sieht so aus: (Nehmen Sie iWidget als Beispiel)

Wo der Schnappschuss angezeigt wird

Die getTimeline- Methode ist das Aktualisierungsereignis, wenn das Widget auf dem Desktop angezeigt wird, und gibt eine Timeline-Instanz zurück, die alle anzuzeigenden Elemente enthält: die erwartete Anzeigezeit (das Datum des Elements) und die „Ablaufzeit“ des Zeitleiste.
Da das Widget-Programm seinen zukünftigen Zustand nicht wie eine Wetteranwendung „vorhersagen“ kann, kann es ihm lediglich in Form einer Zeitleiste mitteilen, welche Daten zu welchem ​​Zeitpunkt angezeigt werden sollen.

Datenmodell – SimpleEntry

Das Modell des Widgets, in dem Date die Eigenschaft von TimelineEntry ist, spart Zeit beim Anzeigen von Daten und kann nicht gelöscht werden. Sie müssen darunter benutzerdefinierte Eigenschaften hinzufügen:

struct SimpleEntry: TimelineEntry {
    
    
    let date: Date
    xxxxx
}

Schnittstelle – MainWidgetEntryView

Die vom Widget angezeigte Ansicht. Bearbeiten Sie die Schnittstelle in dieser Ansicht, zeigen Sie Daten an oder rufen Sie sie hier auf, nachdem Sie die Ansicht angepasst haben. Darüber hinaus kann ein Widget Schnittstellen in drei Größen direkt unterstützen.

struct MainWidgetEntryView : View {
    
    
	@Environment(\.widgetFamily) var family
    var entry: Provider.Entry
    var body: some View {
    
    
		switch family {
    
    
        case .systemSmall: Text("小尺寸界面")
        case .systemMedium: Text("中尺寸界面")
        default: Text("大尺寸界面")
        }
	}
}

Eintrag-MainWidget

Die Haupteingabefunktion des Widgets kann den Titel und die Beschreibung des Widgets festlegen, seine angezeigte Ansicht, den Anbieter, die unterstützte Größe und andere Informationen angeben.

@main
struct MainWidget: Widget {
    
    
    let kind: String = "MainWidget"// 标识符,不能和其他Widget重复,最好就是使用当前Widgets的名字。

    var body: some WidgetConfiguration {
    
    
        StaticConfiguration(kind: kind, provider: Provider()) {
    
     entry in
            MainWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")//Widget显示的名字
        .description("This is an example widget.")//Widget的描述
        .supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
    }
}

Auf die Grube gestoßen

getTimeline ist die erste Grube,iOS14 Widget kann Daten nicht aktiv aktualisieren! ! !
Das Heute-Widget kann aktiv die neuesten Daten abrufen und wird direkt vom Programm gesteuert, das iOS 14-Widget jedoch nicht. Das System fragt das Widget nur nach einer Reihe von Daten und zeigt die erhaltenen Daten entsprechend der aktuellen Uhrzeit an. Da der Code nicht aktiv ausgeführt wird, ist er eher voreingenommenstillDie Darstellung von Informationen, auch Animationen und Videos, ist untersagt.
Das bedeutet, dass wir nur im Voraus schreiben können, welche Daten beim nächsten Mal für das Widget angezeigt werden sollen, und diese in eine Zeitleiste umwandeln, die das System lesen und anzeigen kann. Wir können Daten jedoch automatisch anfordern und aktualisieren, indem wir eine normale Datenabfrage durchführen und das Abschlussformular ausfüllen. Diese Aktualisierungsmethode unterscheidet sich stark von meinem üblichen Entwicklungsdenken, was dazu geführt hat, dass ich viele Fehler gemacht habe.

func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    
    
        var entries: [SimpleEntry] = []
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
    
    
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }

Der offizielle Beispielcode bedeutet: Zeigen Sie die Uhrzeit jeder Stunde in 5 Stunden an und führen Sie nach der Anzeige erneut getTimeline aus. Nachdem Sie die Bedeutung dieser Methode verstanden haben, können Sie den gewünschten Effekt schreiben. Daher müssen wir nur den Wert der Calendar.Component
, die Aktualisierungszeit und die Anzahl der Elemente in den Einträgen steuern und die Richtlinie von TimeLine festlegen , um die Aktualisierungszeit, -zeiten und -methoden des Widgets zu steuern. Aber nach meinem Test beträgt die höchste Aktualisierungsfrequenz von getTimeline einmal alle 5 Minuten , und es funktioniert nicht, wenn sie höher als diese Frequenz ist. Beim Ausfüllen der Einträge sollten wir diese mit den Daten füllen, die innerhalb von 5 Minuten angezeigt werden müssen.

Beispiel: Um eine Uhr zu implementieren, die jede Sekunde aktualisiert wird, sollten den Einträgen 300 Zeitdaten für 300 Sekunden zur Verfügung gestellt werden , um jede Sekunde so genau wie möglich zu aktualisieren . Wenn die Ansicht angezeigt wird, kann sie in eine Zeichenfolge konvertiert werden ist spezifisch für die Sekunde. Run a Die Zeitdaten werden für 5 Minuten nach dem Zeitraum erneut erfasst.
Dies führt zu einer gewissen Abweichung von 1 bis 3 Sekunden in der zweiten Anzeige, und eine Hochfrequenzaktualisierung führt auch zu einem erhöhten Stromverbrauch und stoppt gelegentlich. Derzeit (23. Dezember 2020) wurde keine passende Lösung gefunden. Wenn ja, chatten Sie bitte privat mit mir! ! ! !

func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    
    
	var currentDate = Date()
	var arr:[SimpleEntry] = []
		for idx in 0...300 {
    
    
			let tempDate = Calendar.current.date(byAdding: .second, value: idx, to: currentDate)!
			let tempEntry = SimpleEntry(date: tempDate)
			arr.append(tempEntry)
		}
		let timeline = Timeline(entries: arr, policy: .atEnd)
		completion(timeline)
    }

Aktualisierung des Hauptprogramms und zweite Grube

Im Hauptprogramm können wir WidgetCenter verwenden , das von WidgetKit bereitgestellt wird , um Widgets zu verwalten, wobei reloadTimelines verwendet wird, um eine Aktualisierung des von uns angegebenen Widgets zu erzwingen, oder reloadAllTimelines , um alle Widgets zu aktualisieren.

WidgetCenter.shared.reloadTimelines(ofKind: "xxx")
WidgetCenter.shared.reloadAllTimelines()

Wenn Ihr Hauptprogramm in Oojective-C geschrieben ist, müssen Sie OC verwenden, um Swift zum Schreiben aufzurufen . Einzelheiten zur Hybridkonfigurationsmethode finden Sie im Referenzdokument „Swift mit gemischter Kompilierung aufrufen“ , da dies bei WidgetKit nicht der Fall ist Schreiben Sie eine OC- Version.

import WidgetKit
@objcMembers class WidgetTool: NSObject {
    
    
    @available(iOS 14, *)
    @objc func refreshWidget(sizeType: NSInteger) {
    
    
        #if arch(arm64) || arch(i386) || arch(x86_64)
        WidgetCenter.shared.reloadTimelines(ofKind: "xxx")
        #endif
    }
}

im Code oben

 #if arch(arm64) || arch(i386) || arch(x86_64)
        xxxx
 #endif

Und

@available(iOS 14, *)

Es ist die zweite Grube.
Erstens wird dieses Urteil hinzugefügt, da das Widget nur ausgeführt werden kann, wenn eine dieser drei Bedingungen erfüllt ist. Wenn dieser Satz nicht hinzugefügt wird, wird beim Packen ein Fehler gemeldet. Diese Lösung wird aus dem Problem-Feedback von Apple Developer gefunden.

Zweitens wurde WidgetKit neu in iOS 14 veröffentlicht , da unser Projekt bis iOS 10 unterstützen muss , sodass wir beim Kompilieren und Verpacken eine Versionsbeurteilung hinzufügen müssen.

Verweise

Ich bin ein Neuling. Bitte korrigieren Sie mich, wenn ich Fehler mache. Ich freue mich darauf, mit Ihnen zu kommunizieren und mich weiterzuentwickeln. Es wird empfohlen, die offizielle Dokumentation zu lesen, bevor Sie nach relevanten Netzwerkinformationen suchen.

„Erstellen einer Widget-Erweiterung“
„Ein Widget auf dem neuesten Stand halten“
„Die Perspektive eines Entwicklers auf iOS 14-Widgets“
„iOS14WidgetKit-Entwicklungspraxis 1-4“
„iOS14-Widget-Entwicklung und Umgang mit fehleranfälligen Bereichen“
„So erstellen Sie Widgets in iOS 14 in Swift“
„SwiftUI-Text“
„Gemischter OC-Aufruf von Swift“

Supongo que te gusta

Origin blog.csdn.net/qq_38718912/article/details/107658804
Recomendado
Clasificación