简体   繁体   English

选择器选项未动态更新 SwiftUI

[英]Picker Options Not Updating Dynamically SwiftUI

I have a picker that needs to dynamically update as it finds new BLE peripherals.我有一个选择器,它需要在找到新的 BLE 外围设备时动态更新。 However, the picker never updates with the new values.但是,选择器永远不会使用新值进行更新。

I had a List that was working fine with the following code, so I know everything is ok in my BLE Manager:我有一个使用以下代码可以正常工作的列表,所以我知道在我的 BLE 管理器中一切正常:

List(bleManager.peripheralsDisp) { peripheral in
    Text(peripheral.displayName)
  }.frame(height: 300)

However, the same code used for the picker, does not work:但是,用于选择器的相同代码不起作用:

Picker(selection: $selectedDevice, label: Text("")) {
    ForEach(self.bleManager.peripheralsDisp, id: \.self) { peripheral in
      Text(peripheral.displayName)
    }
  }
  .frame(width: 200, height: 40, alignment: .center)

Any advice as to how to get the picker options to dynamically update would be much appreciated任何关于如何让选择器选项动态更新的建议将不胜感激

EDIT:编辑:

Here is my BLEManager.swift file这是我的 BLEManager.swift 文件

import Foundation
import CoreBluetooth

struct PeripheralDisp: Hashable {
    let serialNo: Int
    let displayName: String
}

class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate {
        
    @Published var isSwitchedOn = false
    @Published var peripheralsDisp = [PeripheralDisp]()

    var myCentral: CBCentralManager!
    var myPeripheral: CBPeripheral!
    
    override init() {
        super.init()
        
        myCentral = CBCentralManager(delegate: self, queue: nil)
        myCentral.delegate = self
    }
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            isSwitchedOn = true
            startScanning()
        }
        else {
            isSwitchedOn = false
            stopScanning()
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        
        var peripheralName: String = ""

        var peripheralSerialNo: Int = 0
        var peripheralDisplayName: String = ""
        
        if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
            peripheralName = name
        }
        else {
            peripheralName = "Unknown"
        }
                let newPeripheralDisp = PeripheralDisp(serialNo: newPeripheralDisp.count, displayName: peripheralDisplayName)
                peripheralsDisp.append(newPeripheralDisp) //peripheralDisplayName)

    }
    
    func startScanning() {
        print("StartScanning")
        myCentral.scanForPeripherals(withServices: nil, options: nil)
    }
    
    func stopScanning() {
        print("StopScanning")
        myCentral.stopScan()
    }
}

Full ContentView.swift:完整内容查看.swift:

struct ContentView: View {
    
    @EnvironmentObject var viewRouter: ViewRouter
    @ObservedObject var bleManager = BLEManager()
    @State var selectedDevice = 0
    
    var body: some View {
        
        
        VStack(spacing: 10) {
                        
            Spacer()
         
            // Device Picker
            Picker(selection: $selectedDevice, label: Text("")) {
                ForEach(self.bleManager.peripheralsDisp, id: \.self) { peripheral in
                    Text(peripheral.displayName)
                }
            }
            .frame(width: 200, height: 40, alignment: .center)
            

            /*List(bleManager.peripherals) { peripheral in
                Text(peripheral.displayName)
            }.frame(height: 300)
            
            Spacer()*/
            
         }
    }
}

There are a couple of issues here.这里有几个问题。 First SwiftUI's ForEach behaves differently for static content and dynamic content.首先 SwiftUI 的ForEach对于 static 内容和动态内容的行为不同。 If you want your content to be dynamic, make your model conform to Identifiable and use the overload of ForEach that just takes a sequence of Identifiable .如果您希望您的内容是动态的,请使您的 model 符合Identifiable并使用仅采用Identifiable序列的ForEach的重载。 Second you are changing the list so its possible that the selection could change as items drop in an out.其次,您正在更改列表,因此选择可能会随着项目的退出而改变。 I suppose you could just reset the selection to 0 if the selected item is deleted.我想如果所选项目被删除,您可以将选择重置为 0。 to do this you should move the selection into your ObservableObject and not your view.为此,您应该将选择移动到您的ObservableObject而不是您的视图中。 I actually think the conceptually simpler idea here is to use a list and not a picker.我实际上认为这里概念上更简单的想法是使用列表而不是选择器。 Just update the list and when they tap on an item you select it.只需更新列表,当他们点击您 select 的项目时。 (ie the list does not need to maintain the idea that some item is always selected while the picker does). (即列表不需要保持选择器总是选择某些项目的想法)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM