SwiftUI:数组无元素时隐藏按钮并解决下标越界问题
解决SwiftUI数组下标越界与动态隐藏按钮问题
你遇到的问题核心是直接通过固定下标访问数组元素,当数组长度不足时必然触发下标越界错误;另外要实现元素不足时隐藏按钮,不能只靠disabled,得用条件渲染来控制按钮是否显示。
一、核心解决思路
- 安全访问数组元素:给数组添加安全下标扩展,避免直接硬编码下标导致崩溃;
- 动态控制按钮显示:根据当前问题的选项数组长度,用
if let判断元素是否存在,存在才渲染对应按钮; - 复用UI代码:把重复的按钮样式抽成独立View,减少冗余代码。
二、修改后的完整代码
import SwiftUI // 给数组添加安全下标扩展,避免越界崩溃 extension Collection { subscript(safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } } struct TestModel { var text: String? var ansver: [String] var correctAnswer: String? } var test: [TestModel] = [ TestModel(text: "one", ansver: ["one", "two", "three", "four"], correctAnswer: "one"), TestModel(text: "five", ansver: ["five", "six", "seven", "eight"], correctAnswer: "five"), TestModel(text: "nine", ansver: ["nine", "ten", "twelve"], correctAnswer: "nine") // 3个元素 ] // 抽离按钮样式,复用代码 struct AnswerButtonLabel: View { let text: String var body: some View { Text(text) .foregroundColor(.black) .padding() .frame(maxWidth: .infinity, alignment: .leading) .background( RoundedRectangle(cornerRadius: 8) .stroke(Color.blue, lineWidth: 2) ) } } struct ContentView: View { @State var currentIndex: Int = 0 @State var score = 0 var body: some View { VStack { if currentIndex < test.count { // 安全访问题目文本,避免强制解包崩溃 if let questionText = test[currentIndex].text { Text(questionText) .font(.title) .padding(.bottom, 20) } // 第一个按钮:数组有第1个元素才显示 if let answer1 = test[currentIndex].ansver[safe: 0] { Button(action: { checkAnswer(answer: answer1) }) { AnswerButtonLabel(text: answer1) } .padding(.bottom, 10) } // 第二个按钮:数组有第2个元素才显示 if let answer2 = test[currentIndex].ansver[safe: 1] { Button(action: { checkAnswer(answer: answer2) }) { AnswerButtonLabel(text: answer2) } .padding(.bottom, 10) } // 第三个按钮:数组有第3个元素才显示 if let answer3 = test[currentIndex].ansver[safe: 2] { Button(action: { checkAnswer(answer: answer3) }) { AnswerButtonLabel(text: answer3) } .padding(.bottom, 10) } // 第四个按钮:数组有第4个元素才显示 if let answer4 = test[currentIndex].ansver[safe: 3] { Button(action: { checkAnswer(answer: answer4) }) { AnswerButtonLabel(text: answer4) } } } else { // 所有题目完成后显示得分 Text("最终得分:\(score)/\(test.count)") .font(.largeTitle) .fontWeight(.bold) } } .padding() } func checkAnswer(answer: String) { if test[currentIndex].correctAnswer == answer { score += 1 } // 控制索引递增,避免越界 currentIndex = min(currentIndex + 1, test.count) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
三、关键修改说明
- 安全下标扩展:通过
subscript(safe:)判断下标是否在数组合法范围内,返回可选值,彻底避免下标越界; - 条件渲染按钮:每个按钮都通过
if let判断对应元素是否存在,不存在则不渲染,实现“元素不足时隐藏按钮”的需求; - 修复得分逻辑:原来的
buttonAction传空字符串导致无法判断正确答案,现在改为传递实际选项文本; - 优化索引控制:用
min函数限制索引最大值,防止超出数组范围; - 代码复用:把按钮样式抽成
AnswerButtonLabel,减少重复代码,方便后续修改样式。
内容的提问来源于stack exchange,提问作者artemasmailru
相关产品推荐
相关产品推荐

