iOS 14 Widget開発関連及びエラー箇所処理について詳しく説明してください。
まず、どうやって作成するかを知ります。
Widgetがユーザーの設定属性をサポートしているなら、これをチェックして(例えば、天気コンポーネント、ユーザーは都市を選択できます)、サポートしていないならチェックしなくてもいいです。
Widgetを作成すると、システムが作成したファイルの内容を理解してください。
以下のコードは、Include Cofigration Intentをチェックしていないところです。
プロバイダー
公式解釈:A type that specifees the date to display a widget、and、optionlly、indicates the current relevance of the widget's content.
もしあなたのアプリにClass Prfixが設定されているなら、この下のConfigrationIntent.selfには対応するプレフィックスが必要です。
例えば、プレフィックスがXYなら、XYConfigrationIntent.selfに変更する必要があります。
Widgetは、system Small、system Medium、system Largeの3つの表示方法をサポートしています。
smallはwidgetUrlのみで処理できます。
私達がfunc timelineでこの方法でデータを求めて写真のリンクを持ってきたら、同期して画像を解析しなければなりません。そうでないと、対応するWidgetViewに直接ロードしてもらえません。
正しい書き方
Widget関連資料
Widgets
Creating a Widget Extension
Keeping a Widget Up To Date
Making a Configrable Widget
ここでiOS 14 Widgetの開発に関する記事を紹介します。iOS 14 Widgetの開発内容については、以前の文章を検索したり、以下の関連記事を見たりしてください。これからもよろしくお願いします。
Xcode -> File -> New -> Target Widget Extension
Widgetがユーザーの設定属性をサポートしているなら、これをチェックして(例えば、天気コンポーネント、ユーザーは都市を選択できます)、サポートしていないならチェックしなくてもいいです。
Widgetを作成すると、システムが作成したファイルの内容を理解してください。
以下のコードは、Include Cofigration Intentをチェックしていないところです。
プロバイダー
// Provider, struct
struct Provider: TimelineProvider {
public typealias Entry = SimpleEntry
// , ,
public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {
}
// , entry , completion
public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
//
Network.request { data in
let entry = SimpleEntry(date: renderDate, data: data)
let timeline = Timeline(entries: [entry], policy: .after(nextRequestDate))
completion(timeline)
}
}
}
Entry公式解釈:A type that specifees the date to display a widget、and、optionlly、indicates the current relevance of the widget's content.
//
struct SimpleEntry: TimelineEntry {
let date: Date
let data: Data
}
Placehodle View
// , 、 、 view
struct PlaceholderView : View {
}
Widget EntryView
// view
struct StaticWidgetEntryView : View {
}
メインエントランス
@main
struct StaticWidget: Widget {
private let kind: String = "StaticWidget"
public var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in
StaticWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
複数のWidgetスタイルに対応しています。
@main
struct MainWidgets: WidgetBundle {
@WidgetBundleBuilder
var body: some Widget {
Widget1()
Widget2()
}
}
Include Configration Intentにチェックを入れてからエラーが発生する可能性があります。もしあなたのアプリにClass Prfixが設定されているなら、この下のConfigrationIntent.selfには対応するプレフィックスが必要です。
例えば、プレフィックスがXYなら、XYConfigrationIntent.selfに変更する必要があります。
@main
struct MainWidget: Widget {
private let kind: String = "MainWidget"
public var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: XYConfigurationIntent.self, provider: Provider(), placeholder: PlaceholderView()) { entry in
IntentWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
Widgetを処理してイベントをクリックします。Widgetは、system Small、system Medium、system Largeの3つの表示方法をサポートしています。
smallはwidgetUrlのみで処理できます。
@ViewBuilder
var body: some View {
ZStack {
AvatarView(entry.character)
.widgetURL(url)
.foregroundColor(.white)
}
.background(Color.gameBackground)
}
mediumとlargeはLinkまたはwidgetUrlで処理できます。中には同じviewが四つあります。つまり左の写真、右の文字があります。このviewコードは下記の通りです。
struct RecipeView: View {
let recipe: RecipeModel
var body: some View {
Link(destination: URL(string: " ")!) {
HStack {
WebImageView(imageUrl: recipe.squareImageUrl)
.frame(width: 65, height: 65)
Text(recipe.adjName + recipe.name)
.font(.footnote)
.bold()
.foregroundColor(.black)
.lineLimit(3)
}
}
}
}
LinkまたはwidgetUrlを追加した後、RecipeViewをクリックするとイベントが発生します。この時はメインプロジェクトのアプリDelegateで次のように処理します。
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
}
Widgetにおけるネットワーク画像のロードタイミングについて私達がfunc timelineでこの方法でデータを求めて写真のリンクを持ってきたら、同期して画像を解析しなければなりません。そうでないと、対応するWidgetViewに直接ロードしてもらえません。
正しい書き方
Struct Model {
...
let image: UIImage
}
func timeline(with context: Context, completion: @escaping (Timeline<LFPlanEntry>) -> ()) {
Network.request { data in
//
var image: UIImage? = nil
if let imageData = try? Data(contentsOf: url) {
image = UIImage(data: imageData)
}
let model = Model(image: image ?? defalutImage) //
let entry = SimpleEntry(date: entryDate, data: model)
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
Struct WidgetView: View {
let model: Model
@ViewBuilder
var body: some View {
Image(uiImage: model.image)
.resizable()
}
}
間違った書き方(直接にurlをなくしてviewにロードする)
struct WidgetView : View {
let model: Model
@State private var remoteImage : UIImage? = nil
let defaultImage = UIImage(named: "default")!
var body: some View {
Image(uiImage: self.remoteImage ?? defaultImage)
.onAppear(perform: fetchRemoteImage)
}
func fetchRemoteImage() {
guard let url = URL(string: model.url) else { return }
URLSession.shared.dataTask(with: url){ (data, response, error) in
if let image = UIImage(data: data!){
self.remoteImage = image
} else {
print(error ?? "")
}
}.resume()
}
}
私たちのアプリに基づいて作った簡単な効果図です。Widget関連資料
Widgets
Creating a Widget Extension
Keeping a Widget Up To Date
Making a Configrable Widget
ここでiOS 14 Widgetの開発に関する記事を紹介します。iOS 14 Widgetの開発内容については、以前の文章を検索したり、以下の関連記事を見たりしてください。これからもよろしくお願いします。