Невозможно отменить 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))

    }

}
Другие вопросы по тегам