10

I have a UIViewController and a UIView inside it. When I try to add an alert inside the UIView, I have to use the controller to present the UIAlertController. How do I pass the reference of UIViewController to the UIView class? Or alternatively how do I create a delegate of controller?

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: "Ok", style: UIAlertActionStyle.Cancel, handler: {(action: UIAlertAction!) in
                println("Game Over")
            }))
            }))
            // Following would fail because self is not a UIViewController here
            // self.presentViewController(alert, animated: true, completion: nil)
        }
    }
}
Bing
  • 181
  • 1
  • 3
  • 12
  • Did you see this answer: http://stackoverflow.com/a/3732812/2468186? – Moon Cat Jul 06 '14 at 09:22
  • This looks identical to your previous question [How to create an alert in a subview class in Swift?](http://stackoverflow.com/questions/24584364/how-to-create-an-alert-in-a-subview-class-in-swift) (for which you accepted an answer). – Martin R Jul 06 '14 at 09:26
  • 1
    @MartinR I post this question to for a more generic problem which is how to reference parent controller from sub UIView. It could be helpful for the others who are looking for such topic – Bing Jul 06 '14 at 10:08

2 Answers2

19

Following the MVC pattern, a ViewController knows about its Views, but the View shouldn't know about the ViewController. Instead you should declare delegate protocol for GameBoardUIView that your ViewController adopts as follows:

// Delegate protocol declared here
protocol GameBoardUIViewDelegate: class {
    func checkIfNextMoveExistsForGameBoardUIView(gameBoardUIView: GameBoardUIView)
}

class GameBoardUIView: UIView {

    // GameBoardUIView has a delegate property that conforms to the protocol
    // weak to prevent retain cycles
    weak var delegate:GameBoardUIViewDelegate?

    func move() {
        if !gameBoard.checkNextMoveExist() {
            delegate?.checkIfNextMoveExistsForGameBoardUIView(gameBoardUIView: self)
        }
    }

}

// View controller subclass adopts the protocol
class GameViewController: UIViewController, GameBoardUIViewDelegate {

    @IBOutlet var gameBoardUIView: GameBoardUIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        gameBoardUIView.delegate = self
    }

    // Delegte protocol method
    func checkIfNextMoveExistsForGameBoardUIView(gameBoardUIView: GameBoardUIView) {

        let alert = UIAlertController(title: "Game Over", message: nil, preferredStyle: .alert)

        alert.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: {(action: UIAlertAction!) in
            print("Game Over")
        }))

        // If you need to feed back to the game view you can do it in the completion block here
        present(alert, animated: true, completion: nil)
    }
}
Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
  • Thanks for the link. I have two questions regarding your code. First is where do we assign the delegate value? It is nil by default, right? Second question is, I noticed you replace `@IBOutlet var gameBoardUIView: GameBoardUIView` with `let gameView = GameBoardUIView() `, how does this work? – Bing Jul 06 '14 at 10:05
  • Makes sense now. Except that the weak reference doesn't work. I will get an EXC_BAD_ACCESS exception at `self.delegate?.checkIfNextMoveExistsForGameBoardUIView(gameBoardUIView: self)`. And if I use normal reference, it works with alert popping out successfully. – Bing Jul 06 '14 at 18:44
  • Thanks for this, worked great for one view controller, but is there a way to have it working in multiple view controllers without repeating the code? Using AppDelegate? Or an extension of UIViewController? – Amir Aug 27 '15 at 23:58
0

alternatively, you can also post notifications from your .xib and have the parent viewcontroller observes it. you will be able to send data via userInfo object during the posting.

user1019042
  • 2,428
  • 9
  • 43
  • 85