我有一个父视图,它正在读取核心数据并创建子节点信息列表。我希望在值更新后立即更新每一行。
我的第一次尝试,我使用 fetchedresults 来创建一个列表,并将 @Observedobject 传递给另一个视图来渲染该行。它有点有效,但仅在您来回切换到不同的 UI 选项卡时更新视图,而不是当场更新。
我的第二次尝试,我在List和NodeListItem上使用了NotificationCenter.default.publisher和@State变量refreshID = UUID。这有助于重新渲染视图,但是当外设接收被动数据时,每个行项目每分钟都会调用 .onReceive 函数约 8-9 次,并且每当外设发送数据时,它还会忽略用于更新用户行的工作表视图短信通知
节点列表
struct NodeList: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@FetchRequest( sortDescriptors: [NSSortDescriptor(key: "user.vip", ascending: false), NSSortDescriptor(key: "lastHeard", ascending: false)], animation: .default)
var nodes: FetchedResults<RadioInfoEntity>
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
@State private var refreshID = UUID()
var body: some View {
NavigationStack (path: $path) {
List(nodes, id: \.self) { node in
NodeListItem(node: node,
connected: bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral?.num ?? -1 == node.num)
.onReceive(self.didSave) { _ in //the listener
self.refreshID = UUID()
print("generated a new UUID")
}
}.id(refreshID)
.onAppear {
if self.bleManager.context == nil {
self.bleManager.context = context
}
}
}
}
}
节点列表项
struct NodeListItem: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@ObservedObject var node: RadioInfoEntity
@State private var name: String = ""
var body: some View {
LazyVStack(alignment: .leading) {
Text(name)
let status = getStatus(status: name)
Text(status)
Button("Update") {
showingSheetView.toggle()
} .sheet(isPresented: $showingSheetView) {
SheetView(user: node)
}
}.onAppear(perform: {
if let tempName = node.user?.name{
self.name = tempName
}
})
}
}
struct SheetView: View {
@Environment(\.dismiss) var SheetViewDismiss
@ObservedObject var user: RadioInfoEntity
@State private var name: String = ""
var body: some View {
HStack{
TextField("name", text: $customName)
}
.onAppear(perform: {
self.longName = user.user?.name ?? ""
})
Button("Update") {
self.user.objectWillChange.send()
var u = User()
//this is a longer function in reality
u.name = getUpdatedName(name: name)
bleManager.saveUser(config: u, fromUser: connectedUser, toUser: user.user!)
SheetViewDismiss()
}
}
}
}
BLEmanager 的工作原理有点像这样
class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
// MARK: Data Read / Update Characteristic Event
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
switch characteristic.uuid {
case FROMRADIO_UUID:
switch decodedInfo.packet.decoded.portnum {
case .nodeinfoApp:
upsertNodeInfoPacket(packet: decodedInfo.packet, context: context!)
}
}
}
}
更新核心数据
func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let fetchNodeInfoAppRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "RadioInfoEntity")
fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
do {
let fetchedRadio = try context.fetch(fetchNodeInfoAppRequest) as? [RadioInfoEntity] ?? []
fetchedRadio[0].id = Int64(packet.from)
fetchedRadio[0].num = Int64(packet.from)
fetchedRadio[0].lastHeard = Date()
if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) {
if nodeInfoMessage.hasUser {
fetchedRadio[0].user!.userId = nodeInfoMessage.user.id
fetchedRadio[0].user!.num = Int64(nodeInfoMessage.num)
fetchedRadio[0].user!.name = nodeInfoMessage.user.name
}
}
do {
//this was never needed or seen to do anything so commented out
//fetchedRadio[0].objectWillChange.send()
try context.save()
print("💾 Updated NodeInfo from Node Info App Packet For: \(fetchedRadio[0].num)")
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Saving RadioInfoEntity from NODEINFO_APP \(nsError)")
}
} catch {
print("💥 Error Fetching RadioInfoEntity for NODEINFO_APP")
}
}
如有任何建议,我们将不胜感激
您需要删除
@State
并使用 @ObservedObject
,如下所示:
@ObservedObject var node: RadioInfoEntity
...
Text(node.name)