Невозможно отменить DispatchWorkItem в представлении SwiftUI
Я работаю над модификатором представления, представляющим представление о текущем содержимом. Это представление должно быть закрыто, если пользователь нажимает на него, или автоматически по прошествии определенного времени. Я пытаюсь сделать это с помощью
struct Modifier<P, V>: ViewModifier where P: Publisher, V: View, P.Failure == Never {
private let publisher: P
private let autodismissAfter: TimeInterval
private let view: () -> V
@State private var presenting: V?
init(publisher: P, autodismissAfter: TimeInterval, present view: @escaping () -> V) {
self.publisher = publisher
self.autodismissAfter = autodismissAfter
self.view = view
}
private var workItem: DispatchWorkItem { DispatchWorkItem { presenting = nil } }
func body(content: Content) -> some View {
ZStack {
content
if let presenting = presenting {
presenting.onTapGesture {
workItem.cancel()
self.presenting = nil
}
}
}
.onReceive(publisher) { _ in
if presenting != nil {
dispatchWork.cancel()
presenting = nil
}
presenting = view
DispatchQueue.main.asyncAfter(deadline: .now() + autodismissAfter, execute: work)
}
}
}
Однако рабочий элемент по-прежнему выполняется, когда наступает крайний срок, даже если он явно отменен. Может ли кто-нибудь помочь мне решить эту проблему? Может ли проблема быть связана с тем, что мне понадобится ссылка на рабочий элемент, а это не так, поскольку модификатор является структурой?
1 ответ
Вы не использовали State или привязку в своем ViewModifier, которые вам точно нужны! также вам НЕ нужно передавать Publisher в ViewModifier! потому что они есть и доступны где угодно!
import SwiftUI
struct ContentView: View {
@StateObject var customModel: CustomModel = CustomModel.shared
var body: some View {
VStack {
Spacer()
Text("Hello, world!")
.presenter(model: customModel) { presentationView }
Spacer()
Button("Present with StateObject") { customModel.isPresented.toggle() }.padding()
Spacer()
}
}
var presentationView: some View {
return Text("Presentation View").bold().fixedSize().padding().background(Color.red).cornerRadius(10)
}
}
class CustomModel: ObservableObject {
static let shared: CustomModel = CustomModel()
@Published var isPresented: Bool = Bool()
}
struct CustomPresenterViewModifier<InputContent: View>: ViewModifier {
@ObservedObject var model: CustomModel
var inputContent: () -> InputContent
private let dismissTimer: Int = 5
@State private var currentDismissId: DispatchTime = DispatchTime.now()
func body(content: Content) -> some View {
return content
.onTapGesture { model.isPresented = true }
.overlay(model.isPresented ? inputContent().onAppear() { dismiss(value: model.isPresented) }.onTapGesture { model.isPresented.toggle() } : nil)
.onChange(of: model.isPresented) { newValue in dismiss(value: newValue); CustomModel.shared.isPresented = newValue }
}
func dismiss(value: Bool) {
currentDismissId = DispatchTime.now() + .seconds(dismissTimer)
if value {
let dismissId: DispatchTime = currentDismissId
DispatchQueue.main.asyncAfter(deadline: dismissId) {
if (dismissId == currentDismissId) { model.isPresented = false }
}
}
}
}
extension View {
func presenter<InputContent: View>(model: CustomModel, inputContent: @escaping () -> InputContent) -> some View {
return self.modifier(CustomPresenterViewModifier(model: model, inputContent: inputContent))
}
}