一个视图中的多个警报只能在 swiftui 中始终使用最后一个警报

Posted

技术标签:

【中文标题】一个视图中的多个警报只能在 swiftui 中始终使用最后一个警报【英文标题】:Multiple Alerts in one view can only the last alert works always in swiftui 【发布时间】:2019-12-18 21:03:48 【问题描述】:

如果布尔值为真,我有两个警报。

Alert - 1 - 如果蓝牙状态有任何问题而不是开机,就会调用它。这直接从名为 BLE 的 swift 包中调用。代码sn-p如下。

Alert - 2 - 当您想要取消配对外围设备时调用它,为用户提供两个选项。取消配对或保持在同一页面上。

问题: 两个警报似乎都工作正常,但如果它们没有放在同一个视图中。当我将警报放在同一个视图中时,最后显示的警报会按从上到下的顺序调用。

操作系统会读取第一个警报,但只有在调用第二个警报时才会激活它。

如果它们被调用,是否有办法使两个警报都起作用。 我提到了下面的解决方案,但我得到了相同的结果。

Solution 1 和 Solution 2

有2个代码sn-ps

1.主要应用

import SwiftUI
import BLE

struct Dashboard: View 

    @EnvironmentObject var BLE: BLE
    @State private var showUnpairAlert: Bool = false

    private var topLayer: HeatPeripheral 
        self.BLE.peripherals.baseLayer.top
    

    var body: some View 
        VStack(alignment: .center, spacing: 0) 
            // MARK: - Menu Bar
            VStack(alignment: .center, spacing: 4) 
                Button(action: 
                    print("Unpair tapped!")
                    self.showUnpairAlert = true
                ) 
                    HStack 
                        Text("Unpair")
                            .fontWeight(.bold)
                            .font(.body)
                    
                    .frame(minWidth: 85, minHeight: 35)
                    .cornerRadius(30)
                
            

        
        .onAppear(perform: 
            self.BLE.update()
        )

            // Alert 1 - It is called if it meets one of the cases and returns the alert
            // It is presented in the function centralManagerDidUpdateState
            .alert(isPresented: $BLE.showStateAlert, content:  () -> Alert in

                let state = self.BLE.centralManager!.state
                var message = ""

                switch state 
                case .unknown:
                    message = "Bluetooth state is unknown"
                case .resetting:
                    message = "Bluetooth is resetting..."
                case .unsupported:
                    message = "This device doesn't have a bluetooth radio."
                case .unauthorized:
                    message = "Turn On Bluetooth In The Settings App to Allow Battery to Connect to App."
                case .poweredOff:
                    message = "Turn On Bluetooth to Allow Battery to Connect to App."
                    break
                @unknown default:
                    break
                

                return Alert(title: Text("Bluetooth is \(self.BLE.getStateString())"), message: Text(message), dismissButton: .default(Text("OK")))
            )

            // Alert 2 - It is called when you tap the unpair button

            .alert(isPresented: $showUnpairAlert) 
                Alert(title: Text("Unpair from \(checkForDeviceInformation())"), message: Text("*Your peripheral command will stay on."), primaryButton: .destructive(Text("Unpair")) 
                    self.unpairAndSetDefaultDeviceInformation()
                    , secondaryButton: .cancel())
        
    
    func unpairAndSetDefaultDeviceInformation() 
        defaults.set(defaultDeviceinformation, forKey: Keys.deviceInformation)
        disconnectPeripheral()
        print("Pod unpaired and view changed to Onboarding")
        self.presentationMode.wrappedValue.dismiss()
        DispatchQueue.main.async 
            self.activateLink = true
        

    
    func disconnectPeripheral()
        if skiinBLE.peripherals.baseLayer.top.cbPeripheral != nil 
            self.skiinBLE.disconnectPeripheral()
        
    


2。 BLE 包

import SwiftUI
import Combine
import CoreBluetooth

public class BLE: NSObject, ObservableObject 

    public var centralManager: CBCentralManager? = nil
    public let baseLayerServices = "XXXXXXXXXXXXXXX"
    let defaults = UserDefaults.standard
    @Published public var showStateAlert: Bool = false

    public func start() 
        self.centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
        self.centralManager?.delegate = self
    

    public func getStateString() -> String 
        guard let state = self.centralManager?.state else  return String() 
        switch state 
        case .unknown:
            return "Unknown"
        case .resetting:
            return "Resetting"
        case .unsupported:
            return "Unsupported"
        case .unauthorized:
            return "Unauthorized"
        case .poweredOff:
            return "Powered Off"
        case .poweredOn:
            return "Powered On"
        @unknown default:
            return String()
        
    



extension BLE: CBCentralManagerDelegate 

    public func centralManagerDidUpdateState(_ central: CBCentralManager) 
        print("state: \(self.getStateString())")
        if central.state == .poweredOn 
            self.showStateAlert = false

            if let connectedPeripherals =  self.centralManager?.retrieveConnectedPeripherals(withServices: self.baseLayerServices), connectedPeripherals.count > 0 
                print("Already connected: \(connectedPeripherals.map$0.name), self.peripherals: \(self.peripherals)")
                self.centralManager?.stopScan()

            
            else 
                print("scanForPeripherals")
                self.centralManager?.scanForPeripherals(withServices: self.baseLayerServices, options: nil)
            
        
        else 
            self.showStateAlert = true // Alert is called if there is any issue with the state.
        
    

谢谢!!!

【问题讨论】:

【参考方案1】:

要记住的是,视图修饰符实际上并不只是修改视图,它们会返回一个全新的视图。所以第一个alert 修饰符返回一个以第一种方式处理警报的新视图。第二个alert 修饰符返回一个新视图,它以第二种方式修改警报(覆盖第一种方法),这是唯一最终生效的方式。最外层的修饰符才是最重要的。

您可以尝试几件事,首先尝试将不同的警报修饰符附加到两个不同的视图,而不是同一个视图。

其次,您可以尝试alert 的替代形式,它采用可选的 Identifiable 绑定并将其传递给闭包。当 value 为 nil 时,什么都不会发生。当状态变为 nil 以外的状态时,应该会出现警报。

这是一个使用alert(item:) 形式的示例,而不是基于布尔的alert(isPresented:)

enum Selection: Int, Identifiable 
  case a, b, c
  var id: Int  rawValue 



struct MultiAlertView: View 

  @State private var selection: Selection? = nil

  var body: some View 

    HStack 
      Button(action: 
        self.selection = .a
      )  Text("a") 

      Button(action: 
        self.selection = .b
      )  Text("b") 

    .alert(item: $selection)  (s: Selection) -> Alert in
      Alert(title: Text("selection: \(s.rawValue)"))
    
  


【讨论】:

以上是关于一个视图中的多个警报只能在 swiftui 中始终使用最后一个警报的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 中一个接一个地显示多个警报对话框?

如何使用 SwiftUI 连续呈现两个警报视图

SwiftUI 警报操作需要点击两次

SwiftUI 在 NavigationView 中添加个人警报视图,后退按钮不适用于 Xcode 12 iOS14

键盘在 SwiftUI 视图中始终位于顶部

使 ScrollView 中的滚动条在 SwiftUI 中始终可见