0

I want to dismiss an UIAlertView after a couple of seconds (without need of an 'ok' button).

After some studying I found that I could use dispatch_after as the delay to eventually dismiss the alert:

func delay(delay:Double, closure:()->()) {  
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))  // ...Int64.init() struct in Swift.
        ),
        dispatch_get_main_queue(), closure)
}

Source: Using dispatch_after vs NSTimer

The above code works fine, but I would like to edit the closure to allow passage of a sender:UIViewController parameter. This is where I'm getting confused.

The dispatch_after() format is:

func dispatch_after(_ when: dispatch_time_t, _ queue: dispatch_queue_t, _ block: dispatch_block_t)

Here is what I tried (which is WRONG):

func delay(delay:Double, closure:(sender:UIViewController)->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))  // ...Int64.init() struct in Swift.
        ),
        dispatch_get_main_queue(), closure:(sender:UIViewController) in {

        })
}

Question: how to I fix this to have a block/closure parameter containing a callback reference ('sender:UIViewController')?

Community
  • 1
  • 1
Frederick C. Lee
  • 9,019
  • 17
  • 64
  • 105

2 Answers2

1

You do do not need to "pass" sender to the closure. You only need to reference sender inside the closure.

This is known as capture, and it is one of the useful things about closures. The closure will capture a reference to sender and take it with it. It's like magic.

So write the closure somewhere where sender is in scope. Reference sender inside the closure. Then, pass the closure to delay and everything should work fine:

func closeAlert(sender: UIVewController) {

    let closure: () -> () = { 
        doSomethingWith(sender)
    }

    delay(3, closure: closure)
}

Again, the secret is that the closure captures a reference to sender that gets carried along with it when the closure gets passed to delay.

Aaron Rasmussen
  • 13,082
  • 3
  • 42
  • 43
0

The following (abridged) code displays my Alert Dialog (without buttons) for a few seconds and disappears.

I was thinking of using NSTimer but learned that was unwise.
Using Swift + dispatch_after() is truly the way to go.

showAlert(sender: self, withTitle: "No Blisses", withMessage: "You don't have any Blisses to show.", alertPurpose: .timed)

enum AlertPurpose:Int {
    case none = 0  // ...default: no alert button.
    case simple // ...generic OK response.
    case timed
    case startBliss
    case noVideo
    case createdHashtag
    case missingProfileImage
    case profileSaved
    case settings
}

func closeAlert(sender: UIViewController) {
    let closure: () -> () = {
        sender.dismissViewControllerAnimated(true, completion: nil)
    }
    delay(3, closure)
}

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}
...
...
...
} else if alertPurpose == .timed {
        closeAlert(sender as! UIViewController)
Frederick C. Lee
  • 9,019
  • 17
  • 64
  • 105