关闭 SwiftUI 视图时如何发送最后一条消息并关闭套接字?

How to send a last message and close the socket when a SwiftUI view is dismissed?

我有一个 SwiftUI sheet 视图和一个 ObservedObject 处理 tcp 通信,当这个 sheet被解雇 我需要它发送最后一条 tcp 消息,然后关闭套接字。 onDisappear 事件似乎永远不会被触发(编辑:找出罪魁祸首是因为我使用 UIHostingController 呈现 sheet ,仍然需要一个解决方案)我试过将它放在表单、导航视图中,尝试为其创建一个新堆栈,但没有任何效果。所以我尝试使用我的 ObservedObject deinit 但是如果我在关闭视图后尝试快速重新打开视图,这会给我一个错误的访问错误。

deinit {
    let msg = getUpdatedTimersString()
    self.connection.sendMsg(msg, success: connection.close)
}

来自我的连接 class 使用 Network Framework

func sendMsg(_ message: String, success: @escaping () -> Void = { }, error: @escaping () -> Void = { }) {
        let msg = message + "\r\n"
        let data: Data? = msg.data(using: .utf8)
        debugPrint("Sending: \(msg)")
        connection.send(content: data, completion: .contentProcessed { (sendError) in
            if let sendError = sendError {
                self.debug("\(sendError)")
                error()
            } else {
                success()
            }
        })
}

func close() {
        connection.cancel()
}

编辑:添加下面的视图代码

struct ScheduleView: View {
    @ObservedObject var scheduleManager = ScheduleManager() // This handles the tcp communication, the deinit you see above is from this

    var body: some View {
        NavigationView {
            Form {
                ForEach(scheduleManager.timers) { timer in
                    ScheduleForm(scheduleManager: self.scheduleManager, timer: timer).onDisappear { debugPrint("schedule form row disappeared") } // This is just a view that adds a section header and a DatePicker to the form for each timer
                }
            }.onDisappear { debugPrint("form disappeared") }

            .navigationBarTitle(Text("Schedule"), displayMode: .inline)
        }.onDisappear() { debugPrint("nav disappeared") }
    }
}

None 这些 onDisappear 对我有用, ScheduleForm 行中的那个是唯一一个甚至触发对我来说,但它会在创建 sheet 时触发,并且每次我滚动一行看不见时触发,但不会在我关闭 sheet.

时触发

我做的第一件事是更改您的函数签名以使用单个闭包来完成成功和错误。

func sendMsg(_ message: String, completion: ((Bool) ->())? = nil)

然后我们可以在 onDisappear 修饰符中使用它。完成后关闭连接。

struct ContentView: View {

  var body: some View {

    Text("Hello World").onDisappear {
      sendMsg("last msg", completion: { completed in
        close()
      })
    }

  }
}

我经常做的事情是这样的:

struct ScheduleView: View {

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

@ObservedObject var scheduleManager = ScheduleManager() 

var body: some View {
    NavigationView {
        Form {
            ForEach(scheduleManager.timers) { timer in
                ScheduleForm(scheduleManager: self.scheduleManager, timer: timer)
            }
        }
    }.onDisappear(perform: doExit)
     .navigationBarTitle(Text("Schedule"), displayMode: .inline)
}

func doExit() {

  // do your closing etc ... here

  // then to go back to the previous view
  self.presentationMode.wrappedValue.dismiss()
}

}

我有这个非常粗略的测试工作。 出现 sheet 滑动时触发 onDisappear 并执行 doExit 函数。对 UIHostingController 一无所知。

struct ContentView: View {
@State var testArray = ["one", "two", "three"]
@State var showMe = false

var body: some View {
    Button(action: { self.showMe = true}) {
        Text("Press button")
    }.sheet(isPresented: $showMe, onDismiss: {self.showMe = false}) { TestView(scheduleManager: self.testArray) }
}
}

struct TestView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State var scheduleManager: [String]

var body: some View {
    NavigationView {
        Form {
            ForEach(scheduleManager, id: \.self) { timer in
                Text(timer)
            }
        }
    }.onDisappear(perform: doExit)
}

func doExit() {
    print("-----> TestView doExit")
    // then to go back to the previous view
    self.presentationMode.wrappedValue.dismiss()
}
}

解决方案:

final class ScheduleController: UIHostingController<ScheduleView> {
    required init?(coder: NSCoder) {
        super.init(coder: coder, rootView: ScheduleView())
    }

    init() {
        super.init(rootView: ScheduleView())
    }
    override func viewWillDisappear(_ animated: Bool) {
        rootView.scheduleManager.updateTimers() // this sends the last message
    }

    override func viewDidDisappear(_ animated: Bool) {
        rootView.scheduleManager.connection.close // this closes the connection
    }
}