teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

追加の質問に答えた。

2020/02/11 09:16

投稿

TsukubaDepot
TsukubaDepot

スコア5086

answer CHANGED
@@ -6,4 +6,115 @@
6
6
  ```
7
7
  `4095`の数値については、[ここのURLに挙げてある数値](http://iphonedevwiki.net/index.php/AudioServices)に変更すれば、バイブレーションだけでなくシステム音も鳴らせるようです。
8
8
 
9
- [ここのあたり](https://program-life.com/600)も参考になりそうですね。
9
+ [ここのあたり](https://program-life.com/600)も参考になりそうですね。
10
+
11
+ ---
12
+ 質問者さんはおそらく「**たった2日でマスターできる iPhoneアプリ開発集中講座**」を参考にして開発されていると思うので、それを元にしてコードの変更点を考えてみました(そうであれば、元の質問には参考文献として書いた方が良いと思います)。
13
+
14
+ 時間切れの時にバイブレーションする方法としては、こんな感じでしょうか。
15
+ コメントアウトしている行の先頭に`**`がある行が新たに追加した行です。
16
+
17
+ ```Swift
18
+ // ** 次の二つの変数を追加
19
+ var timeUpFlag = false
20
+ var timeUpCount = 0
21
+
22
+ @IBAction func startButtonAction(_ sender: Any) {
23
+ if let nowTimer = timer {
24
+ // タイマーが実行中だったら何もしない
25
+ if nowTimer.isValid == true {
26
+ return
27
+ }
28
+ }
29
+
30
+ // **タイマ割り込みの間隔を0.5秒に変更
31
+ timer = Timer.scheduledTimer(timeInterval: 0.5,
32
+ target: self,
33
+ selector: #selector(self.timerInterrupt(_:)),
34
+ userInfo: nil,
35
+ repeats: true)
36
+ }
37
+
38
+ @IBAction func stopButtonAction(_ sender: Any) {
39
+ if let nowTimer = timer {
40
+ // タイマーが実行中だったら
41
+ if nowTimer.isValid == true {
42
+ // 停止
43
+ nowTimer.invalidate()
44
+ // **時間切れフラグをクリアする
45
+ timeUpFlag = false
46
+ }
47
+ }
48
+ }
49
+
50
+ // 画面を更新する
51
+ func displayUpdate() -> Int {
52
+ // UserDefaultのインスタンスを作成
53
+ let settings = UserDefaults.standard
54
+ // 取得した秒数をtimerValueに戻す
55
+ let timerValue = settings.integer(forKey: settingKey)
56
+ // **残り時間を計算
57
+ // 0.5秒毎に呼び出されているので、変数countを二分の一した値を引く
58
+ let remainCount = timerValue - count / 2
59
+ countDownLabel.text = "残り(remainCount)秒"
60
+
61
+ return remainCount
62
+ }
63
+
64
+ // 経過時間の処理 ->  scheduledTimer に渡す関数
65
+ @objc func timerInterrupt(_ timer: Timer) {
66
+ // カウンタを増やす
67
+ count += 1
68
+
69
+ // **時間切れフラグがfalseで、かつdisplayUpdate() の戻り値が0以下のとき
70
+ if timeUpFlag == false && displayUpdate() <= 0 {
71
+ // **時間切れになったときのカウント数を記憶する
72
+ timeUpCount = count
73
+ // **時間切れフラグを立てる
74
+ timeUpFlag = true
75
+ // アラートの表示
76
+ let alertController = UIAlertController(title: "終了", message: "タイマー終了時間です", preferredStyle: .alert)
77
+ let defaultAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
78
+ alertController.addAction(defaultAction)
79
+ present(alertController, animated: true, completion: nil)
80
+ //**時間切れフラグが立っている時
81
+ } else if timeUpFlag == true {
82
+ // 時間切れになってから30回割り込みが呼ばれたとき
83
+ if count - timeUpCount > 30 {
84
+ // カウンタをクリア
85
+ count = 0
86
+ // タイムアップフラグをクリア
87
+ timeUpFlag = false
88
+ // タイマを停止する
89
+ timer.invalidate()
90
+ } else {
91
+ let soundIdRing: SystemSoundID = 1011
92
+ //** バイブレーションが4095の時は思ったような動作が見込めない
93
+ // let soundIdRing: SystemSoundID = 4095 //Vibrate
94
+ AudioServicesPlaySystemSound(soundIdRing)
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ Unixなどのシステムコールの場合であって、iOSが該当するか分かりませんが、一般的には
101
+ - タイマ割り込み内部の処理はできるだけ簡潔にする
102
+ - タイマ割り込みの内部で新たにタイマ割り込みをさせない(不可能ではないが、管理が大変になる)
103
+
104
+ と考えた方がいいと思います。
105
+
106
+ したがって、タイマ割り込み内部で新たにタイマを使って0.5秒待つ処理を記述するのは良い方法だとは思えません。
107
+
108
+ ならば、どういうふうに考えたかというと、
109
+ - タイマ割り込みの間隔を0.5秒にして、一つのハンドラで処理を行わせる
110
+ - タイマ割り込み関数内部での処理を、(1)時間切れのときの処理と、(2)その後のバイブレーションの処理に分ける
111
+ - これらの処理の流れはフラグを立てることで切り分ける
112
+
113
+ という風に考えてみました。
114
+
115
+ もしかしたら、もっとスマートな方法があるかもしれませんので、考えてみると良いかと思います。
116
+
117
+
118
+ ちなみに、`SystemSoundId`が`4095`の場合は、1秒毎にバイブレーションが切れてしまいます。推測ですが、前回のバイブレーションの途中で新たなバイブレーションを起こしても、その切れ目がわからないため持続したバイブレーションと感じてしまうからだと思います。
119
+
120
+ なので、`1011`のような短いバイブレーションを呼び出すのがいいのではないでしょうか。