20200430のSwiftに関する記事は8件です。

HKHealthStoreのデータ読み取り権限があるかチェックする

結論から言うと読み取り権限があるかチェックすることはできません。
書き込み権限の有無はauthorizationStatus(for:)でチェックすることができます。

このメソッドのDiscussionに以下の説明がありました。

This method checks the authorization status for saving data.

To help prevent possible leaks of sensitive health information, your app cannot determine whether or not a user has granted permission to read data. If you are not given permission, it simply appears as if there is no data of the requested type in the HealthKit store. If your app is given share permission but not read permission, you see only the data that your app has written to the store. Data from other sources remains hidden.

権限がない場合はデータが取得できないだけだそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSウィジェットにCO2濃度を表示する 【Raspberry Pi × CO2-mini × co2meter】

WFH、捗ってますでしょうか。

我が家の場合、机やモニターなど家で仕事をするのに十分な設備は元々整っていたのですが、たったひとつだけオフィスにはあって自宅にないものがありました。

CO2モニターです。

成果物

パッと目につくところに表示させたかったので、iOSのウィジェットに置くことにしました。
最終的にこんな感じでCO2濃度、ついでに室温が見えるようになりました。

iPhone

iPad

スクリーンショット 2020-04-26 22.34.59.png

構成と概要解説

構成はこんな感じです
image.png

ポイントは以下です。

  • CO2-miniでCO2濃度、室温を計測する
  • CO2-miniとRaspberry Piを接続して定期的な計測を行い、Webサーバーとしてアクセス可能にする
  • iOSアプリからWebサーバーへアクセスし、ウィジェットとして表示する
  • 換気したくなる

CO2-mini

CO2濃度の計測は既製品であるこちらを使います。
CO 2モニター CO2-mini | 自然環境測定器 - 製品情報 - 計測器のカスタム

MH-Z19などのモジュールも検討したのですが、WFHによる需要の高まりのためか高価になってる or 配送に時間がかかるため、比較的手に入りやすいこちらを選択しました。

電源供給がmicro USB Type-Bなのでこれを利用してPaspberry Piと接続します。
公式でAPIが公開されているわけではありませんが、有志の解析によってOSSなどを経由してアクセスできるようになっています。

なお、今回はCO2-miniに予め備わっている表示部を見て換気したくなる行為は反則負けとします。

サーバーサイド

主となるCO2濃度のロギング、Webサーバー化は co2meter を使います。
https://github.com/vfilimonov/co2meter

このOSSを使うことで、Pythonを使ったCO2-miniへのアクセスが可能になります。さらに定期的な計測、JSON/CSVへの書き出し、FlaskによるWebサーバー化、折れ線グラフによる可視化まで担ってくれます。

クライアントサイド

iOSアプリ部分については自作して、簡単なものですが公開しました。
https://github.com/akeome/RoomCondition

ラズパイで構築したサーバーにアクセスして、レスポンスのJSONの最新値を表示するだけのものです。

CO2濃度を見える化する

この環境を構築するための手順を記します。
僕は今回初めてRaspberry Piをいじったので、備忘も兼ねてセットアップの記載から始めます。

準備

必要なハード類です。

  • Mac
  • Raspberry Pi
    • 今回はRaspberry Pi Zero WHを使いました。Wi-Fiに接続できればどのモデルでも大丈夫なはず
  • CO2-mini
  • microSDカード
  • アダプタ、ケーブル等
    • MacからmicroSDに書き込むためのハブなどが必要です
    • Raspberry PiとCO2-miniの接続は、micro USB Type-Bとmicro USB Type-Bです
    • Raspberry Pi Zero WHの場合、電源供給はmicro USB Type-Bです

Raspberry Pi のセットアップ

今回使用したRaspberry Pi Zero WHには有線LANポートがないので、Wi-Fi経由でMacから操作を行うための手順を書きます。

MacからmicroSDに書き込む

Paspberry PiにmicroSDを挿す前に、Macで諸々書き込んでいきます。

OSを書き込む

公式サイトからOSをダウンロードします。
https://www.raspberrypi.org/downloads/raspbian/

  • GUIは使わないので軽量な Raspbian Buster Lite にしました
  • めっちゃ時間かかります(1時間ぐらいかかりました)

diskutill list コマンドでMacに接続されているmicroSDのパスを取得します。

$ diskutil list
# (中略)
/dev/disk4 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *32.0 GB    disk4
   1:             Windows_FAT_32 NO NAME                 32.0 GB    disk4s1

自分の環境の場合、32.0 GBの表示から /dev/disk4 が該当のmicroSDを指すとわかりました。

diskutil unmountDisk でアンマウントと、 dd でOSの書き込みを行います。

$ diskutil unmountDisk /dev/disk4
Unmount of all volumes on disk4 was successful

$ sudo dd bs=1m if=/?パス/2020-02-13-raspbian-buster-lite.img of=/dev/rdisk4 conv=sync
Password:
1764+0 records in
1764+0 records out
1849688064 bytes transferred in 55.800569 secs (33148194 bytes/sec)
  • /dev/disk4 としている箇所は環境に合わせて変えてください
  • ?パス としている箇所はOSをダウンロードしたパスです
  • .imgファイルのファイル名も時期によって変わるはずです

Macに boot という名前でディスクが接続されていればOKです。

$ ls /Volumes/
Macintosh HD    boot

ssh接続を有効にする

MacからsshでRaspberry Piに(一時的に)接続するために空ファイルが必要です。持続的に接続する方法については後述します。

$ touch /Volumes/boot/ssh

Wi-Fi接続を有効にする

Wi-Fi接続に必要なファイルを作成します。

$ nano /Volumes/boot/wpa_supplicant.conf

以下の内容を書き込みます。

wpa_supplicant.conf
country=JP
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    key_mgmt=WPA-PSK
    ssid="?SSID名"
    psk="?パスワード"
}
  • SSID、パスワード(場合によってはkey_mgmtも)は環境に合わせて変えてください
  • パスワードを直接入力するとセキュリティ上のリスクがあります。ここでは触れませんが wpa_passphrase コマンドで暗号化できます

もちろん nano コマンドを使わなくても、.confを作成できればおっけーです。

ここまででmicroSDへの書き込みは完了です。

Paspberry Piを起動する

諸々書き込みが終わったmicroSDカードをRaspberry Piに差し込みます。
電源に接続します。Raspberry Pi Zero WHの場合、電源供給はPWRと書かれた方の差込口を使います。
電源が入ると緑のLEDが点灯します。

ssh コマンドでMacからラズパイに接続します。
接続先は pi@raspberrypi.local 、パスワードは raspberry です。

$ ssh pi@raspberrypi.local

The authenticity of host 'raspberrypi.local (xxxx)' can't be established.
ECDSA key fingerprint is SHA256:xxxx.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'raspberrypi.local,xxxx' (ECDSA) to the list of known hosts.
pi@raspberrypi.local's password: 
pi@raspberrypi:~ $ 

こんな感じでプロンプトがラズパイになっていれば成功です?

続いて諸々の設定をやっていきます。

タイムゾーンを設定する

ラズパイの設定画面に入るコマンドを使います。

pi@raspberrypi:~ $ sudo raspi-config

スクリーンショット 2020-04-29 0.38.34.png

4 Localisation Options > I2 Change Timezone > Asia > Tokyo
でタイムゾーンを日本にしておきます。

ssh接続を継続して利用可能にする

上記設定画面から、
5 Interfacing Options > P2 SSH > Yes
でssh接続が持続的に利用可能になります。

IPアドレスを固定する

ifconfig で現在のIPアドレスを、 route -n でデフォルトゲートウェイを確認します。

pi@raspberrypi:~ $ ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 4032  bytes 203013 (198.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4032  bytes 203013 (198.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.100.50  netmask 255.255.255.0  broadcast 192.168.100.255

pi@raspberrypi:~ $ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.100.254 0.0.0.0         UG    302    0        0 wlan0

無線LANはwlan0なので、上記の場合 192.168.100.50 が現在のIPです。
また、Gatewayに表示された 192.168.100.254 が現在のデフォルトゲートウェイです。

確認できたら sudo nano /etc/dhcpcd.conf で以下の内容を追記します。

dhcpcd.conf
interface wlan0
static ip_address=192.168.100.50/24
static routers=192.168.100.254
static domain_name_servers=192.168.100.254
  • ip_addressに固定するIPアドレスを入力します。今回は ifconfig で確認した 192.168.100.50 を使っています。サブネットマスクは基本的にはそのまま /24 でいいはずです
  • routersとdomain_name_serversには route -n で確認したデフォルトゲートウェイを入力します

再起動後、設定したIPアドレスになっていればおっけーです。

# 再起動
pi@raspberrypi:~ $ sudo reboot

# 再接続
$ ssh pi@raspberrypi.local

# IP確認
pi@raspberrypi:~ $ ifconfig

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.100.50  netmask 255.255.255.0  broadcast 192.168.100.255

各種ライブラリをアップデートする

ラズパイのセットアップでのお決まりのようです。

pi@raspberrypi:~ $ sudo apt update
pi@raspberrypi:~ $ sudo apt upgrade

デフォルトのPythonのバージョンを3系にする

これは必須ではありませんが、 python コマンドのデフォルトで Python2系が起動したので3系にしておきます。

pi@raspberrypi:~ $ python --version
Python 2.7.16

pi@raspberrypi:~ $ sudo unlink /user/bin/python

pi@raspberrypi:~ $ sudo ln -s /user/bin/python3 python

pi@raspberrypi:~ $ python --version
Python 3.7.3

Gitをインストールする

Gitをインストールします。ライブラリの使用に必要です。

pi@raspberrypi:~ $ sudo apt install git

pi@raspberrypi:~ $ git --version
git version 2.20.1

Raspberry Pi と CO2-mini の接続

ラズパイとCO2-miniを接続します。
接続端子はお互いmicro USB Type-Bです。
接続できるとCO2-miniはしばらくの待機後、CO2濃度と室温を表示してくれます。

co2meter の事前準備

Python製のライブラリ co2meter を使って、ラズパイからCO2-miniへアクセスします。
vfilimonov/co2meter: A Python library for USB CO2 meter

このco2meterを動かすにあたって必要なライブラリをインストールしていきます。

依存ライブラリのインストール

USB接続機器にアクセスしたりするために必要になるみたいです。

pi@raspberrypi:~ $ sudo apt install libusb-1.0-0-dev libudev-dev

rulesファイルの作成

/etc/udev/rules.d/98-co2mon.rules を作成します。

root@raspberrypi:/home/pi# nano /etc/udev/rules.d/98-co2mon.rules

以下を書き込みます。

98-co2mon.rules
KERNEL=="hidraw*", ATTRS{idVendor}=="04d9", ATTRS{idProduct}=="a052", GROUP="plugdev", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="04d9", ATTRS{idProduct}=="a052", GROUP="plugdev", MODE="0666"

udevadm コマンドで設定を反映させます。
が、この操作はrootユーザーとしての実行が必要らしく、まずはrootユーザーになるためにパスワードを設定します。

pi@raspberrypi:~ $ sudo passwd root
New password: 
Retype new password: 
passwd: password updated successfully
  • パスワードを2度聞かれるので2度入力します

su コマンドでrootユーザーに変身します。

pi@raspberrypi:~ $ su
Password: 

root@raspberrypi:/home/pi# 

プロンプトが root@raspberrypi: になっていればおっけーです。
この状態で udevadm コマンドを実行します。

root@raspberrypi:/home/pi/# sudo udevadm control --reload-rules && udevadm trigger

rootユーザーから一般ユーザーに戻るには exit です。

root@raspberrypi:/home/pi# exit
exit

pi@raspberrypi:~ $ 

ここまでで事前準備は完了です。

ライブラリ co2meter のインストール

これによっていよいよCO2-miniへのアクセスが可能になります。

pi@raspberrypi:~ $ pip3 install hidapi co2meter

README記載の基本的な使い方を試してみます。

pi@raspberrypi:~ $ python
>>> import co2meter as co2
>>> mon = co2.CO2monitor()
>>> mon.read_data()
(datetime.datetime(2020, 4, 25, 11, 10, 21), 1005, 24.100000000000023)

取得できました??
内容は (タイムスタンプ, CO2濃度, 室温) のタプルです。

Raspberry Pi をWebサーバーとして動かす

CO2濃度、室温が取得できることがわかったところで、続いてWebサーバーとして動かす方法を記載します。Webサーバーとして動かすことで、外部からブラウザ経由で計測結果を確認することができます。

co2meter には、予めサーバーとして動かす機能が実装されているので利用します。

  • 定期的な計測
  • JSON/CSVへのロギング
  • 折れ線グラフによる可視化
  • Webサーバー立ち上げ

といった機能が簡単に使えます。

関連パッケージのインストール

flask、pandasのインストール

co2meter のWebサーバー化はflaskで実装されています。

pi@raspberrypi:~ $ pip3 install -U flask pandas

Webサーバー起動

co2meter をサーバーとして実行するには co2meter_server コマンドだけでOKです。
これと組み合わせて、バックグラウンドでプロセスを継続させるために nohup コマンドと & オプションをつけて実行します。

pi@raspberrypi:~ $ nohup co2meter_server -H 0.0.0.0 -P 1201 &
  • -H 0.0.0.0 オプションをつけることで外部のブラウザから(例えば同じLAN内のMacから)接続できます
  • -P 1201 オプションはポート番号です。デフォルトで使われていた1201に指定していますが数字に意味はないです

これで、ブラウザから http://ラズパイのIPアドレス:ポート番号 (この手順通りに進めていれば http://192.168.100.50:1201/ )へアクセスしてCO2濃度、室温が見れるようになりました???

スクリーンショット 2020-04-26 18.31.51.png

スクリーンショット 2020-04-26 18.32.09.png

サーバーとして動かすことでlogsディレクトリが作成され、CSVでログが蓄積されていきます。
計測間隔はデフォルトで約35秒のようです。

pi@raspberrypi:~ $ cat logs/co2.csv 

timestamp,co2,temp
2020-04-26 15:01:59,772,23.9
2020-04-26 15:02:34,768,23.9
2020-04-26 15:03:09,766,23.9
2020-04-26 15:03:44,766,23.9
2020-04-26 15:04:20,760,24.0

Webサーバーの停止

停止させるには、 ps コマンドでPIDを確認して kill コマンドです。 -9 オプションは強制終了です。

pi@raspberrypi:~ $ ps
  PID TTY          TIME CMD
  599 pts/0    00:00:00 bash
  626 pts/0    00:23:44 co2meter_server
 6201 pts/0    00:00:00 ps

pi@raspberrypi:~ $ kill -9 626

プロセスが表示されない場合は x オプションをつけて ps x で試してみてください。

iOSウィジェットに表示する

ここまでの手順で念願のCO2濃度可視化は実現できました。
あとはどこにどんな感じで表示するかですが、今回はWebサーバーから返されるJSONを使ってiOSウィジェットに表示することにしました。

できたソースはこちら
https://github.com/akeome/RoomCondition

Xcodeからこのプロジェクトをビルドすれば動くはずです。

  • Targetはまず本体アプリ RoomCondition 、次にウィジェット RoomConditionTodayExtension の順番でビルドします
  • 接続先のIPアドレス、ポート番号は http://192.168.100.50:1201/ にしています。環境に合わせて適宜変更してください。この記事通りに進めていればそのままで問題ないです

ちょっとコード解説

ウィジェット(Today Extension)は今回初めて実装してみたので知見を書いておきます。

API通信部分のコード共通化

本体アプリ、ウィジェットで共通して使う処理はFrameworkとして実装することで共通化できます。

Frameworkの作成はメニューの File > New > Target > Framework です。
今回は APIRequest という名前で作成しました。

RoomCondition.swift
public struct RoomCondition: Codable {
    public let co2: String
    public let temp: String
    public let timestamp: String
}
APIRequest.swift
public struct API {

    public static func request(completion: @escaping (RoomCondition?) -> Void) {

        let url = "http://192.168.100.50:1201/log.json"

        let session = URLSession.shared
        let task = session.dataTask(with: URL(string: url)!) { data, urlResponse, error in

            let currentCondition = try! JSONDecoder().decode([RoomCondition].self, from: data!)
            completion(currentCondition.last)
        }

        task.resume()
    }
}

呼び出し側では import APIRequest して使います。

呼び出し側.swift
API.request(completion: { roomCondition in
    guard let roomCondition = roomCondition else {
        return
    }

    print(roomCondition) // RoomCondition(co2: "1015", temp: "21.4", timestamp: "2020-04-28 00:35:32")
})

今回は結局Today Extension側でしか使ってないのでFrameworkにしなくてもよかったかもしれません?

Today Extensionの更新契機について

ウィジェットが表示される度に func widgetPerformUpdate(completionHandler: @escaping (NCUpdateResult) -> Void) が呼び出されるようです。
この中に通信処理を書いているのですがちゃんと都度最新の値を取ってきてくれます。

completionHandler の引数には NCUpdateResult を渡します。

public enum NCUpdateResult : UInt {
    case newData
    case noData
    case failed
}

上から順に、新規データあり、新規データなし(更新不要)、失敗を表します。
これによっていい感じにウィジェットの再描画が行われるようです。

色分けのロジック

CO2-miniの表示部に着想を得て、ppmによって色分けしました。

TodayViewController.swift
    func co2Color(co2Value: String) -> UIColor {
        switch Int(co2Value) {
        case (..<1000)?:
            return .systemGreen
        case (..<1200)?:
            return .systemYellow
        default:
            return .systemRed
        }
    }

快適

そろそろ換気しよ

あかん

1000とか1200の基準は環境にもよると思うので適宜変更してください。
また室温の色分けは季節によって変えるべきかもしれません。

おまけ、本体アプリ

本体アプリを起動しても真っ白なのは寂しかったのでとりあえず SFSafariViewController でダッシュボートを表示させておきました。

ViewController.swift
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let url = URL(string: "http://192.168.100.50:1201/dashboard")
        if let url = url {
            let safari = SFSafariViewController(url: url)
            present(safari, animated: false)
        }
    }

おわりに 〜二酸化炭素濃度が人体に与える影響について〜

一般的に空気中の二酸化炭素濃度が概ね1000ppmを超えると眠気や倦怠感を誘発すると言われています。

厚生労働省が規定する建築物環境衛生管理基準においても、建築物の管理者は1000ppmになるように空調設備をちゃんとしてね〜との記載があります。
建築物環境衛生管理基準について|厚生労働省

この基準値を検証する資料もありました。
1968年のWHO報告書を根拠としていると考えられる、との考察があったりなかなか興味深いです。
建築物環境衛生管理基準の設定根拠の検証について
(※pdfです)

上記資料から一部抜粋

  • 18ページ

    二酸化炭素自体は、少量であれば人体に有害ではな
    いが、1000ppmを超えると倦怠感、頭痛、耳鳴り、息苦しさ等の症
    状を訴えるものが多くなり、フリッカー値(フリッカー値が小さいほ
    ど疲労度が高い)の低下も著しい

  • 19ページ

    1000ppmのCO2の吸入実験(Eliseeva 1964)で呼吸、循環器系、大
    脳の電気活動に変化がみられたと報告している。

街の噂で聞く1000ppmで眠くなる説はこのあたりがソースになっているのかもしれませんね。

ということで我が家のCO2濃度可視化の記録でした。
適度に換気して、快適なWFHを〜?

参考記事

参考にさせていただきました。先人の皆様ありがとうございます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【swift】xcode 11 標準体重と肥満率の計算

スクリーンショット 2020-04-25 22.54.55.png
スクリーンショット 2020-04-25 22.50.17.png

参考動画

【学んだ点】
・UITextFieldで記入した数字を元に計算する
・計算結果を文字と一緒にテキストに表示させる

雑談
xcodeを勉強し始めて約2ヶ月が経ちました。
最初はコードの意味もわからずただひたすら動画を見て真似するを繰り返しの毎日でした。笑
そこから徐々に動画に出てくるコードを少し自分なりにアレンジしてみたりして、今は簡単なコードでしたら自分で考えて書けるようになり嬉しく、勉強してきて良かったなと思います。
勉強の記録としてこれから少しずつ今まで作ったアプリ含め、これから勉強していくことを記録していきたいと思います。

上記のアプリは一番最初に作ったアプリです。
参考動画の数式以外丸パクリで、全然大したアプリではないのですが自分が考えたことがシミュレーターで動いた時は結構感動しました。笑

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

参照がいつまでたってもわからない

わかったことだけメモ。

<そもそもなんの話か>
・メモリ管理に関する話。
・変数とかを使っている間、それをメモリに取っておかねばならない。
・使い終わったら解放しないと、メモリがいっぱいになってしまう。

<前提>
・swiftの値型では、スコープから抜けると自動でメモリが解放される。参照型の場合はそうではない。
・参照型は複数の箇所から参照されうるので、メモリを解放してよいかを簡単に判断できない。そこで、ARCという方式を使う。
・ARCでは、参照カウントというのを使ってメモリを管理する。(参照カウント=参照されている数。0になったら解放)

<参照の種類>
・強参照(strong):デフォルト。参照カウントが1増える。
・弱参照(weak):参照カウント増えない。メモリ解放後はnilが入るようになる。
・非所有参照(unowned):参照カウント増えない。メモリ解放後に参照しようとするとエラーを投げる。

<循環参照とは?>
・こんな感じでお互いに参照しているといつまでたってもメモリが解放されない。→困る。

a.hoge = b // bの参照メモリが1増える
b.hogehoge = a // aの参照メモリが1増える

→強参照じゃないのを使えば解決!(片方の参照メモリを増やさないことでうまいこと参照メモリが減ってくれるようになる。)

<参考ページ>
詳細で正確な説明が載っています……。
Swiftのメモリ管理を知る - Satsuki Hashiba - Medium

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スタティックプロパティ って何?

プロパティとは

classや構造体などの型が持つ値のことです。

プロパティはそれを誰が保有するのかで分類できる

インスタンスプロパティ

プロパティはデフォルトではこの形になります。ですので、後述するスタティックプロパティと見比べることをお勧めします。

class Man {
    var name = "Taro"
}

let man1 = Man()
let man2 = Man()

man2.name = "Jiro"

print("\(man1.name),\(man2.name)") //Taro.Jiro

human1human2でそれぞれインスタンス化をしています。
同じHumanクラスをインスタンス化したものですが、プロパティであるnameはインスタンスごとに異なっていることがわかります。

スタティックプロパティ

staticとはApple付属の辞書によれば「静止した状態にあるさま。静的。」という意味です。

class Man {
    static let sex = "male"
    var name = "Taro"
}
let man = Man()
print("\(man.name),\(Man.sex)")

インスタンスプロパティと異なり、インスタンスそのものが値を持つわけではないので元のクラスから呼び出すことになります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デリゲートがいつまでたってもわからない

 とりあえずわかったことだけメモ。

<基本構造>
 デリゲート→デリゲート先=親ビュー→子ビュー

<基本の流れ>
①デリゲートが親ビューに、デリゲートが持っているものを委譲する(譲り渡す)。
②親ビューがデリゲートの持ち物の具体的な中身を定義する(*1)。しないと怒られる。
③子ビューのデリゲートに親ビューのデリゲートを指定する。(*2)。
→上から委譲、下から引っ張るみたいなイメージ。

*1:定義しなくてもいいやつがある(デリゲートでoption指定されているもの)。
*2:childview.delegate = self みたいなやつ。

<できること>
・デリゲートの持ち物を子ビューも使えるようになる。
←何が嬉しい?

<デリゲートを使う利点>
「猿がもがきまくって理解したSwiftのデリゲート(Delegate)という仕組み」という記事では、デリゲートを使わないとできない、子ビューから親ビューへの連絡手段として紹介されている。なるほど……?
「SwiftにおけるDelegateとは何か、なぜ使うのか」という記事では、①イベントを検知する処理と②検知後にしたい処理を、コード上ですっぱり分けて書きたいから、と説明されている。なるほど。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift5】iOSアプリを公開するまでの勉強法について

はじめに

この記事では、プログラミング初心者な僕がAppStoreにアプリを公開し、月137円稼ぐまでの勉強法を書きたいと思う。

Swiftの勉強法は2択

Swifrの勉強法は、以下の2択です。
- プログラミングスクール(オンライン)に通って瞬殺でSwiftマスターになるか
- 参考書をたくさん買ってじっくり時間をかけてSwiftマスターになるか

プログラミングスクール

現在のプログラミングスクールでは、自分のオリジナルアプリをAppStoreに公開するまで付き添ってくれるらしい。
iOS開発オススメのプログラミングスクール

参考書

僕は、参考書でじっくり時間をかけてSwiftマスターになる方法を選んだ。

1.ひたすら手を動かす(2019年10月~)

まずは、超初心者のための参考書をたくさん買ってひたすら手を動かした。

ここで気をつけなければならないのは、購入する参考書のバージョンだ。
Swift、Xcodeのバージョン変更は、割と頻繁に行われており、現在のバージョンにあった参考書がとても少ない。
バージョンが違うと、手順通り行ってもエラーが出てしまうため、挫折してしまう。
買う時は、現在のバージョンを調べてから買うこと。

僕が実際に買った参考書

ここで学んだこと

  • 基本的なstoryboardの使い方
  • 基本的なViewControllerの書き方

2.AppStoreに超簡単なアプリをAppStoreに公開してみる(2019年12月~)

ショートカットキークイズアプリを公開してみた。
『クイズアプリを作ってみよう』的な参考書があったので、その通りに作成し、
そのアプリを自分でショートカットキークイズアプリに変えた。

ここで学んだこと

  • ボタンなどのオブジェクトの使い方
  • Viewの表示の仕方
  • AppStoreに公開する方法

3.ちょっと本格的なアプリをAppStoreに公開してみる(2019年1月~)

人間関係を管理するアプリが欲しかったのに、AppStoreに微妙なものしかなかったので、自分で作ってみた。

ヒトメモというアプリを作成した。
このアプリで実装した主な機能は

  • 誕生日通知機能
  • プロフィールメモ機能
  • パスワード機能
  • 検索機能
  • 並び替え機能
  • バリデーションチェックアラート機能

と、Swiftでの基本的な機能だったので、とても学べた。
これらは全てネットの情報を頼りに進めた。

ここで学べたこと

  • ライブラリの使い方
  • 主なSwiftの基本

4.Swift関連の記事をひたすら書く(2019年3月~)

勉強は結局アウトプットらしいので、勉強しながら、勉強したことを記事にしてアウトプットしていく。

と、記事を書きまくった。
Swiftマスターになるために勉強する、記事を書くために勉強すると、勉強する目的を2つに増やしたおかげで、やる気が二倍になった。
これからもこの勉強方法は続けていきたい。

5.今後の勉強予定

  • テストコード
  • 読みやすく、洗練されたコード
  • UIデザイン、UXデザインを超意識した記憶アプリ作成
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUIを使って一覧ページ(リスト表示)を簡単に作ってみよう

今回のお題


これまでstoryboardを触ったことはありますが、ほぼswift初心者なワタクシ。。。
久しぶりにxcodeを開いたらSwiftUIという機能があったので触ってみました。

2019年に出ていたらしいけど、アプリ関係は疎いので全然知らなかった。。。


準備(xcode起動からプロジェクト作成まで)


スクリーンショット 2020-04-26 21.23.38.png

「Create a new Xcode project」を選択


スクリーンショット 2020-04-26 21.27.10.png

「Single View APP」を選択しNext


スクリーンショット 2020-04-26 21.27.42.png

Product Nameを入力します。
今回はbaseballとしました。


スクリーンショット 2020-04-26 21.28.10.png

プロジェクトを作成し、この画面が表示されます。
上部にあるResumeボタンをクリックするとビルドを始めて。。。


スクリーンショット 2020-04-26 21.29.22.png

プレビューとしてこれが出る!
なるほど!
それでは少し触ってみることにします。


textを増やす


このままText("Hello, world")をコピペすると。。。

スクリーンショット 2020-04-29 23.01.09.png

おうふ。。。


なので、「command⌘キー」を押しながらソースコードのTextをクリックしてポップアップを表示します。

スクリーンショット 2020-04-26 23.09.59.png

今回は縦に表示をさせたいので
「Embed in VStack」を選択します。


スクリーンショット 2020-04-29 23.04.43.png

Text("Hello, world")を増やすと。。


スクリーンショット 2020-04-26 23.10.36.png

これでようやくtextを増やすことができました。
当たり前ですが、増やした分だけ表示されますね笑
次は"Hello, world"を書き換えて、そのまま色もつけてみましょうか。


テキストに色をつける


「command⌘キー」を押しながらプレビューのラベルをクリックするとポップアップが表示されます。
「Show SwiftUI Inspector...」を選択すると。。


スクリーンショット 2020-04-26 23.11.12.png

フォントや色を変えられますね。
今回はCustomを選択、それぞれ色を設定しました。


スクリーンショット 2020-04-26 23.22.45.png

ここまで表示できるようになりました。
swiftのRGB値を使った色指定は256段階ではなく、0〜1.00の間で設定するので
255で割った値が入るようにちょいと面倒な書き方としています。


リスト表示


このまま文字列を縦に表示しているだけでは使いづらいので、リストにしましょう。
「command⌘キー」を押しながらソースコードのVStackをクリックしてポップアップを表示し
「Embed in List」を選択します。


スクリーンショット 2020-04-29 23.13.09.png

あらら、、これはクドいw
リストを使えるようにコードを以下のように書き換えました。


ContentView.swift
import SwiftUI

struct Team: Identifiable {
    var id: Int
    var name: String
    var colorRed: Double
    var colorGreen: Double
    var colorBlue: Double
}


struct ContentView: View {
    let teams: [Team] = [
        Team(id: 1, name: "北海道", colorRed: 2 / 255, colorGreen: 81 / 255, colorBlue: 140 / 255),
        Team(id: 2, name: "宮城", colorRed: 133 / 255, colorGreen: 1 / 255, colorBlue: 15 / 255),
        Team(id: 3, name: "埼玉", colorRed: 16 / 255, colorGreen: 41 / 255, colorBlue: 97 / 255),
        Team(id: 4, name: "千葉", colorRed: 34 / 255, colorGreen: 24 / 255, colorBlue: 21 / 255),
        Team(id: 5, name: "大阪", colorRed: 176 / 255, colorGreen: 143 / 255, colorBlue: 50 / 255),
        Team(id: 6, name: "福岡", colorRed: 242 / 255, colorGreen: 202 / 255, colorBlue: 0 / 255),
    ]

    var body: some View {
        List(teams) { team in
            Text(team.name)
                .foregroundColor(Color(red: team.colorRed, green: team.colorGreen, blue: team.colorBlue))
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

その結果。。。


スクリーンショット 2020-04-29 23.17.35.png

それらしく表示されるようになりましたね!
これだけでは寂しいので、もう少し表示内容を増やしてみましょう。


画像の追加


Assets.xcassetsに使いたい画像をドロップします。

スクリーンショット 2020-04-30 1.55.03.png


画像を反映させます。

ContentView.swift
import SwiftUI

struct Team: Identifiable {
    var id: Int
    var name: String
    var name_en: String
    var colorRed: Double
    var colorGreen: Double
    var colorBlue: Double
}


struct ContentView: View {
    let teams: [Team] = [
        Team(id: 1, name: "北海道", name_en: "hokkaido", colorRed: 2 / 255, colorGreen: 81 / 255, colorBlue: 140 / 255),
        Team(id: 2, name: "宮城", name_en: "miyagi", colorRed: 133 / 255, colorGreen: 1 / 255, colorBlue: 15 / 255),
        Team(id: 3, name: "埼玉", name_en: "saitama", colorRed: 16 / 255, colorGreen: 41 / 255, colorBlue: 97 / 255),
        Team(id: 4, name: "千葉", name_en: "chiba", colorRed: 34 / 255, colorGreen: 24 / 255, colorBlue: 21 / 255),
        Team(id: 5, name: "大阪", name_en: "osaka", colorRed: 176 / 255, colorGreen: 143 / 255, colorBlue: 50 / 255),
        Team(id: 6, name: "福岡", name_en: "fukuoka", colorRed: 242 / 255, colorGreen: 202 / 255, colorBlue: 0 / 255),
    ]

    var body: some View {
        List(teams) { team in
            Image(team.name_en)
                .resizable()
                .scaledToFit()
                .frame(width: 32.0, height: 32.0)
            Text(team.name)
                .foregroundColor(Color(red: team.colorRed, green: team.colorGreen, blue: team.colorBlue))
            Text(team.name_en)
                .foregroundColor(Color(red: team.colorRed, green: team.colorGreen, blue: team.colorBlue))
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Teamsにname_enという変数を追加し、画像名と合わせています。
これでプレビューはこんな感じ。


スクリーンショット 2020-04-30 2.00.39.png

列をタップした際にページ遷移する処理などは
またの機会に書けたらと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む