回答編集履歴

1

追加の質問に答えた。

2020/02/11 09:16

投稿

TsukubaDepot
TsukubaDepot

スコア5086

test CHANGED
@@ -15,3 +15,225 @@
15
15
 
16
16
 
17
17
  [ここのあたり](https://program-life.com/600)も参考になりそうですね。
18
+
19
+
20
+
21
+ ---
22
+
23
+ 質問者さんはおそらく「**たった2日でマスターできる iPhoneアプリ開発集中講座**」を参考にして開発されていると思うので、それを元にしてコードの変更点を考えてみました(そうであれば、元の質問には参考文献として書いた方が良いと思います)。
24
+
25
+
26
+
27
+ 時間切れの時にバイブレーションする方法としては、こんな感じでしょうか。
28
+
29
+ コメントアウトしている行の先頭に`**`がある行が新たに追加した行です。
30
+
31
+
32
+
33
+ ```Swift
34
+
35
+ // ** 次の二つの変数を追加
36
+
37
+ var timeUpFlag = false
38
+
39
+ var timeUpCount = 0
40
+
41
+
42
+
43
+ @IBAction func startButtonAction(_ sender: Any) {
44
+
45
+ if let nowTimer = timer {
46
+
47
+ // タイマーが実行中だったら何もしない
48
+
49
+ if nowTimer.isValid == true {
50
+
51
+ return
52
+
53
+ }
54
+
55
+ }
56
+
57
+
58
+
59
+ // **タイマ割り込みの間隔を0.5秒に変更
60
+
61
+ timer = Timer.scheduledTimer(timeInterval: 0.5,
62
+
63
+ target: self,
64
+
65
+ selector: #selector(self.timerInterrupt(_:)),
66
+
67
+ userInfo: nil,
68
+
69
+ repeats: true)
70
+
71
+ }
72
+
73
+
74
+
75
+ @IBAction func stopButtonAction(_ sender: Any) {
76
+
77
+ if let nowTimer = timer {
78
+
79
+ // タイマーが実行中だったら
80
+
81
+ if nowTimer.isValid == true {
82
+
83
+ // 停止
84
+
85
+ nowTimer.invalidate()
86
+
87
+ // **時間切れフラグをクリアする
88
+
89
+ timeUpFlag = false
90
+
91
+ }
92
+
93
+ }
94
+
95
+ }
96
+
97
+
98
+
99
+ // 画面を更新する
100
+
101
+ func displayUpdate() -> Int {
102
+
103
+ // UserDefaultのインスタンスを作成
104
+
105
+ let settings = UserDefaults.standard
106
+
107
+ // 取得した秒数をtimerValueに戻す
108
+
109
+ let timerValue = settings.integer(forKey: settingKey)
110
+
111
+ // **残り時間を計算
112
+
113
+ // 0.5秒毎に呼び出されているので、変数countを二分の一した値を引く
114
+
115
+ let remainCount = timerValue - count / 2
116
+
117
+ countDownLabel.text = "残り(remainCount)秒"
118
+
119
+
120
+
121
+ return remainCount
122
+
123
+ }
124
+
125
+
126
+
127
+ // 経過時間の処理 ->  scheduledTimer に渡す関数
128
+
129
+ @objc func timerInterrupt(_ timer: Timer) {
130
+
131
+ // カウンタを増やす
132
+
133
+ count += 1
134
+
135
+
136
+
137
+ // **時間切れフラグがfalseで、かつdisplayUpdate() の戻り値が0以下のとき
138
+
139
+ if timeUpFlag == false && displayUpdate() <= 0 {
140
+
141
+ // **時間切れになったときのカウント数を記憶する
142
+
143
+ timeUpCount = count
144
+
145
+ // **時間切れフラグを立てる
146
+
147
+ timeUpFlag = true
148
+
149
+ // アラートの表示
150
+
151
+ let alertController = UIAlertController(title: "終了", message: "タイマー終了時間です", preferredStyle: .alert)
152
+
153
+ let defaultAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
154
+
155
+ alertController.addAction(defaultAction)
156
+
157
+ present(alertController, animated: true, completion: nil)
158
+
159
+ //**時間切れフラグが立っている時
160
+
161
+ } else if timeUpFlag == true {
162
+
163
+ // 時間切れになってから30回割り込みが呼ばれたとき
164
+
165
+ if count - timeUpCount > 30 {
166
+
167
+ // カウンタをクリア
168
+
169
+ count = 0
170
+
171
+ // タイムアップフラグをクリア
172
+
173
+ timeUpFlag = false
174
+
175
+ // タイマを停止する
176
+
177
+ timer.invalidate()
178
+
179
+ } else {
180
+
181
+ let soundIdRing: SystemSoundID = 1011
182
+
183
+ //** バイブレーションが4095の時は思ったような動作が見込めない
184
+
185
+ // let soundIdRing: SystemSoundID = 4095 //Vibrate
186
+
187
+ AudioServicesPlaySystemSound(soundIdRing)
188
+
189
+ }
190
+
191
+ }
192
+
193
+ }
194
+
195
+ ```
196
+
197
+
198
+
199
+ Unixなどのシステムコールの場合であって、iOSが該当するか分かりませんが、一般的には
200
+
201
+ - タイマ割り込み内部の処理はできるだけ簡潔にする
202
+
203
+ - タイマ割り込みの内部で新たにタイマ割り込みをさせない(不可能ではないが、管理が大変になる)
204
+
205
+
206
+
207
+ と考えた方がいいと思います。
208
+
209
+
210
+
211
+ したがって、タイマ割り込み内部で新たにタイマを使って0.5秒待つ処理を記述するのは良い方法だとは思えません。
212
+
213
+
214
+
215
+ ならば、どういうふうに考えたかというと、
216
+
217
+ - タイマ割り込みの間隔を0.5秒にして、一つのハンドラで処理を行わせる
218
+
219
+ - タイマ割り込み関数内部での処理を、(1)時間切れのときの処理と、(2)その後のバイブレーションの処理に分ける
220
+
221
+ - これらの処理の流れはフラグを立てることで切り分ける
222
+
223
+
224
+
225
+ という風に考えてみました。
226
+
227
+
228
+
229
+ もしかしたら、もっとスマートな方法があるかもしれませんので、考えてみると良いかと思います。
230
+
231
+
232
+
233
+
234
+
235
+ ちなみに、`SystemSoundId`が`4095`の場合は、1秒毎にバイブレーションが切れてしまいます。推測ですが、前回のバイブレーションの途中で新たなバイブレーションを起こしても、その切れ目がわからないため持続したバイブレーションと感じてしまうからだと思います。
236
+
237
+
238
+
239
+ なので、`1011`のような短いバイブレーションを呼び出すのがいいのではないでしょうか。