此内容根据文章生成,并经过人工审核,仅用于文章内容的解释与总结
说在前面
上一篇我们学到了很多关于视图布局和样式,和像Image、Button、alert等交互元素,这一篇我们综合一下,把这些知识点融合,做一个猜旗子的demo。
直接的项目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| import SwiftUI
struct ContentView: View { static let allCountries = ["Estonia","France","Germany","Ireland","Italy","Monaco","Nigeria","Poland","Spain","UK","US"] @State private var countries = allCountries.shuffled() @State private var correctAnswer = Int.random(in: 0...2) @State private var showingScore = false @State private var showingResults = false @State private var scoreTitle = "" @State private var score = 0 @State private var questionCounter = 1 var body: some View { ZStack{ RadialGradient(stops: [ .init(color: Color(red:0.1,green: 0.2,blue: 0.45), location: 0.3), .init(color: Color(red:0.76,green:0.15,blue:0.26), location: 0.3) ], center: .top, startRadius: 200, endRadius: 700) .ignoresSafeArea() VStack(spacing: 15){ Spacer() Text("猜旗子") .foregroundColor(.white) .font(.largeTitle.weight(.semibold)) Spacer() VStack{ VStack{ Text("选择对应的旗子🚩") .foregroundStyle(.secondary) .font(.subheadline.weight(.heavy)) Text(countries[correctAnswer]) .font(.largeTitle.weight(.semibold)) } ForEach(0..<3){ number in Button{ flagTapped(number) }label: { Image(countries[number]) .clipShape(Capsule()) .shadow(radius:5) } } } .frame(maxWidth: .infinity) .padding(.vertical,20) .background(.regularMaterial) .clipShape(RoundedRectangle(cornerRadius: 20)) Spacer() Text("你的得分:\(score)") .foregroundColor(.white) .font(.title2.bold()) Spacer() } .padding() } .alert(scoreTitle,isPresented: $showingScore){ Button("继续",action: askQuestion) } message: { Text("你的得分是:\(score)") } .alert("游戏结束",isPresented: $showingResults){ Button("重新开始",action: newGame) } message: { Text("你的最终得分是:\(score)") } } func flagTapped(_ number: Int){ if number == correctAnswer { scoreTitle = "正确" score += 1 } else { scoreTitle = "错误!这个是\(countries[number])的旗子" if(score > 0){ score -= 1 } } if questionCounter == Self.allCountries.count - 3 { showingResults = true } else { showingScore = true } } func askQuestion(){ countries.remove(at: correctAnswer) countries.shuffle() correctAnswer = Int.random(in: 0...2) questionCounter += 1 } func newGame(){ questionCounter = 0 score = 0 countries = Self.allCountries askQuestion() } }
|
主界面:
猜错时的提示:
答对时的提示:
游戏结束时的界面:
讲讲思路
- 定义数据和状态
- 首先,我们定义了一个静态的数组 allCountries 来存储所有的国家名称。
- 使用 @State 包装器定义了一些关键的状态变量,如 countries (存储打乱顺序的国家名称)、correctAnswer (正确答案的索引)、showingScore 和 showingResults (用于控制得分和游戏结束提示框的显示)、scoreTitle (得分提示的标题)、score (得分)和 questionCounter (问题计数器)。
- 构建视图布局
- 使用 ZStack 来叠加多个视图元素,创建背景的径向渐变效果。
- 在 ZStack 内部,使用 VStack 进行垂直布局。
- 在 VStack 中,添加标题文本、显示当前要猜测的国家名称、通过 ForEach 循环创建三个按钮,每个按钮上显示一个国家的图片。
- 还添加了显示得分的文本。
- 按钮交互逻辑
- 为每个按钮定义了点击事件 flagTapped 。
- 在点击事件中,根据用户选择的按钮索引与正确答案索引进行比较。如果选择正确,增加得分;如果错误,减少得分(如果得分大于 0)。
- 根据问题计数器的值判断是否显示游戏结束或得分提示框。
- 游戏流程控制
- askQuestion 函数用于在每次回答后更新问题。它移除当前正确答案所在的国家名称,重新打乱国家名称数组,重新生成正确答案索引,并增加问题计数器。
- newGame 函数用于重置游戏状态,包括将问题计数器归零、得分归零、重新设置国家名称数组,并调用 askQuestion 开始新游戏。
知识点
- flagTapped 中会判断是否题答完,刚开始用的 allCountries.count ,到最后答题时会崩溃,想不通,后来想到每次都会随机三个选项,每次答完减一个,最后要留够三个,不然造成数组越界。、
- 更多地是对上一篇学到的样式和组件,进行一个总的演习,比如 alert 、渐变 、毛玻璃效果 等。