SwiftUI exclui linhas de uma lista

Oct 24 2020

Como podem ver na imagem, tenho uma lista de cores, gostaria de poder também dar a possibilidade de eliminar uma cor do array.

Eu tentei adicionar uma lista e, em seguida, chamar a onDeletechamada em ForEach, mas ele não está funcionando bem, me dá problemas.

Além disso, gostaria que a lista tivesse o tamanho dos elementos contidos.

Erro:

Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444

Alguém pode me dar algum conselho?

Código:

import SwiftUI

struct ContentView: View {
    var cornerRadius: CGFloat = 16
    
    @State public var select = 2
    @State public var bgColors: [Color] =
        [
            Color(red: 21.0/255.0, green: 101.0/255.0, blue: 192.0/255.0),
            Color(red: 255.0/255.0, green: 193.0/255.0, blue: 7.0/255.0),
            Color(red: 76.0/255.0, green: 175.0/255.0, blue: 80.0/255.0)
        ]
    @Environment(\.colorScheme) var colorScheme
    
    @State var isShowPicker: Bool = false
    @State var image: Image? = Image("placeholder")
    
    @State private var url: String = "https://a.wattpad.com/useravatar/climaxmite.256.718018.jpg"
    
    init() {
        // Segmented control colors
        UISegmentedControl.appearance().backgroundColor = .systemGray6
        UISegmentedControl.appearance().selectedSegmentTintColor = UIColor(Color.blue)
        UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.systemBackground], for: .selected)
        UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.label], for: .normal)
    }
    
    var body: some View {
        VStack{
            
            ZStack {
                RoundedRectangle(cornerRadius: cornerRadius)
                    .frame(width: UIScreen.main.bounds.width-40, height: 100, alignment: .center)
                    .foregroundColor(colorScheme == .dark ? .black : .white)
                VStack(spacing: 12) {
                    ZStack {
                        Rectangle()
                            .frame(width: UIScreen.main.bounds.width-47, height: 35, alignment: .center)
                            .foregroundColor(Color(UIColor.systemGray6))
                            .cornerRadius(cornerRadius, corners: [.topLeft, .topRight])
                        Text("Select Background")
                            .foregroundColor(Color(UIColor.label))
                            .font(.subheadline)
                            .bold()
                    }
                    Picker(selection: $select, label: Text("Select Background")) { Text("Url").tag(0) Text("Select Image").tag(1) Text("Gradient").tag(2) }.pickerStyle(SegmentedPickerStyle()) .padding(EdgeInsets(top: 0, leading: 30, bottom: 0, trailing: 30)) Spacer() .frame(height: 3) } } if self.select == 0 { VStack{ ZStack { RoundedRectangle(cornerRadius: cornerRadius) .frame(width: UIScreen.main.bounds.width-40, height: 42, alignment: .center) .foregroundColor(Color(UIColor.systemBackground)) TextField("http://", text: $url)
                            .padding(10)
                            .frame(width: UIScreen.main.bounds.width-40)
                            .foregroundColor(Color(UIColor.label))
                            .cornerRadius(cornerRadius)
                            .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 10))
                    }
                    
                    Button(action: {
                        
                    }, label: {
                        Text("Submit")
                            .foregroundColor(Color(UIColor.systemBackground))
                            .bold()
                    })
                    .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
                    .foregroundColor(.white)
                    .font(.subheadline)
                    .background(Color.blue)
                    .cornerRadius(cornerRadius)
                }
            }
            
            if self.select == 1 {
                VStack {
                    Button(action: {
                        withAnimation {
                            self.isShowPicker.toggle()
                        }
                    }) {
                        Image(systemName: "photo")
                            .font(.headline)
                            .foregroundColor(colorScheme == .dark ? .white : .black)
                        Text("Import")
                            .font(.headline)
                            .foregroundColor(colorScheme == .dark ? .white : .black)
                        
                    }
                    .foregroundColor(.black)
                }
                .sheet(isPresented: $isShowPicker) { ImagePicker(image: self.$image)
                }
            }
            
            if self.select == 2 {
                VStack(alignment: .trailing){
                    Button(action: {
                        bgColors.append(Color.clear)
                    }) {
                        Image(systemName: "plus")
                            .font(.headline)
                            .foregroundColor(colorScheme == .dark ? .white : .black)
                            .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 15))
                    }
                    
                    List {
                        
                        ForEach(Array(bgColors.enumerated()), id: \.offset) { index, element in
                            ZStack {
                                ColorPicker("Set the background color", selection: $bgColors[index]) } .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 10)) } .onDelete(perform: delete) }.background(Color.blue) } } Spacer() } .padding(.top, 25) .ignoresSafeArea(.keyboard) .background(Color(UIColor.systemGray6)) .edgesIgnoringSafeArea(.all) } func delete(at offsets: IndexSet) { bgColors.remove(atOffsets: offsets) } } struct RoundedCorner: Shape { var radius: CGFloat = .infinity var corners: UIRectCorner = .allCorners func path(in rect: CGRect) -> Path { let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) return Path(path.cgPath) } } extension View { func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { clipShape( RoundedCorner(radius: radius, corners: corners) ) } } // extension for keyboard to dismiss extension UIApplication { func endEditing() { sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } } struct ImagePicker: UIViewControllerRepresentable { @Environment(\.presentationMode) var presentationMode @Binding var image: Image? class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { @Binding var presentationMode: PresentationMode @Binding var image: Image? init(presentationMode: Binding<PresentationMode>, image: Binding<Image?>) { _presentationMode = presentationMode _image = image } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage image = Image(uiImage: uiImage) presentationMode.dismiss() } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { presentationMode.dismiss() } } func makeCoordinator() -> Coordinator { return Coordinator(presentationMode: presentationMode, image: $image)
    }
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController,
                                context: UIViewControllerRepresentableContext<ImagePicker>) {
        
    }
    
}

Respostas

2 George_E Oct 24 2020 at 14:31

O problema é que no seu List, idvocê o dá \.offset. No entanto, como você está removendo dados de bgColors, então esses dados podem mudar. Em vez disso, você deve definir o idcomo \.elementporque ele será constante para cada cor.

Considere este exemplo simplificado, que falha quando você remove um Colorda lista:

struct ContentView: View {
    
    @State private var arr: [Color] = [.red, .green, .blue]

    var body: some View {
        List {
            ForEach(Array(arr.enumerated()), id: \.offset) { (index, _) in
                ColorPicker("Color", selection: $arr[index])
            }
            .onDelete(perform: delete)
        }
    }
    
    private func delete(at offsets: IndexSet) {
        arr.remove(atOffsets: offsets)
    }
}

E o exemplo de trabalho, onde as mudanças são iddadas ao List, e as novas Bindingà cor:

struct ContentView: View {

    @State private var arr: [Color] = [.red, .green, .blue]

    var body: some View {
        List {
            ForEach(Array(arr.enumerated()), id: \.element) { (index, _) in
                ColorPicker(
                    "Color",
                    selection: Binding<Color>(
                        get: { arr[index] },
                        set: { arr[index] = $0 }
                    )
                )
            }
            .onDelete(perform: delete)
        }
    }

    private func delete(at offsets: IndexSet) {
        arr.remove(atOffsets: offsets)
    }
}