時間を測るアプリを作りたくて、
まずはシンプルなストップウォッチを作ることにしました。
100で1秒になるの知らなかった🥲
— やんも🌈iOSエンジニアになる (@Beer7769) February 17, 2021
文字幅が数字ごとに違うって知らなかった🥺
コンマ秒を何秒にする計算式が難しかった🦊
地味にprintって便利🦊✨
後悔はしない😌笑 pic.twitter.com/VDZ6fzPMoZ
##参考にしたサイト
ストップウォッチが作りたくて日本語で検索しても、古いものしか出てこなかったので英語で検索したら比較的最近アップされたと思われる動画を見つけることが出来ました。
▼今回参考にした動画
Simple Timer App Swift Xcode Tutorial - Stopwatch (Count up Timer)
▼サンプルコードのリンク
Timer App Example Source Code
上記以外で参考にしたサイト
Swift ストップウォッチ
0.1は浮動小数点数で正確に表せないのに、printしたときに0.1と表示されるのはなぜか
【swift5】ストップウォッチタイマーの作り方
Apple公式リファレンス scheduledTimer
Apple公式リファレンス Timer
##開発環境
Swift5
Xcode12.4
#コードの中身の説明
コード全体は、Timer App Example Source Codeを参考にして、自分なりにアレンジした箇所だけ記載していきます。
##ストップウォッチで表示される文字について
シュミレーターを動かしてみて気づいたのですが、
秒が増えるごとにブルブルするというか、震えたようになりました。
それは、数字によって文字幅が違うので、
表示するごとにサイズが変わり、震えたように見える、でした。
以下のリンクを参考にコードを追記しました。
iOSで等幅フォントを指定する
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
startStopButton.setTitleColor(UIColor.green, for: .normal)
timerLabel.font = UIFont.monospacedDigitSystemFont(ofSize: 60, weight: .medium)
print("viewDidLoad")
}
プロポーショナルフォントと言って、文字毎に文字幅が異なるフォントのことをいうそうです。
ああ、確かに一文字POPをパワポで作るときに
スライドごとに微調整しないとなんか変な感じになるのは、
プロポーショナルフォントだから、だったんだと思いました。
print("viewDidLoad")
これは必要ないですが、viewDidLoadが
問題なく動いてくれてるか確認するために書きました。
##初心者だからわからないエラーの文言
このエラーに1週間くらい悩まされました(笑
エラーが出てるけど、何が原因でシュミレーターが落ちてしまうのかが
まーーーーったくわかりません。
しかし、よーく見てみると赤いところに
[StopwatchByYoutube.ViewController start:]:unrecognized selector sent to instance
って言ってる。
何かが、うまく紐付けれてないっぽい。
さらに、startって書いてる。
「スタートボタン」周辺でなにか起こってると予測し、
調べた結果、
インスペクターコネクションのところの、
touch Up Insideのところが、2つあった!!
上図は、直したあとの画像なのですが。
多分、よくある消し忘れです。
ここが原因でエラーが出てたようです。
なんじゃぁ〜♪
##スタートボタンを押した時
@IBAction func startStopTapped(_ sender: Any) {
print("pressButton")
if(timerCounting) {
timerCounting = false
timer.invalidate()
startStopButton.setTitle("START", for: .normal)
startStopButton.setTitleColor(UIColor.green, for: .normal)
print("false")
} else {
timerCounting = true
startStopButton.setTitle("STOP", for: .normal)
startStopButton.setTitleColor(UIColor.red, for: .normal)
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(timerCounter), userInfo: nil, repeats: true)
print("true")
}
}
printのコードは、なくて大丈夫なやつで、
ちゃんと動作してるか確認するために書いてあります。
@スタートボタンが押されたら以下のことをやる。
もし、timerCounting(さっくり言えばストップウォッチのこと)が
false(ストップウォッチが動かしてない)場合
・タイマーはinvalidate(=無効化する)
・スタートストップボタンはSTARTと表示する
・スタートストップボタンのフォントカラーはグリーンで、ノーマルタイプで表示する
true(ストップウォッチが動かしてる)場合
・スタートストップボタンはSTOPと表示する
・スタートストップボタンのフォントカラーは赤で、ノーマルタイプで表示する
・タイマーを発動。0.01秒ごとに、違うところに書いたtimerCounterを、繰り返し(repeats)実行する。
##時間の計算方法について
動画のサンプルコードは1秒ごとに増えていくストップウォッチでしたが、
私が作りたかったのは、0,01秒からカウントするヤツ。
サンプルコードでは、
func secondsToHoursMinutesSeconds(seconds: Int) -> (Int, Int, Int)
{
return ((seconds / 3600), ((seconds % 3600) / 60),((seconds % 3600) % 60))
}
サンプルの方は1秒ごとに1を足していって秒をカウントしていくのですが、
私の場合は0.01秒からカウントしたいのでこのコードだと、
0.6秒で1秒になってしまいました。
return ((seconds / 3600), ((seconds % 3600) / 60), seconds)
}
書き換えてみたけど、このコードだと、
0.1秒が60で繰り上がらなくなる,0.6秒で1秒になるは変わらない。
ここで気づいたのが、これ多分一般常識なんだろうけど、
0.01秒が100になったときに1秒になる。
1秒が60秒になったときに、1分になる。
いや、当たり前なんですけど、あんまり意識したことなかったので、
そもそも、この計算式だと違うくなっちゃうよね、ということで、
return ((seconds / 6000), ((seconds / 100) % 60), (seconds % 100))
これが正解でした。
secondsが、0.01秒ごとに1増えていく。
(seconds % 100)
%で割ったあまりを表示させるってなってるけど、
直訳はそんな感じですが、ニュアンスでいうと、
どこまで表示させたいか、で考えればわかりやすいかなぁと思います。
今回は0.01秒が100になったときに1秒になるので、
100になったら0からカウントし直したい。
なので、「100で割って、そのあまりを表示させる」
((seconds / 100) % 60)
0.01秒が100になったら1秒になって、
60秒で1分になるから、
0.01が6000になったときに1分になるようにしたいから、
6000を100で割って、何秒なのかを計算して、それを60であまりを出すことで、
60秒になったら0からまたカウントするようになる。
この計算式考えるのに、結構頭使いました笑
##%02dで、「00」を表示させる
func makeTimeString(hours: Int, minutes: Int, seconds: Int) -> String {
var timeString = ""
timeString += String(format: "%02d", hours)
timeString += " : "
timeString += String(format: "%02d", minutes)
timeString += " . "
timeString += String(format: "%02d", seconds)
return timeString
}
"%02d"ってすると0のときに00で表示させることができる。
あと、 timeString += " . " も、変更しました。
サンプルでは:になってたのですが、
AppleWatchのストップウォッチを見てみると、
コンマ秒は" . "になってたので、変更しました。
#勉強になったこと
英語で検索する
やったほうがいいのはわかっていたけど、
やはり英語で検索すると全然情報量が違いました。
Youtubeで学習する際も、英語で検索したほうがいっぱい出てくる。
確かに何かしゃべってるかわからないけど、
十分理解することができる。
最初は、違うサイトを見ながらストップウォッチを作成したけど、
バージョンが古かったりで、完成に辿り着けなかった。
初心者の私には、アレンジとかまだ、わからないので、
英語で検索して最新の情報をキャッチしたほうが早いと思いました。
1秒の詳細
0.01秒が100になったときに1秒になるのが衝撃的。
普通にみんな知ってることなのかな??
60秒で1分なのに、0.01秒はなんで100なんだ?
ただ、そこまで掘り下げる必要もないので流します。
プロポーショナルフォント
ただ単に数字を表示させるだけだと思っていたけど、
実際に作ってみたら「なるほど」そういうことも考えないといけないのね、
ということがわかった。
エラーと向き合う
以前、「絶対コードはあってるのにエラーがでる」という発想は捨てることにしました。
なぜなら「どこかが絶対違うからエラーがでる」から。
そこは良かったのだけど、向き合うってことが出来てなかったなぁと。
エラーコードもよく見れば、場所まで教えてくれてるし、
ヒントらしき単語はちゃんと読めば書いてある。
次からは、ちゃんとエラーとじっくり向き合おうと思いました。
#まとめ
初めて「自分の作りたいものを自分で作った!」って感じ、最高に気持ちいい。