おそるおそるSwift 4-6 ハイローゲームを作る ~ハイスコアを表示する~

Swift5+Xcode11.7
おそるおそるSwift 4-5 ハイローゲームを作る ~ハイスコアを記録する~
Swift5+Xcode11.6 今回のゴール ハイスコアを記録して表示するために、まずはゲームごとにスコアを保存できるようにします。 どこへ保存するか 保存先候補は、アプリ内部かデータフォルダのどちらかとなります。 […]
今回のゴール
前回作成したハイスコアを表示させるためのサブビューを作ります。
ハイスコアを表示する画面を作成する
練習も兼ねてハイスコアを表示するサブビューを作成し、画面を切り替える仕様にしました。最初に、サブビュー(ScoreBoardとしました)のファイルを作成します。


配列をビューに表示させるときのAny型問題
サブビュー(ScoreBoard)に以下のようなコードを記述したところ、その配列はAny型のはずでString型とは限らないんじゃないの?という意味のエラーが表示されました。コードを入力するたびにSwiftから何か怒られるのではとビクビクしてしまいます。
import SwiftUI struct ScoreBoard: View { @ObservedObject var processor = Processor() var body: some View { List(0 ..< processor.highScore.count) { item in HStack { Text(String(item + 1)) Spacer() Text(self.processor.highScore[item][0]) Spacer() Text(self.processor.highScore[item][1]) Spacer() Text(self.processor.highScore[item][2]) } } } } struct ScoreBoard_Previews: PreviewProvider { static var previews: some View { ScoreBoard() } }
配列に格納するときに型を指定する
他にも方法はあるのでしょうが、今回は配列に値を格納するときにString型と明示することにしました。processorx.swiftの一部(下の図の39行目)を変更しました。
import Foundation class Processor : ObservableObject { @Published var history :[Int] = [5] //history:抽選数字履歴保持用配列 @Published var result: String = "" @Published var reward: Int = 1 var randNum: Int = 0 //randNum:乱数 let dateformatter = DateFormatter() var recordDate = "" var highScore:[[String]] { get { return UserDefaults.standard.array(forKey: "highScore") as? [[String]] ?? [] } set { UserDefaults.standard.set(newValue, forKey: "highScore") } } // func judge(choice:String) -> String { func judge(choice:String) { randNum = Int.random(in:1...9) history.append(self.randNum) for (index, his) in history.enumerated() { print("history[\(index)]: \(his)") } print("endIndex:\(history.endIndex)") let prevNum:Int = history[history.endIndex - 2] //prevNum:1つ前の抽選数字 print ("prevNum:\(prevNum)") let nowNum:Int = history[history.endIndex - 1] //nowNum:今回の抽選数字 print ("NowNum:\(nowNum)") if (nowNum == prevNum) || ((nowNum >= prevNum)&&(choice == "high")) || ((nowNum <= prevNum)&&( choice == "low")) { result = "Win" reward *= 2 } else { result = "Lose! You Lost All Money!!" dateformatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddHHmmss", options: 0, locale: Locale(identifier: "ja_JP")) recordDate = dateformatter.string(from: Date()) highScore.append([recordDate,String(reward),String(history.endIndex - 1)]) print (highScore) reward = 1 history = [5] } } func num(times:Int) -> String { if history.endIndex >= times { return String(history[history.endIndex - times]) } else { return "-" } } func reset() { UserDefaults.standard.removeObject(forKey: "highScore") } }

サブビューへのリンクを貼る
ビューの切り替えはさほど面倒ではなく、ビュー全体をNavigationViewと宣言して、NavigationLinkで行先のリンクを貼るだけです。NavigationViewの記述場所を誤ると、Viewの中にViewを作ってしまうのか、レイアウトが変になります。下の図のようにContentView.swiftを書き換えました。「ハイスコアへ」というのも味気がないので「高額当選記録へ」としました。
import SwiftUI struct ContentView: View { @ObservedObject var processor = Processor() var body: some View { NavigationView { VStack { Text("High&Low Game!") .font(/*@START_MENU_TOKEN@*/.largeTitle/*@END_MENU_TOKEN@*/) .fontWeight(.heavy) .foregroundColor(Color.white) .background(/*@START_MENU_TOKEN@*/Color.orange/*@END_MENU_TOKEN@*/) Spacer() HStack { Spacer() Text("3回前") .frame(width: 50.0) Text("2回前") .frame(width: 50.0) Text("前回") .frame(width: 50.0) Text("今回") .font(.title) .frame(width: 80.0) Spacer() } HStack { Spacer() Text(processor.num(times:4)) .frame(width: 50.0) Text(processor.num(times:3)) .frame(width: 50.0) Text(processor.num(times:2)) .frame(width: 50.0) Text(processor.num(times:1)) .font(.largeTitle) .frame(width: 80.0) Spacer() } Spacer() HStack { Spacer() Button(action: { self.processor.judge(choice: "high") }) { Text("High") .font(.largeTitle) .fontWeight(.bold) .foregroundColor(Color.white) } .frame(width: 100.0) .background(/*@START_MENU_TOKEN@*/Color.red/*@END_MENU_TOKEN@*/) Spacer() Button(action: { self.processor.judge(choice: "low") }) { Text("Low") .font(.largeTitle) .fontWeight(.bold) .foregroundColor(Color.white) } .frame(width: 100.0) .background(Color.blue) Spacer() } Spacer() Text(processor.result) .font(.title) .fontWeight(.heavy) .foregroundColor(Color.orange) .multilineTextAlignment(.center) Text("$\(processor.reward)") .font(.largeTitle) .fontWeight(.black) .foregroundColor(Color.red) HStack { Button(action: processor.reset) { Text("ResetData") } Spacer() NavigationLink(destination: ScoreBoard()) { Text("高額当選記録へ") } } } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
次回は
実は配列を格納順にはき出しているだけで、スコア順に並んでいません。次回は、上位20位まできちんと並べて表示させてみます。