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

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

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

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

Q&A

解決済

1回答

871閲覧

ジャンケンアプリの作成

Narikura

総合スコア6

Swift

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

0グッド

0クリップ

投稿2020/08/09 08:19

編集2020/08/10 04:27

前提・実現したいこと

ジャンケンアプリの作成になります。
✊✌️✋のいずれかのUIButtonを押すと、cpのimageViewが✊✌️✋の順番に画像がグルグルし、2秒後にlabelが勝敗判定(win, draw, lose)に変化します。同時にグルグルしていたimageViewがcpが出した手の画像に切り替わります。ちなみにcpの手はランダムです。

発生している問題・エラーメッセージ

2回目以降のジャンケン結果がおかしくなる。
自分の押した手とcpの出力された手の正しい勝敗がlabelにprintされない。また、2回目以降imageViewのグルグルがなくなる。

該当のソースコード

Swift

1 2import UIKit 3 4final class ViewController: UIViewController { 5 6 @IBOutlet weak var cpHand: UIImageView! 7 @IBOutlet weak var janken: UILabel! 8 @IBOutlet weak var selectbutton: UIButton! 9 @IBOutlet weak var scissorsbutton: UIButton! 10 @IBOutlet weak var paperbutton: UIButton! 11 @IBOutlet weak var rockbutton: UIButton! 12 13 var timer: Timer? 14 var nowIndex:Int = 0 15 16 enum hand: Int { 17 case rock = 0 18 case scissors = 1 19 case paper = 2 20 } 21 22 var cpHandImage:[UIImage] = [ 23 UIImage(named: "rockpic")!, 24 UIImage(named: "scissorspic")!, 25 UIImage(named: "paperpic")! 26 ] 27 28 override func viewDidLoad() { 29 super.viewDidLoad() 30 // Do any additional setup after loading the view. 31 } 32 33 @IBAction func selectbutton(_ sender: UIButton) { 34 35 let Button = Int(sender.tag) 36 guard let youHand = hand(rawValue: Button) else { 37 return 38 } 39 guard let cpHand = hand(rawValue: Int.random(in: 0...2)) else { 40 return 41 } 42 43 if timer == nil { 44 timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(changeImage), userInfo: nil, repeats: true) 45 } 46 DispatchQueue.main.asyncAfter(deadline: .now() + 1.99) { 47 self.timer?.invalidate() 48 self.janken(youHand: youHand, cpHand: cpHand) 49 self.result(youHand: youHand, cpHand: cpHand) 50 self.timer = nil 51 } 52 } 53 54 @objc func changeImage() { 55 nowIndex += 1 56 if (nowIndex == cpHandImage.count) { 57 nowIndex = 0 58 } 59 cpHand.image = cpHandImage[nowIndex] 60 } 61 62 func janken(youHand: hand, cpHand: hand) { 63 if youHand.rawValue == cpHand.rawValue { 64 janken.text = "you draw" 65 }else if (youHand.rawValue + 1)%3 == cpHand.rawValue { 66 janken.text = "you win!" 67 }else { 68 janken.text = "you lose" 69 } 70 } 71 72 func result(youHand: hand, cpHand: hand) { 73 if cpHand.rawValue == 0 { 74 self.cpHand.image = UIImage(named: "rockpic") 75 } else if cpHand.rawValue == 1 { 76 self.cpHand.image = UIImage(named: "scissorspic") 77 } else if cpHand.rawValue == 2 { 78 self.cpHand.image = UIImage(named: "paperpic") 79 } 80 } 81} 82

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

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

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

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

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

TsukubaDepot

2020/08/09 13:28

掲載されているコードが崩れているようです。 このままだと見にくいですし、仮にコピーしても正しく動かすのに手間がかかりますので、整形しなおしていただけますでしょうか。
Narikura

2020/08/09 13:34

申し訳ございません。修正いたしました。よろしくお願いいたします。
Narikura

2020/08/10 04:49 編集

ご回答いただきありがとうございます。ご回答のように実装いたしました(質問ページのcodeも伴って変更させていただきました)。しかしながら、✊✌️✋全てのボタンが✊ボタンとして処理されてしまっているようです。改善策を教えていただくことは可能でしょうか。自分としてはsender.tagで割り振られているものだと思っておりました。 追記: これまでバラバラに宣言されていたbuttonを          @IBOutlet var selectbutton: [UIButton]!    でまとめましたが、変化はありませんでした。
TsukubaDepot

2020/08/10 04:59

試しに変更後のコードで動かしてみましたが、私の環境だときちんと動いています。 もちろん、ボタンは sender.tag で割り振っていることに気付いていましたので、StoryBoard で各ボタンに0, 1, 2 のタグを割り振ってあります。 ちなみに、私の回答に対するご質問ですので、できれば回答直下のコメント欄にコメントいただければと思います。
Narikura

2020/08/10 06:56

私が勘違いをしておりました。何かの拍子でtagが全て0になっていたようです。ありがとうございました。 コメントにつきましても以後気をつけます。不慣れなものですみませんでした。
guest

回答1

0

ベストアンサー

Swift

1 if (timer == nil) { 2 timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(changeImage), userInfo: nil, repeats: true) 3 }

ここで timernil か否かをチェックしていますが、2秒経過後に再度 timernil に戻していないため、実質このブロック内は初回のみしか実行されません。

たとえば、このような感じに変更すると期待したように動くかと思います。

Swift

1import UIKit 2 3final class ViewController: UIViewController { 4 // 中略 5 6 // MARK: 安全のため ? のオプショナル型で宣言する 7 //var timer: Timer! 8 var timer: Timer? 9 10 // 中略 11 12 @IBAction func selectbutton(_ sender: UIButton) { 13 14 let Button = Int(sender.tag) 15 guard let youHand = hand(rawValue: Button) else { 16 return 17 } 18 guard let cpHand = hand(rawValue: Int.random(in: 0...2)) else { 19 return 20 } 21 22 // MARK: DispatchQueue に入れる 23 // Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) {_ in 24 // self.janken(youHand: youHand, cpHand: cpHand); 25 // self.result(youHand: youHand, cpHand: cpHand) 26 // } 27 28 // MARK: 一般的に Swift の if では () は付けない 29 //if (timer == nil) { 30 if timer == nil { 31 timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(changeImage), userInfo: nil, repeats: true) 32 } 33 34 DispatchQueue.main.asyncAfter(deadline: .now() + 1.99) { 35 // MARK: timer は ? のオプショナル型なので、オプショナルチェーン(ドットの前が?)となる 36 //self.timer.invalidate() 37 self.timer?.invalidate() 38 // MARK: ラベルの更新なども DispatchQueue に入れてしまう 39 self.janken(youHand: youHand, cpHand: cpHand) 40 self.result(youHand: youHand, cpHand: cpHand) 41 // MARK: timer を nil にする 42 self.timer = nil 43 } 44 } 45 46 // 後略 47}

!付きのオプショナル型に再度 nil を代入することは可能なのですが、バグの温床になりかねないので、? 付きのオプショナル型として宣言した方がいいかと思います。

それに伴い、timer のメソッドにアクセスするためには、オプショナルチェーン(Optional Chaining)を使う必要がありますので、ご確認ください。

また、2秒後の処理は一つの DispatchQueue にまとめた方が見通しが立ちやすいと思います。

ところで、現在の実装だと、ボタンを押した後結果が出るまでの間もボタンを押すことが可能となっています。このままだと問題が発生する可能性がありますので、一度ボタンを押したら結果を押すまでボタンを操作できないようにしたほうがいいかと思います。

投稿2020/08/09 21:43

TsukubaDepot

総合スコア5086

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

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

Narikura

2020/08/10 06:57

解決いたしました。2度にわたる質問にも答えていただきありがとうございました。とても分かりやすく手厚いご回答でした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問