1

ピッカーの選択データをText()で表示しようとすると「Index out of range」というエラーが発生します。

エラーメッセージのイメージ

ただし、選択したデータを表示する Text() にコメントを付けると、正常に機能します。以下は、形式のピッカーのコードです。

struct VMPickerView: View {
    
    @State var vmIndex = 0
    @ObservedObject var stockViewModel = StockViewModel()

    var body: some View {
        
        let allVM = self.stockViewModel.arrKey

        return VStack {
        
            Form {
                
                Section {
                    
                    Picker(selection: $vmIndex, label: Text("Location")) {
                        
                        ForEach(0..<allVM.count, id: \.self) {
                            
                            Text(allVM[$0]).tag($0)
                        }
                    }
                    //Text(allVM[vmIndex])
                }
            }
        }
    }
}

以下は、「Text(allVM[vmIndex])」にコメントしたときのアプリケーションの画像です。

画面 1

画面 2

以下は、firebase からデータを取得して配列に格納するために使用したコードです。

class StockViewModel: ObservableObject {
    
    @Published var itemList = [ItemList]()
    @Published var arrKey = [String]()
    
    init() {
        retrieveAllVM()
    }
    
    func retrieveAllVM() {
        
        var arrKey = [String]()
       
        let ref = Database.database().reference().child("VM")

        ref.observeSingleEvent(of: .value, with: { snapshot in
            for items in snapshot.children {
                let itemSnap = items as! DataSnapshot
                let allKey = itemSnap.key
                arrKey.append(allKey)
            }
            self.arrKey = arrKey
            print(self.arrKey)
       })
    }
}

*変更後の私のコード:

class StockViewModel: ObservableObject {
    
    @Published var itemList = [ItemList]()
    @Published var arrKey = [String]()
    
    func retrieveAllVM() {
        
        var arrKey = [String]()
       
        let ref = Database.database().reference().child("VM")

        ref.observeSingleEvent(of: .value, with: { snapshot in
            for items in snapshot.children {
                let itemSnap = items as! DataSnapshot
                let allKey = itemSnap.key
                arrKey.append(allKey)
            }
            DispatchQueue.main.async {
                self.arrKey = arrKey
                print(self.arrKey)
            }
            //self.arrKey = arrKey
       })
    }
}
struct VMPickerView: View {
    
    @State var vmIndex = 0
    @ObservedObject var stockViewModel: StockViewModel

    var body: some View {
        
        let allVM = self.stockViewModel.arrKey

        return VStack {
        
            Form {
                
                Section {
                    
                    Picker(selection: $vmIndex, label: Text("Location")) {
                        
                        ForEach(0..<allVM.count, id: \.self) {
                            
                            Text(allVM[$0]).tag($0)
                        }
                    }
                    //Text(allVM[vmIndex])
                }
            }
        }.onAppear {
            self.stockViewModel.retrieveAllVM()
        }
    }
}
4

1 に答える 1

0

observeSingleEventメソッドは非同期のようです。@Publishedメインスレッドでプロパティを更新してください。

交換:

self.arrKey = arrKey

と:

DispatchQueue.main.async {
    self.arrKey = arrKey
}

ViewModel が作成されるたびに、init のコードが実行されます。

class StockViewModel: ObservableObject {
    ...
    init() {
        retrieveAllVM()
    }

通話retrieveAllVM.onAppear次の場所に移動できます。

struct VMPickerView: View {
    @State var vmIndex = 0
    @ObservedObject var stockViewModel = StockViewModel()

    var body: some View {
        let allVM = self.stockViewModel.arrKey

        return VStack {
            ...
        }.onAppear {
            self.stockViewModel.retrieveAllVM()
        }
    }
}

または、ViewModel を .xml ファイルに直接作成しないでくださいVMPickerView。親ビューで ViewModel を作成し、次のように渡しますVMPickerView

struct VMPickerView: View {
    @State var vmIndex = 0
    @ObservedObject var stockViewModel: StockViewModel // pass only
    ...
}

または、SwiftUI 2.0 を使用している場合は、次を使用できます@StateObject

struct VMPickerView: View {
    @State var vmIndex = 0
    @StateObject var stockViewModel = StockViewModel()
    ...
}

編集

インデックスを扱うのは危険です。他の理由でコードが失敗する場合は、iforguardステートメントを使用して、無効なインデックスにアクセスしないようにしてください。

それ以外の:

Form {
    Section {
        ...
        Text(allVM[vmIndex])
    }
}

ピッカー ビューに、現在のキー ビューを返す計算されたプロパティを追加できます。

@ViewBuilder
var currentKeyText: some View {
    if vmIndex < stockViewModel.arrKey.count {
        Text(stockViewModel.arrKey[vmIndex])
    }
}

次のようにアクセスします。

Form {
    Section {
        ...
        currentKeyText
    }
}
于 2020-07-29T18:20:04.087 に答える