- 投稿日:2020-05-23T13:48:33+09:00
【Swift】Protocolとはなにか、どんな時に便利か
Protocolとは
Protocolとはある機能を実現するclassやstructが必ず実装するべきメソッドやプロパティの名前や型を定義するものです。
他の言語いう抽象クラスやInterfaceにあたります。protocol CanFly { func fly() // 実際の処理は書かない }protocolを定義するときは、必要なメソッドやプロパティの名前や型を定義するのみで実際の処理は書きません。
どんなときに便利か
特定のclassやstructが持っているべき要件(このメソッドやプロパティを持っているべき)を適用したい場合に便利です。
例えばBirdクラスを作ります。鳥の特徴は空を飛べて、メスは卵を産みますね。class Bird { var isFemale = true func layEgg() { if isFemale { print("トリは卵を産みました") } } func fly() { print("トリは翼で羽ばたいて空を飛びました") } }次にEagleクラスとPenguinクラスを作りましょう。どちらも鳥の一種なのでBirdクラスを継承して定義してみます。
Eagleの特徴は上昇気流に利用して高く上がって滑空して移動すること、Penguinの特徴は泳ぐことですね。
ここでインスタンスを作って使えるメソッドを確認してみます。class Eagle: Bird { func soar() { print("タカは上昇気流に乗って高く上がりました") } } class Penguin: Bird { func swim() { print("ペンギンは翼で羽ばたいて泳ぎました") } } let myEagle = Eagle() myEagle.layEgg() // "トリは卵を産みました" myEagle.fly() // "トリは翼で羽ばたいて空を飛びました" myEagle.soar() // "タカは上昇気流に乗って高く上がりました" let myPenguin = Penguin() myEagle.layEgg() // "トリは卵を産みました" myEagle.fly() // "トリは翼で羽ばたいて空を飛びました" myEagle.swim() // "ペンギンは翼で羽ばたいて泳ぎました"一見良さそうですが、ペンギンは空を飛ぶことはできませんのでこの実装だとおかしなことになってしまいます。
更に同じく空を飛ぶAirplaneクラスを作る場合はどうなるでしょう。Birdクラスがflyメソッドを持っていますが、翼で羽ばたくわけではないのでオーバーライドして少し内容を変えましょう。
class Airplane: Bird { override func fly() { print("飛行機はエンジンを使って空を飛びました") } } let myAirplane = Aiplane() myAirplane.layEgg() // "トリは卵を産みました" myAirplane.fly() // "飛行機はエンジンを使って空を飛びました"これもよく見るとBirdクラスが持っているlayEggメソッドも継承しているので飛行機が卵を産むことになっていますね。おかしいです。
それなら継承せずに必要なクラスにだけflyメソッドを定義すればよいのですが、プログラムの規模が大きくなってくると、複雑になりそうですし実装漏れも起きそうです。Protocolを使って実装する
そんなときにProtocolを使うと実装が必要なメソッドやプロパティを定義しておけるので便利です。
例えば今までの例でいうとEagleとAirplaneはflyメソッドを持っている必要がありますが、Penguinは必要ないですね。
従ってEagleとAirplaneのみにCanFlyを適用します。protocol CanFly { func fly() // 必要なメソッドの名前を書くだけ、実装はclassやstructの定義時に書く } class Bird { var isFemale = true func layEgg() { if isFemale { print("トリは卵を産みました") } } } class Eagle: Bird, CanFly { // CanFlyを適用する func fly() { // flyメソッドを実装しないとエラーになる print("タカは翼で羽ばたいて空を飛びました") } func soar() { print("タカは上昇気流に乗って高く上がりました") } } class Penguin: Bird { // Penguinは飛べないのでCanFlyは適用しない func swim() { print("ペンギンは翼で羽ばたいて泳ぎました") } } struct Airplane: CanFly { // CanFlyを適用する func fly() { // flyメソッドを実装しないとエラーになる print("飛行機はエンジンを使って空を飛びました") } }こうするとペンギンが空を飛んだり、飛行機が卵を産んだりしなくて済みます。
因みに継承はclassではできてstructではできませんが、protocolの適用はclass/structどちらでもできるのが特徴です。参考
https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
https://www.udemy.com/course/ios-13-app-development-bootcamp/
- 投稿日:2020-05-23T12:58:54+09:00
Swift学習記#5 「カウントアプリの開発」
はじめに
前回の投稿で、swiftでの開発を行う上で必要な
基礎知識のアウトプットも終了しました。とりあえずソースコードを見て、おおよそ何がしたいのか
理解できる様になってきたので、
今回からは実際にアプリを開発しながら
更なるレベルアップをして行きたいと思います。もちろん知らないメソッドやプロパティなどが出てきたら、
随時アウトプットして行きます。カウントアプリ開発
ということで、今回は簡単なカウントアプリの開発を通して
開発の手順と、Xcodeの機能の把握を行っていこうと思います!開発環境
swift5.2.2
、Xcode(Version11.4.1)
で、
シミュレーターはiphone11で行っています。仕様書の作成
まずはXcodeを開く前にアプリの仕様書を作成しましょう。
最初はこんな感じで、
①アプリの仕様、②作成手順、③アプリイメージくらいの
簡単な仕様書でいいと思います。技術力を上げると共に仕様書の書き方もマスターしましょう!
新規プロジェクトの作成
基礎編の投稿では
playground
を利用していましたが、
アプリの開発ではproject
を利用して行きます。新規プロジェクトを作成するには、
Xcodeを開きCreate a new Xcode project
を選択します。
すると、こんな感じの画面に移ります。今回作るアプリのViewページは1つなので、
Single View App
を選択します。
すると、今度はこんな感じの画面に移ります。
ここでは、プロジェクトの名前やドメインなどを決めて行きます。
・Product Name
プロジェクトの名称を決めます。
今回は、カウントアプリなのでCountApp
と入力しましょう。・
Team
これは、実機テストをする際には設定する必要がありますが、
今回は設定しません。・
Organization Name
会社などの場合は入力が必要ですが、
基本は入力しなくても大丈夫です。・
Organization Identifier
任意の値でOKです。
しかし、App Storeにリリースする場合はこの値で
アプリを識別するため、重複しないものでないといけません。
オススメは、自身のメールアドレスを逆さにする事です。・
Language
使用する言語は、SwiftとObjective-Cを選べます。
今回は、swift
を選びましょう。・
User Interface
ここでは、Storyboard
を選択してください。この後、プロジェクトを保存する場所を選択して、
新規プロジェクトの作成は終了です。次からは、いよいよ開発に入って行きます!
アプリケーションの開発
ここからの作業は、アプリ仕様書の作業手順2以降の作業となります。
作業手順2 パーツの配置
2−1 配置
まずは、左側にある
NavigatorArea
の
Main.storyboard
を開きます。
ここに、ボタンやラベルなどのパーツを付け足して行きます。次に
Library
を開いて、「UILabel」×1、「UIButton」×2を配置します。
Library
は右上にある「+」のボタンから開けます。
今の段階では大きさや位置の調節などは行わなくて大丈夫です。2−2 位置、大きさ
まずは、
UILabel
の調節から行って行きます。
① Utility areaを表示させる
② UILabelのTextを「0」に、FontのSizeを「100」に変更
③ Alignmentを中央に変更
次に
UIButton
の調節を行います。
① 左のUIButtonのUtility areaを表示させる
② UIButtonのTextを「UP」に、FontのSizeを「50」に変更
③ 右のUIButtonのUtility areaを表示させる
④ UIButtonのTextを「DOWN」に、FontのSizeを「50」に変更
最後にパーツの位置を調整して行きます。
調節にはXcodeのAutoLayout
の機能を利用します。*AutoLayout機能
AutoLayout機能
とは、画面やパーツに幅や高さ、余白、位置などの
制約を設定し、表示を調整できる機能です。また、ここで設定した機能は、
縦、横の表示が変更されても自動で調節されます。まずは、
UILabel
の余白の制約を設定します。
①右下の5つあるAutoLayout機能からAdd New Constraints
を選択する。
②Add New Constraints
の上部で余白を設定できます。
*注意:四角についている赤い点線が、直線になった時のみ
入力した値が有効になります。
③下部にるAdd3~
をクリックし、
設定した余白がおかしくないか確認する。
適用後
こんな感じになっていればクリアです。次に、今のままでは右寄りになっている
UPボタン
とDOWNボタン
の
位置を設定して行きます。
①右下の5つあるAutoLayout機能からAlign
を選択する。
②Horizontally in Container
とVertically in Container
に
値を入力する。
・「UPボタン」にHorizontally「-80」、Vertically「150」
・「DOWNボタン」にHorizontally「80」、Vertically「150」*Horizontally in Containerは水平方向の
Vertically in Containerは垂直方向の位置を定義する。水平方向とは、
垂直方向とは、
中央線から見て、プラス方向であればプラスの値を、
マイナス方向であればマイナスの値を入力する。③下部にる
Add2~
をクリックし、
設定した位置がおかしくないか確認する。
こんな感じになればクリア!ここまでで、手順2は終了です。
ただ1つ注意して欲しいのが、AutoLayout機能についてです。AutoLayout機能は便利ですが、
「値を間違った」、「思った通りの表示にならなかった」などで
調節したい時は、Utility areaのSize Inspecter
から
行ってください。AutoLayoutからもう一度値を入力すると、
二重で定義することとなり、エラーが起きてしまします‼︎
作業手順3 パーツの接続
Main.storyboard上に配置したラベルやボタンを、
ソースコードから扱えるようにOutletやActionの接続を行います。3−1 Outlet接続
Outlet接続とは、Storyboard上のパーツを、
ソースコード上の変数として使えるようにする仕組みです。①まずは、
Assistant editor
を開きます。
Assistant editorは右上のボタンから開けます。
②次に、Storyboardの「UILabel」を選択し、
Controlキーを押しながら、Assistant editorで開いた
View Controllerのソースコード上にドラッグ&ドロップします。
Nameの部分には、「Label」と入力しておきましょう。
するとこんな感じになるはずです。
これで、Outlet接続は完了です。3−2 Action接続
ここでは、ボタンの押されたときにアクションが動く様に
ソースコードとの関連づけを行います。①まずは、「UPボタン」をControlを押しながら、
Assistant editorにドラッグ&ドロップします。
そして、NameはUPBotton、TypeにはAnyと入力しましょう。
「DOWNボタン」も同じ手順でドラッグ&ドロップし、
NameはDOWNBotton、TypeにはAnyと入力しましょう。
するとこんな感じになるはずです。
これで、Action接続は完了です。作業手順4
ここで、接続したアクションに処理を書いて行きます。
コードはそこまで難しくないので、この手順ではソースコードを
そのまま掲載します。ViewController.swift// // ViewController.swift // CountApp // import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBOutlet weak var Label: UILabel! var number = 0 @IBAction func UPBotton(_ sender: Any) { number = number + 1 Label.text = String(number) } @IBAction func DOWNBotton(_ sender: Any) { if number < 1 { return } else { number = number - 1 Label.text = String(number) } } }これでアクションの定義は完了です。
コードの詳しい解説はしなので、わからない事があれば
自分で調べてみてください!作業手順5
ここまで来たらあとはテストです。
せっかく作っても動かないのでは意味がありませんからね。
シミュレーターでテストしてみましょう!Build手順
①まずは、左上の「▶️」のボタンを押して、アプリをBuildします。
この手順で、シミュレーターにアプリのデータがインストールされます。
②シミュレーターが起動するとアプリが起動するので、
期待どうりの動作をするか確認しましょう!
特に、DOWNボタンを押して0以下にならないかは
見ておいてください。
問題なければ完成です‼︎
エラーが出た場合は、諦めずに
エラーの箇所の解説に戻って再度挑戦しましょう‼︎今回は小規規模の開発だったため、一度しかBuildしませんでしたが、
開発の規模が大きいのであれば、こまめにBuildすることをオススメします。これまで
これでカウントアプリの開発は終わりです。
今回はボタンが二つだけでしたが、
複数配置して機能を増やしてみても面白いと思います‼︎AutoLayout機能あたりは慣れないとミスすることも多いですが、
慣れてくると便利に感じてきます。
頑張って慣れましょう!今回は初めての開発ということで、
画像を多めに使い、解説も出来るだけ詳しくすることを意識しています。
そのため、ごちゃごちゃしていて読みづらいかもしれません。
わかりにくい事があれば、コメントしてください‼︎
- 投稿日:2020-05-23T12:31:53+09:00
Swift、はじめました。
- 投稿日:2020-05-23T12:26:06+09:00
[Swift] if文 を if式 にしてみた
if文は式じゃないから値をかえさないですよね。
条件によってある変数に異なる異なる値を与える、なんてことは日常的にありますが、swiftではifは関数ではありませんので値を返すことをしません。変数への代入は各ブロックの中で行うことになります。
let val: Int if conditionA { val = val1 } else if conditionB { val = val2 } else if let val3 = doSome() { val = val3 } else { val = val4 }では、このif文を関数化してみましょう!
やってみた
class ConditionalBranch<T> { private var val: T? func `if`( _ bool: Bool, _ ex: () -> T ) -> ConditionalBranch<T> { if bool { val = ex() } return self } func ifLet<E>(_ some: E?, ex: (E) -> T ) -> ConditionalBranch<T> { if val != nil { return self } guard let wapped = some else { return self } val = ex(wapped) return self } func elseIf( _ bool: Bool, _ ex: () -> T ) -> ConditionalBranch<T> { if val != nil { return self } return self.if(bool,ex) } func `else`(_ ex: () -> T) -> T { return val ?? ex() } }使い方はこんな感じです。
let val = ConditionalBranch<Int>() .if(conditionA) { return val1 } .elseIf(conditionB) { return val2 } .ifLet(doSome()) { val3 in return val3 } .else { return val4 }想像以上に綺麗!(自己満足)なので、Swift6で採用してもらいたいです 笑
- 投稿日:2020-05-23T11:03:50+09:00
【Swift】QRコードを読み取って文字列を取得する
簡単に実装したサンプルです。参考になると幸いです。
asa08/QRreaderカメラを設定する
import AVFoundation
して、AVCaptureSession
をインスタンスを生成します。private let session = AVCaptureSession()次に、カメラの設定を行います。
// カメラの設定 // 今回は背面カメラなのでposition: .back let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .back) let devices = discoverySession.devices if let backCamera = devices.first { do { // カメラでQRの読み取りに成功した時の処理 let deviceInput = try AVCaptureDeviceInput(device: backCamera) doInit(deviceInput: deviceInput) } catch { print("Error occured while creating video device input: \(error)") } }
doInit()
の中でデコードを行います。private func doInit(deviceInput: AVCaptureDeviceInput) { if !session.canAddInput(deviceInput) { return } session.addInput(deviceInput) let metadataOutput = AVCaptureMetadataOutput() if !session.canAddOutput(metadataOutput) { return } session.addOutput(metadataOutput) metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) metadataOutput.metadataObjectTypes = [.qr] // カメラを起動 let previewLayer = AVCaptureVideoPreviewLayer(session: session) previewLayer.frame = view.bounds previewLayer.videoGravity = .resizeAspectFill caputureView.layer.addSublayer(previewLayer) session.startRunning() } func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { for metadata in metadataObjects as! [AVMetadataMachineReadableCodeObject] { // QRのtype: metadata.type // QRの中身: metadata.stringValue guard let value = metadata.stringValue else { return } session.stopRunning() textLavel.text = value caputureView.isHidden = true } }これでカメラで読み取ったQRから文字列を取得することができます。
- 投稿日:2020-05-23T08:48:38+09:00
こんなソースコードはイヤだ-関数の機能不足と引数
プログラムのソースコードのより良い書き方をまとめていこうと思います。
これは実際にあるアプリで使われていたものです。関数の機能不足と引数
sample.swift//パラメータを受け取り、APIを投げる func getUser(parameters: [String:String]) { let url = Const.UserUrl + "/" + parameters["id"] api.connectGet(url: url, queryParameters: parameters) { (data, error) in //レスポンスの処理 //.... } } func start() { var parameters = [String:String]() parameters["id"] = self.userId parameters["type"] = "male" parameters["country"] = "japan" getUser(parameters: parameters) }
- 投稿日:2020-05-23T06:53:19+09:00
iOS ResearchKit を利用して非公式の医療研究試験を実施する
ResearchKit
とは?
ResearchKit
はApple
が医学研究目的で開発したオープンソースのフレームワークで、多くの機能が搭載されています。ここでは、基本的な医学的検査を例に、このフレームワークの使い方を紹介します。ここで得られる検査結果は公式のものではなく、健康状態を結論づける公式なデータとしては使えないことにご注意ください。本フレームワークは英語のみで提供されていますが、一部の検査は言語非依存です。
学習内容
- (記憶)空間記憶
- タッピングスピード (iPhone)
- ハノイの塔
- トレイルメイキング(数字と文字を順に結んでいく)
- 視覚コントラスト検査
- 視力検査
- PSAT(前の2つの数字を合計)
- (聴覚)トーン聴覚検査
- (聴覚)dBHLトーン聴覚検査
警告
これらの検査は研究目的のみで実施されるものです!医学的検査を実施できるのは医師のみです。本記事は、このフレームワークの機能の使用法の紹介のみを目的としています。
実装
ステップ1.
ResearchKit
フレームワークを複製する
ResearchKit
フレームワークを既存のプロジェクトに複製します:git@github.com:ResearchKit/ResearchKit.git
ステップ2. フレームワークを
Xcode
に追加する次のように、フレームワーク
Xcode
のプロジェクトファイルをあなたのプロジェクトファイルにドラッグします:次に、
ResearchKit
のビルドされた製品をプロジェクトターゲットのフレームワーク、ライブラリ、埋め込みコンテンツセクションに追加します:
「+」アイコンをクリックし、ResearchKit
を選択します。 そして、タイプをEmbed & Sign
に変更しますステップ3. テストコードを追加して結果を受け取る
テストを提示し、その結果を受け取る基本的な構造は次のとおりです:
import ResearchKitテストビューを表示:
let taskViewController = ORKTaskViewController(task: task, taskRun: nil) taskViewController.outputDirectory = FileManager.default.temporaryDirectory taskViewController.delegate = self present(alert, animated: true, completion: nil)結果を確認するには:
extension testingTableView: ORKTaskViewControllerDelegate { func taskViewController(_ taskViewController: ORKTaskViewController, didFinishWith reason: ORKTaskViewControllerFinishReason, error: Error?) { taskViewController.dismiss(animated: true, completion: nil) } func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { //結果はここで受け取られ、`result.identifier` で取り込むことができます //特定のテストの結果を受け取る方法について説明します。 } }各テストの固有コード
(記憶)空間記憶
let task = ORKOrderedTask.spatialSpanMemoryTask(withIdentifier: "spatialSpanMemory", intendedUseDescription: nil, initialSpan: 2, minimumSpan: 1, maximumSpan: 5, playSpeed: 1, maximumTests: 8, maximumConsecutiveFailures: 2, customTargetImage: nil, customTargetPluralName: nil, requireReversal: false, options: []) let taskViewController = ORKTaskViewController(task: task, taskRun: nil) taskViewController.outputDirectory = FileManager.default.temporaryDirectory taskViewController.delegate = self present(alert, animated: true, completion: nil)extension testingTableView: ORKTaskViewControllerDelegate { ... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if result.identifier == "spatialSpanMemory" { if let memoryResult = result.stepResult(forStepIdentifier: "cognitive.memory.spatialspan")?.results?.first as? ORKSpatialSpanMemoryResult { let score = String(memoryResult.score) //スコア let numFailed = String(memoryResult.numberOfFailures) //失敗したカウント let totalNum = String(memoryResult.numberOfGames) //総数 //TODO } } } ... }タッピングスピード (iPhone)
let task = ORKOrderedTask.twoFingerTappingIntervalTask(withIdentifier: "tappingTest", intendedUseDescription: nil, duration: 10, handOptions: .both, options: []) //ORKTaskViewController...//delegate... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if result.identifier == "tappingTest" { if let leftHandResults = result.stepResult(forStepIdentifier: "tapping.left")?.results, let rightHandResults = result.stepResult(forStepIdentifier: "tapping.right")?.results { if leftHandResults.count == 2 && rightHandResults.count == 2 { let leftClickCount = (leftHandResults[1] as? ORKTappingIntervalResult)?.samples?.count ?? 0 //左側の数 let rightClickCount = (rightHandResults[1] as? ORKTappingIntervalResult)?.samples?.count ?? 0 //右側の数 //TODO } } } } //...ハノイの塔
let task = ORKOrderedTask.towerOfHanoiTask(withIdentifier: "towerOfHanoi", intendedUseDescription: nil, numberOfDisks: 4, options: []) //ORKTaskViewController...//delegate... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if result.identifier == "towerOfHanoi" { if let stepResult = result.stepResult(forStepIdentifier: "towerOfHanoi")?.results?.first as? ORKTowerOfHanoiResult, let moves = stepResult.moves, let time = moves.last?.timestamp { let movesTaken = String(moves.count) let timeCompleted = String(format: "%.2f", time) //TODO } } } //...トレイルメイキング(数字と文字を順に結んでいく)
let task = ORKOrderedTask.trailmakingTask(withIdentifier: "trailMaking", intendedUseDescription: nil, trailmakingInstruction: nil, trailType: .B, options: []) //ORKTaskViewController...//delegate... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if result.identifier == "trailMaking" { if let trailResult = result.stepResult(forStepIdentifier: "trailmaking")?.results?.first as? ORKTrailmakingResult, let timeTaken = trailResult.taps.last?.timestamp { let numErrors = trailResult.numberOfErrors //エラー数 //TODO } } } //...視覚コントラスト検査
let task = ORKOrderedTask.landoltCContrastSensitivityTask(withIdentifier: "landoltCcontrast", intendedUseDescription: nil) //ORKTaskViewController...//delegate... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if result.identifier == "landoltCcontrast" { if let stepResults = result.stepResult(forStepIdentifier: "landoltCStep")?.results { var passedTestCount = 0 for stepResult in stepResults { if let convertedResult = stepResult as? ORKLandoltCResult { if (convertedResult.outcome ?? false) { passedTestCount += 1} } } //TODO: `passedTestCount` 正しい選択の数, `String(stepResults.count)` 総数 } } } //...視力検査
let task = ORKOrderedTask.landoltCVisualAcuityTask(withIdentifier: "visualAcuity", intendedUseDescription: nil) //ORKTaskViewController...//delegate... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if result.identifier == "visualAcuity" { if let visualResults = result.stepResult(forStepIdentifier: "landoltCStep")?.results { var passed = 0 for result in visualResults { if let convertedResult = result as? ORKLandoltCResult { if (convertedResult.outcome ?? false) { passed += 1 } } } //TODO: `passed` は、合格したテストの数を意味します。 } } } //...PSAT(前の2つの数字を合計)
let task = ORKOrderedTask.psatTask(withIdentifier: "psat", intendedUseDescription: nil, presentationMode: .visual, interStimulusInterval: 4.5, stimulusDuration: 4.5, seriesLength: 12, options: []) //ORKTaskViewController...//delegate... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if result.identifier == "psat" { if let stepResult = result.stepResult(forStepIdentifier: "psat")?.results?.first as? ORKPSATResult { let totalCount = String(stepResult.totalCorrect) //総数 let correctCount = String(stepResult.samples?.count ?? 0) //正しい選択の数 //TODO } } } //...(聴覚)トーン聴覚検査
Present the test view:
let task = ORKOrderedTask.toneAudiometryTask(withIdentifier: "toneAudiometry", intendedUseDescription: nil, speechInstruction: nil, shortSpeechInstruction: nil, toneDuration: 60, options: []) //ORKTaskViewController...Checking the result
//delegate... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if let audioResults = result.stepResult(forStepIdentifier: "tone.audiometry")?.results { var matchedCount = 0 //正しい選択の数 var missedChannel = [String]() //失敗したオーディオチャンネル guard let audioResult = audioResults.first as? ORKToneAudiometryResult else { return } guard let audioSamples = audioResult.samples else { return } let totalTestCount = audioSamples.count //実施されたテストの総数 for sample in audioSamples { if sample.channel == sample.channelSelected { matchedCount += 1 //一致した } else { missedChannel.append("周波数 " + String(format: "%.2f", sample.frequency)) //違う } } } } //...(聴覚)dBHLトーン聴覚検査
let task = ORKOrderedTask.dBHLToneAudiometryTask(withIdentifier: "dBHLToneAudiometry", intendedUseDescription: nil, options: []) //ORKTaskViewController...//delegate... func taskViewController(_ taskViewController: ORKTaskViewController, didChange result: ORKTaskResult) { if result.identifier == "dBHLToneAudiometry" { var resultStr = "" if let stepResult1 = result.stepResult(forStepIdentifier: "dBHL1.tone.audiometry") { if let firstResult = stepResult1.results?.first as? ORKdBHLToneAudiometryResult { resultStr += "オーディオチャンネル 0:\n" + processSingleDBHLAudioResult(firstResult: firstResult) + "\n" } } if let stepResult2 = result.stepResult(forStepIdentifier: "dBHL2.tone.audiometry") { if let secondResult = stepResult2.results?.first as? ORKdBHLToneAudiometryResult { resultStr += "オーディオチャンネル 1:\n" + processSingleDBHLAudioResult(firstResult: secondResult) + "\n" } } } } func processSingleDBHLAudioResult(firstResult: ORKdBHLToneAudiometryResult) -> String { var resultStr = "" if let samples = firstResult.samples { /* 各サンプルには異なる可聴周波数が含まれています */ for sample in samples { var subResultStr = "\n周波数 " + String(sample.frequency) + "\n" /* 単位は、周波数内での異なるdB(音量レベル)のテストを指します */ if let units = sample.units { var successfulTests = 0 for unit in units { let dBValue = String(format: "%.2f", unit.dBHLValue) let userTapped = (unit.userTapTimeStamp != Double.zero) if userTapped { successfulTests += 1 } subResultStr += dBValue + " dB " + userTapped.getYesNo() + ", " } subResultStr += String(successfulTests) + " / " + String(units.count) + " 合格しました" + "\n" } resultStr += subResultStr } } return resultStr } //... extension Bool { func getYesNo() -> String { if self { return "タップ済みです" } else { return "タップしていません" } } }警告
これらの検査は研究目的のみで実施されるものです!医学的検査を実施できるのは医師のみです。本記事は、このフレームワークの機能の使用法の紹介のみを目的としています。
iOS開発、Swift、プログラミングに関することなら何でも、遠慮なくTwitterで質問してください。
@MaShunzhe