Home > other >  index out of bound from TabViewStyle of SwiftUI
index out of bound from TabViewStyle of SwiftUI

Time:01-17

I am trying to implement a tabView that takes a list of items into pages that I could swipe back and forth. However, it keeps bugging out with an "index out of bound" error. It's confusing to me because I never set index at all, and I don't know how to force an index either...

Below are my code. Apologize for any naive code, I am new to SwiftUI. Any helps are appreciated, thank you!


import SwiftUI

//@Published private var list = QuestionList.self
struct QuestionList: Codable {
    var list:[QuestionItem]
}

class QuestionItem: Codable, Identifiable {
    var id: Int
    var text: String
    var type: Int
    var answer: String
}


struct ContentView: View {
    @State private var qlist = [QuestionItem]()
    @State private var isShowForm = false
    @State private var q1 = true
    @State private var answer = ""
    @State private var isOn = [Bool]()
    @State private var selectedTab = 0
    func showForm() {
        isShowForm = !isShowForm
        let url = URL(string: "http://127.0.0.1:3000/question")!
        let task = URLSession.shared.dataTask(with: url) {
            data, response, error in
            if let error = error {
                print(error)
                return
            }
            guard let httpResponse = response as? HTTPURLResponse,
                  (200...299).contains(httpResponse.statusCode) else {
                      print(response)
                      return
                  }
            guard let data = data else {
                return
            }
            do {
                let list = try JSONDecoder().decode([QuestionItem].self, from:data)
                qlist = list
                print(qlist[1])
                for i in 0..<qlist.count {
                    isOn.append(qlist[i].type == 0)
                }
                print(isOn)
//                print(isOn)
                print(type(of: qlist[1]))
            } catch {
                print("error: ", error)
            }
        }
        task.resume()
    }
    var body: some View {
        Text("Hello, world!")
            .padding()
        Button("Open Form") {
            self.showForm()
        }

        if (isShowForm) {
            TabView(selection: $selectedTab) {
                ForEach(qlist.indices, id: \.self) { index in
                    if qlist[index].type == 0 {
                        HStack {
                            Text("\(self.qlist[index].text)")
//                            Toggle("", isOn: $isOn[index])
                            Toggle("", isOn: $q1)
                        }
                    } else {
                        VStack {
                            Text("\(self.qlist[index].text)")
//                                .lineLimit(2)
//                                .multilineTextAlignment(.leading)
                                .fixedSize(horizontal: false, vertical: true)
//                                .frame(width: 300)
                            TextField("Enter your answer here:", text: $qlist[index].answer) {
                            }
                        }
                    }
                }
            }
        .tabViewStyle(.page(indexDisplayMode: .always))
        .indexViewStyle(.page(backgroundDisplayMode: .always))
        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

CodePudding user response:

It is not recommended to use indices in ForEach loops, instead use something like this updated code that allows you to use your TextField :

            ForEach($qlist) { $qItem in  // <-- here $
                 if qItem.type == 0 {
                     HStack {
                         Text(qItem.text)
//                            Toggle("", isOn: $isOn[index])
                         Toggle("", isOn: $q1)
                     }
                 } else {
                     VStack {
                         Text(qItem.text)
//                                .lineLimit(2)
//                                .multilineTextAlignment(.leading)
                             .fixedSize(horizontal: false, vertical: true)
//                                .frame(width: 300)
                         TextField("Enter your answer here:", text: $qItem.answer) {
                         }
                     }
                 }
             }

and as I mentioned in my comment, remove the print(qlist[1]) and print(type(of: qlist[1])) in showForm, because if qlist is empty or only has one element, you will get the index out of bound error . Remember one element is qlist[0].

EDIT-1: full test code:

this is the code I used in my test. It does not give any index errors.

import SwiftUI

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
 
struct ContentView: View {
    @State var qlist = [QuestionItem]()
    
    @State private var isShowForm = false
    @State private var q1 = true
    @State private var answer = ""
    @State private var isOn = [Bool]()
    @State private var selectedTab = 0
    
    func showForm() {
        let url = URL(string: "http://127.0.0.1:3000/question")!
        let task = URLSession.shared.dataTask(with: url) {
            data, response, error in
            if let error = error {
                print(error)
                return
            }
            guard let httpResponse = response as? HTTPURLResponse,
                  (200...299).contains(httpResponse.statusCode) else {
                    //  print(response)
                      return
                  }
            guard let data = data else {
                return
            }
            do {
                let list = try JSONDecoder().decode([QuestionItem].self, from:data)
                qlist = list
  //              print(qlist[1])
                for i in 0..<qlist.count {
                    isOn.append(qlist[i].type == 0)
                }
                print(isOn)
//                print(isOn)
    //            print(type(of: qlist[1]))
                isShowForm.toggle()   // <--- here important
            } catch {
                print("error: ", error)
            }
        }
        task.resume()
    }
    
    var body: some View {
        Text("Hello, world!")
            .padding()
        Button("Open Form") {
          //  self.showForm()
            // simulate showForm()
            qlist = [
                QuestionItem(id: 0, text: "text0", type: 0, answer: "0"),
                QuestionItem(id: 1, text: "text1", type: 1, answer: "1"),
                QuestionItem(id: 2, text: "text2", type: 2, answer: "2"),
                QuestionItem(id: 3, text: "text3", type: 3, answer: "3")
            ]
           isShowForm.toggle()   // <--- here after qlist is set
        }
        
        if (isShowForm) {
            TabView(selection: $selectedTab) {
                ForEach($qlist) { $qItem in
                    if qItem.type == 0 {
                        HStack {
                            Text(qItem.text)
                            //  Toggle("", isOn: $isOn[index])
                            Toggle("", isOn: $q1)
                        }
                    } else {
                        VStack {
                            Text(qItem.text)
                            //                                .lineLimit(2)
                            //                                .multilineTextAlignment(.leading)
                                .fixedSize(horizontal: false, vertical: true)
                            //                                .frame(width: 300)
                            TextField("Enter your answer here:", text: $qItem.answer)
                                .border(.red)
                        }
                    }
                }
            }
            .tabViewStyle(.page(indexDisplayMode: .always))
            .indexViewStyle(.page(backgroundDisplayMode: .always))
        }
    }
}

struct QuestionItem: Codable, Identifiable {  // <-- here note the struct
    var id: Int
    var text: String
    var type: Int
    var answer: String
}
  •  Tags:  
  • Related