おそるおそる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位まできちんと並べて表示させてみます。