SwiftUIでToday Extension (Widget)を作る

Published by @SoNiceInfo at 7/5/2020


SwiftUIを使ってToday Extension (Widget)を作成する方法を紹介します。

iOS 14から使える新しいWidgetを作成する方法は「Widget Extensionを作る」をご覧ください。

Today ExtensionをSwiftUIで開発する画像

Today Extensionを追加する

Today Extensionを追加する手順

File → New → Target...と進みダイアログが表示されたらToday Extensionを選択します。
適切なProduct Nameを入力してFinishします。

Today ExtensionのSchemeを有効化する手順

Activate "PRODUCT_NAME" schemeと聞かれるのでActivateを選択します。
新しいフォルダが作成されTodayViewController.swift, MainInterface.storyboard, info.plistが追加されました。
これでToday Extensionを作成する準備は完了です。

Today Extensionの最初の状態

初期状態のToday ExtensionではHello Worldと表示されています。

MainInterface.storyboardでHello Worldを削除する

Storyboardの最初の状態

MainInterface.storyboardの中身はこの様になっています。
今回はStoryboardではなくてSwiftUIで開発するのでHello Worldのラベルは削除します。

StoryboardでBackgroundをなしにする

ここでBackgroundをなしにします。Viewを選択して後に右のInspector PaneでBackgroundをnone Defaultにします。他の色にしたい場合は自由に選択してください。

SwiftUIのViewを用意する

File → New → File...(⌘N)でSwiftUI Viewを選択します。
ここではファイル名はWidgetViewとしました。

//
//  WidgetView.swift
//

import SwiftUI

struct WidgetView: View {
    var body: some View {
        HStack {
            Image(systemName: "globe")
            Text("Hello, SwiftUI!")
        }
        .font(.title)
    }
}

struct WidgetView_Previews: PreviewProvider {
    static var previews: some View {
        WidgetView()
    }
}

TodayViewControllerでWidgetViewを呼び出す

viewDidLoad内でUIHostingControllerを使ってWidgetViewを呼び出します。
import SwiftUIをするのを忘れないでください。
WidgetViewを呼び出したら、表示領域を自動的に決定するようにして背景をクリアにします。

//
//  TodayViewController.swift
//

import UIKit
import NotificationCenter
import SwiftUI

class TodayViewController: UIViewController, NCWidgetProviding {
        
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let vc  = UIHostingController(rootView: WidgetView())
        self.addChild(vc)
        self.view.addSubview(vc.view)
        vc.didMove(toParent: self)

        vc.view.translatesAutoresizingMaskIntoConstraints = false
        vc.view.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true
        vc.view.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
        vc.view.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
        vc.view.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
        vc.view.backgroundColor = UIColor.clear
    }
        
    func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
        // Perform any setup necessary in order to update the view.
        
        // If an error is encountered, use NCUpdateResult.Failed
        // If there's no update required, use NCUpdateResult.NoData
        // If there's an update, use NCUpdateResult.NewData
        
        completionHandler(NCUpdateResult.newData)
    }
    
}

完成

Today_ExtensionをSwiftUIで作成できるようになった図

GitHubにコードをおいています。d1v1b/WidgetSample: Today Extension Sample with SwiftUI

iOS 14対応

WWDC20ではホーム画面に配置することができる新しいWidgetが発表されました。
今回紹介した方法でiOS 14でもToday Extensionは引き続き利用できます。
WWDC20で発表されたiOS 14からの新しいWidgetを作成する方法は「Widget Extensionを作る」をご覧ください。

参考

一部の画面だけSwiftUIを使いたいとき - Qiita


    アプリをリリースしました!

    ToDoアプリ

    iCloudを利用したデバイス間データ共有、ダークモードに対応しています。

    リリースしたToDoアプリのスクリーンショット

    IPアドレス履歴保存アプリ

    取得したIPアドレスを確認、位置情報とともに保存できます。

    リリースしたIPアドレス保存アプリのスクリーンショット