質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

1112閲覧

画面遷移ができません。カウントダウンアプリを作成しています。

RumiaCamellia

総合スコア5

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/06/16 12:01

前提・実現したいこと

初心者です。
・最初の画面(ViewController)でpickerを使用して時間を選択してもらう
・開始のボタンを押して画面遷移をさせる
・遷移先(NextViewController)で「00:00:00」のようにlabelに時間を表示させ、カウントダウンを進める

上記のようなアプリを作ろうと思っています。

エラーは出ていないのですが、画面遷移ができず、開始ボタンを押すとそのまま固まってしまいます。
画面遷移のやり方を教えていただきたいです。
また、値の渡し方がこれで合っているのかもわかりません。そのことに関してもアドバイスが欲しいです。

初心者で何もわからず、根本的なところが間違っているかもしれないのですが、それがどこだかわからないです。どなたか分かる方いらっしゃいましたら、よろしくお願いします。

該当のソースコード

ViewController.swift

1import UIKit 2 3 4class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource{ 5 6 @IBOutlet weak var start: UIButton! 7 @IBOutlet weak var time: UIPickerView! 8 9 let data = [[Int](arrayLiteral: 15,30,45,60,90,120)] 10 11 var pickerdata:String! 12 var timeTotal:Int! 13 14 15 16 @IBAction func start(_ sender: Any) { 17 18 let storyboard: UIStoryboard = self.storyboard! 19 let timer = storyboard.instantiateViewController(withIdentifier: "toNextViewController") as! NextViewController 20 21 timer.getTime = timeTotal 22 23 self.present(timer, animated: true, completion: nil) 24 25 total() 26 27 self.performSegue(withIdentifier: "toNextViewController", sender: nil) 28 29 } 30 31 32 override func viewDidLoad() { 33 super.viewDidLoad() 34 // Do any additional setup after loading the view. 35 36 time.dataSource = self 37 time.delegate = self 38 } 39 40 41 42 43 func numberOfComponents(in pickerView: UIPickerView) -> Int { 44 return data.count 45 } 46 47 func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 48 return data[component].count 49 } 50 51 52 func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 53 return String(data[component][row]) 54 } 55 56 func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 57 total() 58 } 59 60 // pickerの数字をたすメソッド 61 func total() { 62 timeTotal = data[0][time.selectedRow(inComponent: 0)] * 60 63 } 64 65} 66

NextViewController.swift

1import UIKit 2 3class NextViewController: UIViewController { 4 5 var timer = Timer() 6 var count = 0 7 var getTime:Int! 8 9 @IBOutlet weak var timerLabel: UILabel! 10 11 override func viewDidLoad() { 12 super.viewDidLoad() 13 runTimer() 14 timer.invalidate() 15 count = 0 16 timerLabel.text = timeString(time: TimeInterval(getTime)) 17 } 18 19 20 // カウントダウンをする関数 21 @objc func updateTimer() -> Int { 22 23 count += 1 24 //時間 = pickerで設定する時間 ー count 25 let remainCount = getTime! - count 26 timerLabel.text = timeString(time: TimeInterval(remainCount)) 27 28 // timerLabel.text = "(remainCount)" 29 30 // 0秒になったら止まる 31 if remainCount == 0 { 32 timer.invalidate() 33 timerLabel.text = "00:00:00" 34 } 35 return remainCount 36 37 } 38 39 //タイマーを動かす関数 40 func runTimer() { 41 timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true) 42 } 43 44 //00:00:00に変える処理 45 func timeString(time: TimeInterval) -> String { 46 let hour = Int(time) / 3600 47 let minutes = Int(time) / 60 % 60 48 let second = Int(time) % 60 49 50 return String(format: "%02d:%02d:%02d", hour, minutes, second) 51 } 52 53 54 override func viewDidAppear(_ animated: Bool) { 55 56 timerLabel.text = timeString(time: TimeInterval(getTime)) 57 print(getTime ?? "this is nil") 58 59 } 60 61} 62

試したこと

segueに問題があるのかと思い、開始ボタンからsegueをつけたり、ViewControllerからsegueをつけたりしているのですができませんでした。現在はViewControllerから関連づけています。

補足情報(FW/ツールのバージョンなど)

Swift5でコーディングしています。
こちらのサイトを参考にして作成しました。
https://qiita.com/katsualonso14/items/bf64e2201e8fc73125e0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

エラーは出ていないのですが、画面遷移ができず、開始ボタンを押すとそのまま固まってしまいます。

直接的な原因は、NextViewControllerで

Swift

1 override func viewDidLoad() { 2 super.viewDidLoad() 3 runTimer() 4 timer.invalidate() // <-ココが問題 5 count = 0 6 timerLabel.text = timeString(time: TimeInterval(getTime)) 7 }

という具合に、タイマーをinvalidate(無効にする、という意味)しているからだと思います。

画面遷移のやり方を教えていただきたいです。

提示していただいたソースを見ると、

Swift

1 let storyboard: UIStoryboard = self.storyboard! 2 let timer = storyboard.instantiateViewController(withIdentifier: "toNextViewController") as! NextViewController 3 4 timer.getTime = timeTotal 5 6 self.present(timer, animated: true, completion: nil) 7 8 total() 9 10 self.performSegue(withIdentifier: "toNextViewController", sender: nil)

とありますが、

  • StoryBoardで作成したViewControllerのインスタンス化
  • Segueでの画面遷移

の2つを混同して使われているように思えます。

元記事ではSegueを使っていませんので、前者に統一するのがいいかと思います。

また、値の渡し方がこれで合っているのかもわかりません。そのことに関してもアドバイスが欲しいです。

  • StoryBoardで作成したViewControllerのインスタンス化

で値渡しをするのであれば、この方法で問題ないかと思います。Segueを使う場合にはまた異なってきます。

その他、配列の初期化方法や、PickerViewに渡すデータの扱い方など、先々問題に繋がりそうな部分もありましたので、コメントを入れてみました。

もしわからない点があればコメントいただくか、内容によっては新たにご質問いただければと思います。

Swift

1import UIKit 2 3class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource{ 4 5 @IBOutlet weak var start: UIButton! 6 @IBOutlet weak var time: UIPickerView! 7 8 // MARK: 変更 9 // Int(arrayLiteral: ) はコンパイラが使う内部向け表現なので使わない 10 // また、今回の目的だと一次元配列で済むため、次のように書き換える 11 // let data = [[Int](arrayLiteral: 15,30,45,60,90,120)] 12 let data = [15, 30, 45, 60, 90, 120] 13 14 var pickerdata:String! 15 // MARK: 変更 16 // Int!型のように、!で宣言するオプショナル型の利用は極力避ける。 17 // 今回の場合は単純にゼロで初期化すれば良い。 18 //var timeTotal:Int! 19 var timeTotal = 0 20 21 @IBAction func start(_ sender: Any) { 22 23 let storyboard: UIStoryboard = self.storyboard! 24 let timer = storyboard.instantiateViewController(withIdentifier: "toNextViewController") as! NextViewController 25 26 // MARK: 27 // もし、timeTotalがオプショナル型で宣言されていたら、この時点で実行時エラーになる可能性が高い 28 timer.getTime = timeTotal 29 self.present(timer, animated: true, completion: nil) 30 31 // MARK: 削除 32 // present()で表示したのであれば、下記の行は不要 33 //total() 34 //self.performSegue(withIdentifier: "toNextViewController", sender: nil) 35 36 } 37 38 override func viewDidLoad() { 39 super.viewDidLoad() 40 // Do any additional setup after loading the view. 41 42 time.dataSource = self 43 time.delegate = self 44 45 // MARK: 追加 46 // timeTotal の初期値を900(秒)にする。 47 // 一番最初からピッカービューを動かさずに画面を遷移させても、初期値が代入されないことへの対応 48 timeTotal = 15 * 60 49 } 50 51 func numberOfComponents(in pickerView: UIPickerView) -> Int { 52 // MARK: 変更 53 // PickerView に使うのは1列だけなので、今回は決め打ちで値を返す 54 //return data.count 55 return 1 56 } 57 58 func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 59 // MARK: 変更 60 // 今回使うのは一次元配列なので、配列の要素数を .count を使って返す 61 //return data[component].count 62 return data.count 63 } 64 65 66 func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 67 // MARK: 変更 68 //return String(data[component][row]) 69 return String(data[row]) 70 } 71 72 func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 73 total() 74 } 75 76 // pickerの数字をたすメソッド 77 func total() { 78 // MARK: 変更 79 // 一次元配列になったため計算方法を変更 80 //timeTotal = data[0][time.selectedRow(inComponent: 0)] * 60 81 timeTotal = data[time.selectedRow(inComponent: 0)] * 60 82 } 83 84}

Swift

1import UIKit 2 3class NextViewController: UIViewController { 4 5 var timer = Timer() 6 var count = 0 7 var getTime:Int! 8 9 @IBOutlet weak var timerLabel: UILabel! 10 11 override func viewDidLoad() { 12 super.viewDidLoad() 13 runTimer() 14 // MARK: 削除 15 // invalidate()はタイマを止めるメソッドのため、記述するとタイマは動かずカウントダウンしない。 16 //timer.invalidate() 17 count = 0 18 timerLabel.text = timeString(time: TimeInterval(getTime)) 19 } 20 21 // カウントダウンをする関数 22 @objc func updateTimer() -> Int { 23 24 count += 1 25 //時間 = pickerで設定する時間 ー count 26 // MARK: 変更 27 // getTimeはオプショナル型だが、!で宣言されたオプショナル型なので、アクセス時のアンラップは不要 28 //let remainCount = getTime! - count 29 let remainCount = getTime - count 30 timerLabel.text = timeString(time: TimeInterval(remainCount)) 31 32 // timerLabel.text = "(remainCount)" 33 34 // 0秒になったら止まる 35 if remainCount == 0 { 36 timer.invalidate() 37 timerLabel.text = "00:00:00" 38 } 39 return remainCount 40 41 } 42 43 //タイマーを動かす関数 44 func runTimer() { 45 // MARK: コメント 46 // scheduledTimerは呼び出された直後から指定された関数の呼び出しを始める。 47 timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true) 48 } 49 50 //00:00:00に変える処理 51 func timeString(time: TimeInterval) -> String { 52 let hour = Int(time) / 3600 53 let minutes = Int(time) / 60 % 60 54 let second = Int(time) % 60 55 56 return String(format: "%02d:%02d:%02d", hour, minutes, second) 57 } 58 59 // MARK: 削除 60 // viewDidLoad()で同じような処理を行なっているので、どちらか一方に記述する 61// override func viewDidAppear(_ animated: Bool) { 62// 63// timerLabel.text = timeString(time: TimeInterval(getTime)) 64// print(getTime ?? "this is nil") 65// 66// } 67 68}

投稿2020/06/17 11:28

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

RumiaCamellia

2020/06/17 12:59

とても丁寧な回答、ありがとうございます。色々と勘違いをしていたことがわかりました。 重ねて質問失礼いたします。 最初の画面(ViewController.swift)の27行目 let timer = storyboard.instantiateViewController(withIdentifier: "toNextViewController") as! NextViewController にて、Thread 1: signal SIGABRT というエラーが出ています。 関連付けなどの確認はしたのですが、他に考えられる原因はありますでしょうか?
TsukubaDepot

2020/06/17 20:50

この場所で実行時エラーが出るということは、 1. instantiateViewControllerで指定しているIdentifierが間違っている(存在しない) 2. as!による強制ダウンキャストに失敗している 関連付けに間違いはないというお話ですが、この辺りを中心として再度確認されてはいかがでしょうか。 ご質問の段階では実行時エラーはなかったはずですので、どこかを間違って修正したことが考えられます。 どうしても見つからなければ、新しくプロジェクトを立ち上げて、ソースコードだけコピーするのも一つかと思います。 のどちらかしかないと思います。
RumiaCamellia

2020/06/18 14:01

ご返信ありがとうございます。identifierが間違っていたらしく、修正したところ動きました! 丁寧な回答、感謝します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問