0

I am trying to create a paged scrollview I have used a TabView to create this.

here is my code

struct TT: Identifiable {
    let id = UUID()
    var v: String
}

struct TTest: View {
    @State var currentIndex = 0
    @State var data = [
        TT(v: "0")
    ]
    var body: some View {
        VStack {
            SwiftUI.TabView(selection: $currentIndex) {
                ForEach(data.indexed(), id: \.1.id) { index, value in
                    TTestConsomer(data: $data[index]).tag(index)
                }
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            Spacer()
            HStack {
                Image(systemName: "plus")
                    .resizable()
                    .frame(width: 50, height: 50)
                    .onTapGesture(perform: add)
                Image(systemName: "minus")
                    .resizable()
                    .frame(width: 50, height: 5)
                    .onTapGesture(perform: delete)
            }
            Text("\(currentIndex + 1)/\(data.count)")
        }
        
    }
    
    func add() {
        data.append(TT(v: "\(data.count)") )
    }
    
    func delete() {
        data.remove(at: currentIndex)
    }
}

struct TTestConsomer: View {
    @Binding var data: TT
    var body: some View {
        Text(data.v)
            .padding(30)
            .border(Color.black)
    }
}

// You also need this extension
extension RandomAccessCollection {
    func indexed() -> IndexedCollection<Self> {
        IndexedCollection(base: self)
    }
}

struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection {
    typealias Index = Base.Index
    typealias Element = (index: Index, element: Base.Element)

    let base: Base

    var startIndex: Index { base.startIndex }

    var endIndex: Index { base.endIndex }

    func index(after i: Index) -> Index {
        base.index(after: i)
    }

    func index(before i: Index) -> Index {
        base.index(before: i)
    }

    func index(_ i: Index, offsetBy distance: Int) -> Index {
        base.index(i, offsetBy: distance)
    }

    subscript(position: Index) -> Element {
        (index: position, element: base[position])
    }
}

When I click on the + button, I can add more tabs. But when I click on the delete button, My app crashes. What is the problem here? There does not seem to be anything wrong with the code.

Just a coder
  • 15,480
  • 16
  • 85
  • 138

1 Answers1

1

No, I don't think you're doing anything wrong. I submitted an issue to Apple about a related issue on the TabView a few months ago but never heard anything back. The debug comes from the collection view coordinator:

#0  0x00007fff57011ca4 in Coordinator.collectionView(_:cellForItemAt:) ()

It seems like after removing an item, the underlying collection view doesn't get updated. In UIKit, we would probably call .reloadData() to force it to update. In SwiftUI, you could redraw the collection view on the screen to force it to update. Obviously, this affects the UI and isn't a perfect solution.

struct TabViewTest: View {
    
    @State var isLoading: Bool = false
    @State var data: [String] = [
        "one",
        "two"
    ]
    
    var body: some View {
        if !isLoading, !data.isEmpty {
            TabView {
                ForEach(data, id: \.self) { item in
                    Text(item)
                        .tabItem { Text(item) }
                }
            }
            .tabViewStyle(PageTabViewStyle())
            .overlay(
                HStack {
                    Text("Add")
                        .onTapGesture {
                            add()
                        }
                    
                    Text("remove")
                        .onTapGesture {
                            remove()
                        }
                }
                , alignment: .bottom
            )
        }
    }
    
    func add() {
        data.append("three")
        print(data)
    }
    
    func remove() {
        isLoading = true
        data.removeFirst()
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            isLoading = false
        }
        print(data)
    }
}

struct TabVIewTest_Previews: PreviewProvider {
    static var previews: some View {
        TabViewTest()
    }
}
nicksarno
  • 3,850
  • 1
  • 13
  • 33