0

I want to wait until dismiss animation completes but I don't want to use many blocks in my code, so I wrote this function in UIViewController extension (almost like this worked several years ago for me):

func dismissAnimated() {
   var comleted: Bool = false
   self.dismiss(animated: true) {
      comleted = true
   }

   while !comleted {
      RunLoop.current.run(mode: RunLoop.Mode.common, before: Date.distantFuture)
   }
}

so now instead of:

viewController.dismiss(animated: true) {
    // code after completion
}

I was supposed to write:

viewController.dismissAnimated()
// code after completion

But it doesn't dismiss view controller and doesn't enter into completion block.

I tried different RunLoop modes, tried different dates, tried inserting RunLoop.current.run into while condition, it didn't work. Any ideas how to accomplish this?

Edit:

And it worked on iOS 9 or something like this (may be with some code changes, because I can't find my source code). I start RunLoop.current.run to avoid blocking main thread. For instance, if I put completed = true in DispatchQue.main.asyncAfter , it will work, the issue is with dismiss

Paul T.
  • 4,938
  • 7
  • 45
  • 93

3 Answers3

1

I tried again because I was curious and this solution actually works for me:

@objc private func dismissTapped() {

    let dismissalTime = dismissAnimated()
    print("Dismissal took: %ld", abs(dismissalTime))

}

private func dismissAnimated() -> TimeInterval {

    let startDate = Date()

    var completed = false
    self.dismiss(animated: true) {
        completed = true
    }

    while !completed {
        RunLoop.current.run(mode: .default, before: .distantFuture)
    }

    return startDate.timeIntervalSinceNow

}

iOS 12.1.2 | Swift 4.2

Goergisn
  • 617
  • 4
  • 15
  • Thank you, it works, actually it works only when I tap, if I call dismissAnimated() from DispatchQueue.main, it doesn't work (dismiss works but completion never called), but I'll try to figure it out – Paul T. Jan 24 '19 at 12:48
0

It doesn't dismiss because your while !completed loop has stalled the main thread and the UI update happens on the main thread. What is wrong with running whatever code you need to run inside the dismiss completion closure?

self.dismiss(animated: true) {
    runSomeCode()
}
adamfowlerphoto
  • 2,708
  • 1
  • 11
  • 24
  • because of improving archichecture decision I'd better use code without block, anyway the question is how to make this decision to work. And it worked on iOS 9 or something like this (may be with some code changes, because I can't find my source code). I run runLoop to avoid blocking main thread. For instance, if I put in DispatchQue.main.asyncAfter , it will work, the issue is with dismiss – Paul T. Jan 23 '19 at 06:02
0

If it's really just about not using blocks maybe this may be a solution?

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    if self.navigationController?.isBeingDismissed ?? self.isBeingDismissed  {
        print("Dismissal completed...")
    }
}
Goergisn
  • 617
  • 4
  • 15