How to create an alert in a subview class in Swift?
Asked Answered
H

2

1

I have a view controller which contains a sub-view. And inside the sub-view class, I may need to pop out an alert when some condition is satisfied.

class GameViewController: UIViewController {
    @IBOutlet var gameBoardUIView: GameBoardUIView
    ...
}

class GameBoardUIView: UIView {
    ...
    func move() {
        if !gameBoard.checkNextMoveExist() {
            var alert = UIAlertController(title: "Game Over", message: nil, preferredStyle: UIAlertControllerStyle.Alert)
            alert.addAction(UIAlertAction(title: "Take Me Back", style: UIAlertActionStyle.Cancel, handler: {(action: UIAlertAction!) in
                println("Taking user back to the game without restarting")
            }))
            alert.addAction(UIAlertAction(title: "New Game", style: UIAlertActionStyle.Destructive, handler: {(action: UIAlertAction!) in
                println("Starting a new game")
                self.restartGame()
            }))
            // This is where the question is
            // self.presentViewController(alert, animated: true, completion: nil)
        }
    }
}

As you can see from the code, I cannot call presentViewController function to show the alert because my sub view is not a controller class. Should I somehow create a week reference to the parent controller inside the sub-view? What would be the best practice to implement such reference?

Hakenkreuz answered 5/7, 2014 at 8:5 Comment(1)
Check my answer, I have created custom class which can be used anywhere in the app. By writing single line alert will appear : #24022979Gonium
S
0

there are couple of ways how you can catch a UIViewController in your UIView.

  1. you can make any of your view controllers as a delegate to show an alert;
  2. you can pass a view controller's reference to your view; and
  3. in general you can always grab the rootViewController anywhere in your code.

you need to call the dismissViewControllerAnimated(_: completion:) on the same view controller when you'd like to dismiss your alert later.

thus, I'd do such a quick solution for your case:

func move() {
    if !gameBoard.checkNextMoveExist() {

        let rootViewController: UIViewController = UIApplication.sharedApplication().windows[0].rootViewController

        var alert = UIAlertController(title: "Game Over", message: nil, preferredStyle: UIAlertControllerStyle.Alert)
        alert.addAction(UIAlertAction(title: "Take Me Back", style: UIAlertActionStyle.Cancel, handler: {(action: UIAlertAction!) in
            rootViewController.dismissViewControllerAnimated(true, completion: nil)
            println("Taking user back to the game without restarting")
        }))
        alert.addAction(UIAlertAction(title: "New Game", style: UIAlertActionStyle.Destructive, handler: {(action: UIAlertAction!) in
            rootViewController.dismissViewControllerAnimated(true, completion: nil)
            println("Starting a new game")
            self.restartGame()
        }))
        rootViewController.presentViewController(alert, animated: true, completion: nil)
    }
}
Sevenup answered 5/7, 2014 at 8:22 Comment(12)
Oops. Good to know we can get the rootViewController in such an easy way. But actually my game view is not the first view of this app. So rootViewController is not the controller I'm trying to reference here. Also one more newbie question, why do we need to call dismissViewControllerAnimated function for each alert action?Hakenkreuz
I grabbed the UIWindow object rootViewController... it has to be a UIViewController or a subset of it.Sevenup
I just tried to grab the game view controller by UIApplication.sharedApplication().windows[1].rootViewController. Looks like all the views are added to the windows array. But this method doesn't scale very well when we have several or dozens of views in a single app.Hakenkreuz
I also tried to remove the dismissViewControllerAnimated function calls and the alert still works.I understand we need to cast the rootViewController has to be a UIViewController, but I don't quite understand what dismissViewControllerAnimated does here.Hakenkreuz
I'm not sure your application has more than one UIWindow, that is possible, but very unusal situation, so you have to grab the object at index [0].Sevenup
Could you provide a few lines of sample code for the first two approaches? They sounds exactly something I want to learn from this question! Much appreciate for your help!Hakenkreuz
please try the code, I've posted, first and tell me what you experinced, do not change any index, because you don't need to change the code above. that is a working solution as is.Sevenup
Good to know. Yeah, if we have more than one UIWindow, then it would be more fragile to get reference of some view controller in such way. Guess I'd better only use this approach to get the reference of the root view controller and use the other two methods to reference parent controllers as the best practice.Hakenkreuz
Oh sorry for the confusion. It works when I grab the controller by UIApplication.sharedApplication().windows[1].rootViewController with index 1. It would fail when using index 0 because the controller it returned is not the view of the game but the actual root view of the app.Hakenkreuz
yes, if you have more than one UIVindow that may be not working, but you know your code better, you must know how many UIWindow objects you deal with, and you can always grab the visible one. but, as I said, that is a very rare situation when you need to deal with more than one UIWindow in an iOS app.Sevenup
Makes sense. I think I still need to look into the concept of UIWindow to have a better understanding of what you've said. Thanks for the help. I'll post another question for the other two approaches you listed above since they are not restricted to this topic already. Thanks!Hakenkreuz
dear Donwvoter! thank you for the nice explanation and pointing out the reason why my answer is not good, that really helps me to improve and correct. (damn idiot).Sevenup
M
2

Swift 4

Just present the alert in UIApplication windows in root view controller:

let title = "Some title"
let message = "body message"

let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let action = UIAlertAction(title: "Aceptar", style: .cancel, handler: nil)

alert.addAction(action)

UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
Mullane answered 16/8, 2018 at 14:55 Comment(0)
S
0

there are couple of ways how you can catch a UIViewController in your UIView.

  1. you can make any of your view controllers as a delegate to show an alert;
  2. you can pass a view controller's reference to your view; and
  3. in general you can always grab the rootViewController anywhere in your code.

you need to call the dismissViewControllerAnimated(_: completion:) on the same view controller when you'd like to dismiss your alert later.

thus, I'd do such a quick solution for your case:

func move() {
    if !gameBoard.checkNextMoveExist() {

        let rootViewController: UIViewController = UIApplication.sharedApplication().windows[0].rootViewController

        var alert = UIAlertController(title: "Game Over", message: nil, preferredStyle: UIAlertControllerStyle.Alert)
        alert.addAction(UIAlertAction(title: "Take Me Back", style: UIAlertActionStyle.Cancel, handler: {(action: UIAlertAction!) in
            rootViewController.dismissViewControllerAnimated(true, completion: nil)
            println("Taking user back to the game without restarting")
        }))
        alert.addAction(UIAlertAction(title: "New Game", style: UIAlertActionStyle.Destructive, handler: {(action: UIAlertAction!) in
            rootViewController.dismissViewControllerAnimated(true, completion: nil)
            println("Starting a new game")
            self.restartGame()
        }))
        rootViewController.presentViewController(alert, animated: true, completion: nil)
    }
}
Sevenup answered 5/7, 2014 at 8:22 Comment(12)
Oops. Good to know we can get the rootViewController in such an easy way. But actually my game view is not the first view of this app. So rootViewController is not the controller I'm trying to reference here. Also one more newbie question, why do we need to call dismissViewControllerAnimated function for each alert action?Hakenkreuz
I grabbed the UIWindow object rootViewController... it has to be a UIViewController or a subset of it.Sevenup
I just tried to grab the game view controller by UIApplication.sharedApplication().windows[1].rootViewController. Looks like all the views are added to the windows array. But this method doesn't scale very well when we have several or dozens of views in a single app.Hakenkreuz
I also tried to remove the dismissViewControllerAnimated function calls and the alert still works.I understand we need to cast the rootViewController has to be a UIViewController, but I don't quite understand what dismissViewControllerAnimated does here.Hakenkreuz
I'm not sure your application has more than one UIWindow, that is possible, but very unusal situation, so you have to grab the object at index [0].Sevenup
Could you provide a few lines of sample code for the first two approaches? They sounds exactly something I want to learn from this question! Much appreciate for your help!Hakenkreuz
please try the code, I've posted, first and tell me what you experinced, do not change any index, because you don't need to change the code above. that is a working solution as is.Sevenup
Good to know. Yeah, if we have more than one UIWindow, then it would be more fragile to get reference of some view controller in such way. Guess I'd better only use this approach to get the reference of the root view controller and use the other two methods to reference parent controllers as the best practice.Hakenkreuz
Oh sorry for the confusion. It works when I grab the controller by UIApplication.sharedApplication().windows[1].rootViewController with index 1. It would fail when using index 0 because the controller it returned is not the view of the game but the actual root view of the app.Hakenkreuz
yes, if you have more than one UIVindow that may be not working, but you know your code better, you must know how many UIWindow objects you deal with, and you can always grab the visible one. but, as I said, that is a very rare situation when you need to deal with more than one UIWindow in an iOS app.Sevenup
Makes sense. I think I still need to look into the concept of UIWindow to have a better understanding of what you've said. Thanks for the help. I'll post another question for the other two approaches you listed above since they are not restricted to this topic already. Thanks!Hakenkreuz
dear Donwvoter! thank you for the nice explanation and pointing out the reason why my answer is not good, that really helps me to improve and correct. (damn idiot).Sevenup

© 2022 - 2024 — McMap. All rights reserved.