ios – Methods to outline onTapGesture with debounce in SwiftUI utilizing PassThroughSubject less complicated manner than modifier, ViewModifier and Mannequin inside?

0
1
ios – Methods to outline onTapGesture with debounce in SwiftUI utilizing PassThroughSubject less complicated manner than modifier, ViewModifier and Mannequin inside?


A debounce operation will be applied by ready inside a job(id:). Change the id parameter in onTapGesture to begin a brand new job. If one other faucet happens in the course of the Job.sleep name, the duty can be cancelled and so Job.sleep will throw an error.

struct DebounceTapModifier: ViewModifier {
    non-public let motion: () -> Void
    non-public let debounce: CGFloat
    
    @State non-public var taskTrigger: Bool?
    
    init(motion: @escaping () -> Void, debounce: CGFloat) {
        self.motion = motion
        self.debounce = debounce
    }
    
    func physique(content material: Content material) -> some View {
        content material
            .onTapGesture {
                taskTrigger = !(taskTrigger ?? false)
            }
            .job(id: taskTrigger) {
                guard taskTrigger != nil { return } // the consumer has not tapped but
                do {
                    strive await Job.sleep(for: .seconds(debounce))
                    // if Job.sleep throws an error, motion is not going to be referred to as
                    motion()
                } catch {
                    // one other faucet occurred in the course of the debounce period
                }
            }
    }
}

As on your Mix method, the lifetime of DebounceViewModel shouldn’t be being managed by SwiftUI. I might make it an ObservableObject and put it in a @StateObject, in order that its lifetime is identical because the view modifier.

class Debouncer: ObservableObject {
    var cancellable: (any Cancellable)?
    let topic = PassthroughSubject()
    
    deinit {
        print("deinit")
    }
}

struct DebounceTapModifier: ViewModifier {
    non-public let motion: () -> Void
    non-public let debounce: CGFloat
    non-public var debouncer = Debouncer()
    
    init(motion: @escaping () -> Void, debounce: CGFloat) {
        self.motion = motion
        self.debounce = debounce
    }
    
    func physique(content material: Content material) -> some View {
        content material
            .onTapGesture {
                debouncer.topic.ship(0)
            }
            .onChange(of: debounce) {
                // redo the writer subscription if the debounce time has modified
                // if debounce is all the time the identical then you'll be able to simply do that in onAppear
                setupActions()

                 // *don't* name setupActions within the initialiser. The StateObject has not but been "put in" at the moment.
            }
    }
    
    non-public func setupActions() {
        debouncer.cancellable = debouncer.topic
            .debounce(for: .seconds(debounce), scheduler: DispatchQueue.principal)
            .sink { _ in
                motion()
            }
    }
}

LEAVE A REPLY

Please enter your comment!
Please enter your name here