2

I have a UIControl that calls a function after 0.5 seconds depending on how many times the user presses it. (Eg 1 press calls f1(), 2 presses calls f2(), 3 presses calls f3())

So basically I need to set a timer when a user presses the Control. If the Control is not pressed for 0.5 seconds then create a dialog. I have tried using a DispatchQueue, but when it gets to the point of making the dialog, it takes several seconds. I think it is because it is being called concurrently instead of on the main thread (apologies if poor terminology).

self.operationQueue.cancelAllOperations() //To cancel previous queues
self.mainAsyncQueue = DispatchQueue(label: "bubblePressed" + String(describing: DispatchTime.now()), qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent)

let time = DispatchTime.now()
self.currentTime = time

self.mainAsyncQueue!.asyncAfter(deadline: time + 0.5){
  guard self.currentTime == time else {
    return
  }

  let tempOperation = BlockOperation(block:{
    self.displayDialog()
  })
  self.operationQueue.addOperation(tempOperation)
}

operationQueue and mainAsycQueue are defined in viewDidLoad as

self.currentTime = DispatchTime.now()
self.operationQueue = OperationQueue()

How can I call my function displayDialog() in the main thread so that it loads faster?

A_toaster
  • 1,196
  • 3
  • 22
  • 50
  • 1
    The problem is that you are trying to update the UI on a thread other than the main thread. You don't need any operation queues nor do you need to create that `mainAsyncQueue`. For this code, all you need is the main queue. – Rob Jun 30 '17 at 04:22

3 Answers3

5

Based on the question title, answer is:

let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
   //update UI here
   self.displayDialog()
}

or

DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 
   self.displayDialog()
}
adev
  • 2,052
  • 1
  • 21
  • 33
3

I don't think it needs to be anywhere near that complicated. You can just use a Timer;

class MyClass: UIViewController {

    var tapCount = 0
    var tapTimer: Timer?

    @IBAction tapped(_ sender: Any) {
        if tapCount < 3 {
            tapCount += 1
            tapTimer?.invalidate()
            tapTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { (timer) in
                switch (self.tapCount) {
                    case 1:
                      self.f1()
                    case 2:
                      self.f2()
                    case 3:
                      self.f3()
                    default:
                      // Hmm, shouldn't happen
                }
                self.tapCount = 0
            })
    }
}

The timer will be scheduled on the main queue by default, so there is no need to dispatch anything on the main queue specifically

Paulw11
  • 108,386
  • 14
  • 159
  • 186
0

Use below func, it executes the func in the main thread and no other action will perform during this execution.

DispatchQueue.main.async {
   self.displayDialog()                
}
dahiya_boy
  • 9,298
  • 1
  • 30
  • 51
  • 1
    Yep, but if the OP merely did the initial `asyncAfter` to the main queue like adev suggested, then this inner `async` call is not needed at all. – Rob Jun 30 '17 at 04:29