有没有办法让NavigationLink同时连接和导航?

问题描述 投票:0回答:1

我正在为一家石油和天然气公司构建一个应用程序。该应用程序将在现场用于连接和管理将润滑剂注入泵的设备。该应用程序应按如下方式运行:用户从 DeviceListView 中选择要管理的设备,应用程序将导航到 HomeView,用户可以在其中以自己想要的方式设置系统。 我正在解决的问题是导航。 按原样,当用户点击该行的绿色部分(蓝色突出显示)时,我的 NavigationLink 将进行导航,但它不会与设备连接。这意味着,所有要在 HomeView 中显示的值都不会显示。 但是,当我点击文本(红色/橙色突出显示)时,应用程序将连接到设备并加载控制台中的特征,但它不会导航到 HomeView。 我以为这是一个 UI 问题。我尝试了 H-Stack、Z-stack、修改器等的一大堆组合,但结果仍然相同。 有没有办法将 NavigationLink 设置为同时执行连接和导航或使用相同的点击手势? 设备列表视图

    import SwiftUI
    import CoreData
    import CoreBluetooth
    import Foundation
    
    //Defines the model for an item in the device list.
    struct DeviceViewItem: Identifiable{
        var id = UUID()
        var uuid: String?
        var sn: String?
        var discovered: Bool
        var name: String?
    }
    
    // Making a shared property to store savedArray
    class DeviceDataManager: ObservableObject {
        static let shared = DeviceDataManager() // Singleton instance
        @Published var savedArray: [DeviceViewItem] = []
    }
    
    struct DeviceListView: View {
        //CoreData vars:
        @FetchRequest(entity: Device.entity(), sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)]) var coreDataDevices: FetchedResults<Device>
        @EnvironmentObject var centralManager: CentralManager
        @Environment(\.managedObjectContext) var viewContext
        
        // State for managing deletion confirmation alert
        @State private var showingDeleteAlert = false
        @State private var deviceToDelete: Device? = nil
    
        var body: some View {
            NavigationStack {
                ZStack{
                    Color("Green 1").ignoresSafeArea()
                    VStack{
                        Image("HeliosLogoSmall")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 200, height: 100)
                            .padding(.top, 40)
                        Text("CONNECT TO A DEVICE")
                            .textStyle(TextStyles.headerBold)
                            .padding()
                    List {
                        let deviceViewList = buildDeviceListView(savedDevice: coreDataDevices, discoveredDevice: centralManager.discoveredPeripherals)
                        ForEach(deviceViewList, id: \.id) { device in
                            NavigationLink(destination: HomeView(), label:{
                                HStack {
                                    Text(device.name ?? "Unknown Device")
                                        .textStyle(TextStyles.display4)
                                }
                                .foregroundColor(device.discovered ? .black : .gray)
                                .onTapGesture {
                                    signalConnect(device: device, manager: centralManager)
                                    updateSaveDevice(deviceViewItem: device, context: viewContext)
                                }
                            }
                            )
                            .listRowBackground(Color("Green 1"))
                        }
                        // Swipe to delete functionality
                        .onDelete(perform: showDeleteAlert)
                    }
                    .listStyle(.plain)
                    .font(Font.custom("InstrumentSans-Regular", size:22))
                    .foregroundColor(Color("Grey 5"))
                    .alert(isPresented: $showingDeleteAlert) {
                        Alert(
                            title: Text("Delete Device"),
                            message: Text("Are you sure you want to delete this device? This action cannot be undone."),
                            primaryButton: .destructive(Text("Delete")) {
                                if let device = deviceToDelete {
                                    deleteDevice(device)
                                }
                            },
                            secondaryButton: .cancel()
                        )
                    }
                    ScanButtonView() //button to begin scanning
                }//Vstack
                .padding([.leading, .trailing], 20)
              }//Zstack
            } //NavigationStack
        }
        
        // Functions to delete a saved device
        
        // Save the context after deleting the device
    }
    
    
    //compares the serial number that we are trying to connect to with the serial number that we have stored
    private func signalConnect(device: DeviceViewItem, manager: CentralManager) {
        print("Tapped " + device.sn!)
        manager.connectBySN(sn: device.sn ?? "Serial1234")
    }
    
    private func buildDeviceListView(savedDevice: FetchedResults<Device>, discoveredDevice: [CBPeripheral] ) -> Array<DeviceViewItem> {
        var savedArray = Array<DeviceViewItem>()
        var discoveredArray = Array<DeviceViewItem>()
        
        // Start by adding all devices from CoreData with discovered = false
        for device in savedDevice {
            var item = DeviceViewItem(discovered: false)
            item.sn = device.serialNumber
            item.uuid = device.uuid
            item.name = device.name
            savedArray.append(item)
        }
        
        // Parse through every discovered device
        for device in discoveredDevice {
            var item = DeviceViewItem(discovered: false)
          //  var item = DeviceViewItem(discovered: true, isConnected: centralManager.isConnected(device.identifier.uuidString)) 🟡🟣
            let sn = CentralManager.buildSn(uuid: device.identifier.uuidString, name: device.name)
            
            // Find out if we've connected to this device before
            // Check if device is present in array
            if let matchIndex = savedArray.firstIndex(where: { $0.sn == sn }) {
                savedArray[matchIndex].discovered = true
            }
            else {
                // We've never saved or connected to this device before, therefore add it to the list.
                item.sn = sn
                item.uuid = device.identifier.uuidString
                item.discovered = true
                item.name = device.name
                discoveredArray.append(item)
            }
        }
        // Update the shared savedArray
        DeviceDataManager.shared.savedArray = savedArray
        
        return savedArray + discoveredArray  //MAKE savedArray value public and accessible to all.
    }
ios swift swiftui mobile
1个回答
0
投票

即使您可以将

.simultaneousGesture
NavigationLink
一起使用(我认为这不起作用),您也会遇到两个独立且同时操作的逻辑问题:导航和连接。

因此,这里的问题不仅仅是布局或 UI 问题,而是一个更根本的问题:导航应该依赖于连接状态,而不是并行发生。

解决方案是使用编程导航来控制导航的条件。您将如何导航(如果有的话)取决于您的具体用例。

这是一个使用编程导航的简化工作示例,其中单击设备会触发一个模拟函数,该函数返回连接成功布尔值(基于奇数/偶数设备 ID 号):

  • 如果连接“成功”,则会触发导航。
  • 如果没有,则会显示错误并且不会进行导航。
import SwiftUI

//Routes for programatic navigation
enum NavigateConnectRoute: Hashable {
    case home, devices
}

//Observable singleton
@Observable
class DeviceDataManager {
    var deviceID: Int?
    static let shared = DeviceDataManager() // Singleton instance
    private init() {}
}

//Main view
struct NavigateConnect: View {
    
    //State values
    @State private var path = NavigationPath()
    @State private var showError: Bool = false
    @State private var errorDeviceID: Int?
    
    //Bindings
    @Bindable var deviceManager: DeviceDataManager = .shared
    
    //Body
    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                
                Text("Select a device:")
                    .fontWeight(.bold)
                
                List {
                    ForEach(1...10, id: \.self) { id in
                        Button {
                            
                            //Simulate connection to a device (odd numbers fail, even numbers succeed)
                            let connected = connect(deviceID: id, status: id % 2 == 0 ? true : false)
                            
                            if connected {
                                //Display console message
                                print("Connection successful for device \(id)")
                                
                                //Navigate to Home view programmatically
                                path.append(NavigateConnectRoute.home)
                            }
                            else {
                                //Show error
                                errorDeviceID = id
                            }
                        } label : {
                            HStack {
                                Text("Device \(id)")
                                    .foregroundStyle(errorDeviceID == id ? .red : .primary)
                                Spacer()
                                Image(systemName: "chevron.right")
                                    .foregroundStyle(.secondary)
                            }
                        }
                        .listRowBackground(Color.clear)
                        .tint(.black)
                    }
                }
                .scrollContentBackground(.hidden)
                .background(Color.clear)
                
                Spacer()
            }
            .onChange(of: errorDeviceID) {
                if errorDeviceID != nil {
                    showError = true
                }
            }
            .alert(
                "Connection Error",
                isPresented: $showError,
                presenting: errorDeviceID
            ) { id in
                Button("OK", role: .cancel) {
                    print("Showed error alert for device \(id)")
                }
            } message: { id in
                Text("Unable to connect to device \(id)")
            }
            .padding(.top, 50)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            .background{
                Color.yellow
                    .ignoresSafeArea()
            }
            .navigationTitle("Connect")
            .navigationDestination(for: NavigateConnectRoute.self) { route in
                switch route {
                    case .home: NavigateConnectHome()
                    case .devices: NavigateConnect()
                }
            }
        }
        .tint(.black)
    }
    
    //Helper function to simulate connecting to a device and returning a connection status
    private func connect(deviceID: Int, status: Bool) -> Bool {
        
        //Display console message
        print("Connection to device \(deviceID) was \(status ? "successful" : "unsuccessful")")

        //Set singleton observer value with the device id
        deviceManager.deviceID = status ? deviceID : nil
        
        //Return simulated status
        return status
    }
}

//Device home view
struct NavigateConnectHome: View {
    
    //Binding to singleton observable
    @Bindable var deviceManager: DeviceDataManager = .shared

    //Body
    var body: some View {
        VStack {
            Text("This is home view for device \(deviceManager.deviceID ?? -1) ")
        }
        .navigationTitle("Home")
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
        .background{
            Color.yellow
                .ignoresSafeArea()
        }
        .onDisappear {
            //Reset device id when leaving home view
            deviceManager.deviceID = nil
        }
    }
}

//Preview
#Preview {
    NavigateConnect()
}

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.