10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

toio™(ロボットトイ | toio(トイオ))Advent Calendar 2019

Day 16

toio コア キューブの状態を SwiftUI で表示してみた

Last updated at Posted at 2019-12-15

はじめに

日頃の業務や趣味では TypeScript を使うことが多いですが、最近は個人的な興味で、Swift を使った iOS アプリの開発を始めています。
まだ Swift の文法や流儀などよくわかっていない状況ではありますが、いろいろ調べる中で「SwiftUI」が便利そうだと感じ、今回試しに、toio コア キューブ (以下、キューブ) の位置や向きの表示に使ってみました。

下図は今回作った iOS アプリのスクリーンショットです。
IMG_851D7CD86E95-1.jpeg
画面の上半分で、接続中のキューブをリスト形式で表示しています。
キューブは 2台以上接続することもでき、画面右上の「Add」を押すたびに、リスト項目が増えます。
リスト項目はそれぞれ 1台のキューブを示していて、最新の「(x座標, y座標), 向き」を表示します。右端のトグルで、キューブとの接続・切断を制御できます。

画面下半分には「Forward (前進)」といったボタンが並び、接続中の複数キューブを同時に前進させるなど制御することができます。

 ソースコード
  https://github.com/eqot/toio-sample.swift

なお本記事では、SwiftUI の利用に主眼を置いていることから、iOS アプリにおける BLE 利用方法といった、SwiftUI と直接関係ないトピックの記載は割愛しています。興味ある方は、書籍「iOS×BLE Core Bluetoothプログラミング」など参照してください。

SwiftUI とは

SwiftUI は、Apple が 2019年 6月の開発者向けイベント「WWDC19」で発表したフレームワークで、シンプルな記述で UI を定義できます。

 SwiftUI
  https://developer.apple.com/jp/xcode/swiftui/
hero-lockup-medium_2x.png

宣言型プログラミング

SwiftUI の特長のひとつは、いわゆる「宣言型 (declarative) プログラミング」で、一般的なプログラミングで使われる「命令型 (imperative) プログラミング」とは記述内容や考え方が異なります。

命令型プログラミングでは、開発者が実現したいことを「どのように実現するか」を記述します。たとえば、画面に文字と画像を表示したいときは、

    canvas.drawText("foo", 100, 200)
    canvas.drawImage(barImage, 300, 200)

といったように、「どのクラスのどの機能を使うか」を開発者が意識し、これらをうまく使いこなす必要があります。

一方の宣言型プログラミングでは、「開発者が実現したいことは何か」を記述します。

    Text("foo")
    Image("barImage")

このとき、「どのクラスのどの機能を使うか」はフレームワークが適切に処理するため、開発者は意識する必要がなくなります。これによって、表示する画面の大きさや操作体系が異なる場合 (macOS アプリ、iOS アプリ、watchOS アプリなど) や、将来的により高度な機能が備わった場合にも、アプリにおける UI 定義は変えることなく、フレームワークによって最適な処理を実現できます。

宣言型のひとつである HTML が、ブラウザの種類が異なっても、古いブラウザでも新しいブラウザでも、(概ね) 同じ表示になることから実感できるかと思います。

データバインディング

SwiftUI と同時に、非同期処理を扱う Combine フレームワークも追加されました。
Combine フレームワークは SwiftUI と連携することもでき、UI に表示するデータをシンプルな実装で UI と紐付けて、UI 操作に応じてデータを更新したり、更新されたデータを UI に反映することができます。

具体的な実装を見てみましょう。

import Combine

public class CoreCube: ObservableObject {
        ()

    @Published public var x: Int = 0
    @Published public var y: Int = 0
    @Published public var direction: Int = 0

        ()
}

上記は、キューブを管理・制御するクラス CoreCube の一部です。メンバー変数の xydirection は、マットの上でのキューブの位置や向きに応じて適宜変化します。クラスが ObservableObject を継承し、これらのメンバー変数に @Published をつけることで、値の変化を監視することができるようになります。

値の変化を捉え、UI に表示する実装は以下のようになります。

import SwiftUI

struct CoreCubeRow: View {
    @ObservedObject var cube: CoreCube

    var body: some View {
        Text("(\(self.cube.x), \(self.cube.y)), \(self.cube.direction)")
    }
}

構造体 CoreCubeRow は、値変化の監視対象となるインスタンス cube を保持します。このとき @ObservedObject を宣言しているところがポイントで、これによって値変化を契機に CoreCubeRow が再描画され、UI 上には常に最新の位置と向きが表示されるようになります。

ここでも宣言型プログラミングが使われています。
従来の命令型プログラミングでは、フレームワークが提供する、もしくは自前の Observer / Listener クラスを使うなどして、データと UI を紐付ける必要がありました。宣言型プログラミングでは、開発者が「どのような仕組みを使うか」を意識する必要はなく、実現したいことを非常にシンプルな記述で定義できます。

別の例も見てみましょう。

今回試作したアプリでは、複数台のキューブへ接続したかったため、複数台のキューブを管理するクラス CoreCubeGroup を定義しています。

public class CoreCubeGroup: ObservableObject {
    @Published var cubes = [CoreCube]()
    
    func add() {
        cubes.append(CoreCube())
    }
}

CoreCube のインスタンスを配列として保持するメンバー変数 cubes を定義しています。先述の @Published をこの配列に記述し、項目が増える (= 接続中のキューブが増える) などの変化を監視します。

この変化を反映する UI 側の実装は以下のようになります。

import SwiftUI

public struct CoreCubeList: View {
    @ObservedObject var cubes: CoreCubeGroup

    public var body: some View {
        List(self.cubes.cubes) { cube in
            CoreCubeRow(cube: cube)
        }
    }
}

構造体 CoreCubeList は、CoreCubeGroup のインスタンスを保持し、接続中のキューブをリスト形式で表示します。
接続中のキューブが増えたことを契機に CoreCubeList が再描画され、接続中であるすべてのキューブが表示されます。

まとめ

SwiftUI を使って、接続中のキューブをリスト形式で表示し、キューブそれぞれの位置や向きを表示してみました。

今後は、SwiftUI で定義した UI を watchOS 上でも表示させてみるなど、SwiftUI ならではのメリットを活かした実装も試してみたいと思っています。

参考サイト

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?