3

I have a button in my UICollectionViewCell.swift: I want, based on certain parameters for it to be able to present an alert.

class CollectionViewCell: UICollectionViewCell {
     var collectionView: CollectionView!

 @IBAction func buttonPressed(sender: UIButton) {

    doThisOrThat()
}


func doThisOrThat() {
    if x == .NotDetermined {
        y.doSomething()
    } else if x == .Denied || x == .Restricted {
            showAlert("Error", theMessage: "there's an error here")
            return
        }
    }

func showAlert(title: String, theMessage: String) {
    let alert = UIAlertController(title: title, message: theMessage, preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
    self.collectionView!.presentViewController(alert, animated: true, completion: nil)
}

On the self.collectionView!.presentViewController line I get a break:

fatal error: unexpectedly found nil while unwrapping an Optional value

I'm guessing that this has someting to do with how CollectionView is being used - I don't totally understand optionals yet. I know that a UICollectionCell can't .presentViewController - which is why I'm trying to get UICOllectionView to do it.

How do I make this work? I've thought of using an extension but don't know how to make UICOllectionViewCell adopt .presentViewController

Any ideas?

SamYoungNY
  • 6,444
  • 6
  • 26
  • 43

3 Answers3

7

The collection view cell should not depend on knowledge of its parent view or view controller in order to maintain a functional separation of responsibilities that is part of a proper app architecture.

Therefore, to get the cell to adopt the behavior of showing an alert, the delegation pattern can be used. This is done by adding a protocol to the view controller that has the collection view.

@protocol CollectionViewCellDelegate: class {    
    func showAlert()    
}

And having the view controller conform to that protocol:

class MyViewController: UIViewController, CollectionViewCellDelegate {

Also add a delegate property to the cell:

    weak var delegate: CollectionViewCellDelegate?

Move the showAlert() function inside your collection view controller.

When you make a cell, assign the delegate for the cell to the collection view controller.

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

    // Other cell code here.

    cell.delegate = self

When it is time to show the alert, have the cell call

delegate.showAlert()

The alert will then be presented on the collection view controller because it has been set as the delegate of the collection view cell.

Daniel Zhang
  • 5,778
  • 2
  • 23
  • 28
  • hi @Daniel Z - thanks for your explanation. I have added `@protocol...` directly under the `CollectionView`'s `import` statement. Then I added the `CollectionViewDelegate` to the class `CollectionViewCell`... added the delegate property to `CollectionViewCell` (would not allow me to make it weak). Moved `showAlert` to the view controller. Assigned delegate in `cellForItem`, and called the alert back in the cell with the `delegate`... – SamYoungNY Nov 19 '15 at 15:17
  • I get an error "Type 'CollectionViewCell' does not conform to protocol CollectionViewDelegate" opening this up says... "Protocol requires function 'showAert(0' with type '() -> ()' ... – SamYoungNY Nov 19 '15 at 15:17
  • I made a couple of corrections to my answer. The protocol needs to have the class annotation to have the delegate property be weak. Also, it is your view controller that will conform to the protocol thereby providing the showAlert functionality that the cell will use. I was wrong about having the cell conform to the protocol. – Daniel Zhang Nov 19 '15 at 21:25
  • Upvote for good design practice using delegate/protocol rather than optional chaining into oblivion – sanch Mar 26 '18 at 14:36
  • 1
    Benefit of protocol oriented programming – Coder ACJHP Mar 03 '19 at 23:33
3

With me, it happens a little difference in present method:

self.window?.rootViewController?.present(alert, animated: true, completion: nil)
Sebastian Lenartowicz
  • 4,695
  • 4
  • 28
  • 39
2
self.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil)

You can use the parent controller's class. Please upvote the other post. (Taken from How to present an AlertView from a UICollectionViewCell)

Westy92
  • 19,087
  • 4
  • 72
  • 54
vee
  • 680
  • 1
  • 10
  • 27