Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【After iOSDC LT Night〜ピクシブ×日経×タイミー〜】実装!Interact...

tatsubee
September 12, 2024
13

【After iOSDC LT Night〜ピクシブ×日経×タイミー〜】実装!Interactive Widgets

tatsubee

September 12, 2024
Tweet

Transcript

  1. 2 自己紹介 • ニックネーム: tatsubee • 戸籍ネーム: 山本小龍 • ピクシブ23新卒

    • pixivアプリ作ってる • 最近やっていること ◦ お絵描き ◦ テニス ◦ iOS ◦ Flutter tatsubee iOSエンジニア
  2. struct SampleWidget: Widget { let kind: String = "com.example.sample-widget" var

    body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: SampleProvider()) { entry in SampleWidgetView() .containerBackground(.fill, for: .widget) } } } struct SampleProvider: TimelineProvider { func placeholder(in context: Context) -> SampleEntry { SampleEntry(date: Date()) } func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {} func getTimeline(in context: Context, completion: @escaping (Timeline<SampleEntry>) -> Void) {} } struct SampleEntry: TimelineEntry { var date: Date } struct SampleWidgetView: View { var body: some View { VStack {} } } 15
  3. struct SampleWidget: Widget { let kind: String = "com.example.sample-widget" var

    body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: SampleProvider()) { entry in SampleWidgetView() .containerBackground(.fill, for: .widget) } } } struct SampleProvider: TimelineProvider { func placeholder(in context: Context) -> SampleEntry { SampleEntry(date: Date()) } func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {} func getTimeline(in context: Context, completion: @escaping (Timeline<SampleEntry>) -> Void) {} } struct SampleEntry: TimelineEntry { var date: Date } struct SampleWidgetView: View { var body: some View { VStack {} } } 16
  4. /// Widget本体 struct SampleWidget: Widget { … } /// Timelineに依るWidgetの振る舞い

    struct SampleProvider: TimelineProvider { … } /// Timelineのある地点でのWidgetの状態 struct SampleEntry: TimelineEntry { … } /// WidgetのUI struct SampleWidgetView: View { … } 17
  5. /// Widget本体 struct SampleWidget: Widget { /// Widgetの識別子 let kind:

    String = "com.example.sample-widget" /// Widgetの種類によってWidgetConfigurationのsub classを選ぶ /// 他にAppIntentConfigrationとEmptyConfigration(多分あまり使わない)がある var body: some WidgetConfiguration { StaticConfiguration( kind: kind, provider: SampleProvider() ) { entry in SampleWidgetView() .containerBackground(.fill, for: .widget) } } } struct SampleProvider: TimelineProvider { … } struct SampleEntry: TimelineEntry { … } struct SampleWidgetView: View { … } 18
  6. struct SampleWidget: Widget { … } /// Timelineに依るWidgetの振る舞い struct SampleProvider:

    TimelineProvider { /// デフォルトの見た目 func placeholder(in context: Context) -> SampleEntry { SampleEntry(date: Date()) } /// 現在の見た目 func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {} /// Timelineの生成 func getTimeline( in context: Context, completion: @escaping (Timeline<SampleEntry>) -> Void) {} } struct SampleEntry: TimelineEntry { … } struct SampleWidgetView: View { … } 19
  7. struct SampleWidget: Widget { … } struct SampleProvider: TimelineProvider {

    … } /// Timelineのある地点でのWidgetの状態 struct SampleEntry: TimelineEntry { var date: Date // var hoge: Hoge } struct SampleWidgetView: View { … } 20
  8. struct SampleWidget: Widget { … } struct SampleProvider: TimelineProvider {

    … } struct SampleEntry: TimelineEntry { … } /// WidgetのUI struct SampleWidgetView: View { var body: some View { VStack {} } } 21
  9. struct SampleWidget: Widget { … } struct SampleProvider: TimelineProvider {

    … } struct SampleEntry: TimelineEntry { … } struct SampleWidgetView: View { var body: some View { VStack {} } } #Preview(as: .systemLarge) { SampleWidget() } timeline: { SampleEntry(date: .now) } 22
  10. struct SampleWidget: Widget { … } struct SampleProvider: TimelineProvider {

    … } struct SampleEntry: TimelineEntry { … } struct SampleWidgetView: View { var body: some View { VStack { Text("Hello World") .font(.title) } } } #Preview(as: .systemLarge) { SampleWidget() } timeline: { SampleEntry(date: .now) } 23
  11. struct SampleWidget: Widget { … } struct SampleProvider: TimelineProvider {

    … } struct SampleEntry: TimelineEntry { … } struct SampleWidgetView: View { var body: some View { VStack { Button(intent: SampleAppIntent()) { Image(systemName: "heart") } Toggle(isOn: true, intent: SampleAppIntent()) { Image(systemName: "heart") } Toggle(isOn: false, intent: SampleAppIntent()) { Image(systemName: "heart") } } } } struct SampleAppIntent: AppIntent { static var title: LocalizedStringResource = "Sample" func perform() async throws -> some IntentResult { return .result() } } 24
  12. 27 アクション活用アイデア • ブックマーク ◦ 基本的にはこれになりそう ◦ いいね / スキップ

    基本的にはアプリに誘導したい! → ショートカットできる最小限に留める
  13. 29 Widget の今後妄想 Androidでできること • 動的なWidgetサイズの変更 • スワイプ・スクロール • 文字列の入力

    • Widget → アプリ → Widgetのシームレスな切り替え • そもそもWidgetとして独立