What is the best practice for wiring up a table view cell's subview to a storyboard segue with popover presentation style? Such that the popover anchor points at that subview?
I am trying to solve multiple problems that I previously avoided through a lot of blechy pre-iOS 6 programmatic code (subview tracks touches, tells controller to show popover manually), that I want the storyboard to solve, and I keep coming up empty.
Problem 1: I want to specify a UITapGestureRecognizer on the cell's subview from the storyboard to replace the touch tracking code, but if I create one in the storyboard and drop it onto the subview in the storyboard, the gesture only appears to work for the first instance of the cell. If I manually create a UITapGestureRecognizer for the subview each time the cell is being configured, then it works on each instance of the cell.
Problem 2: I want the UITapGestureRecognizer to trigger the popover segue pointing at the subview, to replace telling the controller code that was manually to show a popover. Because the gesture recognizer is not in the storyboard, I have to have the cell send a notification to the view's controller, so that it can then perform the segue, at which time I've lost a reference to the subview unless I thread it through the notification (I make it the object, so it can be the source of the segue). So I feel like I just re-engineered the old hack using segues, but I'm OK with that.
Problem 3: I don't know of a way to change the anchor for the popover presentation controller (I've tried casting, querying and the like on the destination controller, but I see no popover controller for the segue), so it doesn't point to the right view yet, which makes this solution worse off than without storyboards. I'd really like it to point at the subview, like it used to with the blechy programmatic code.
Have I overlooked something?
I think I have partial answers to my questions; it still feels icky, but it's in the general direction of leveraging storyboards:
1. Stick with the workaround to Problem 1: when configuring the cell, create a tap gesture recognizer on the subview, replacing existing recognizers:
if mySubview.gestureRecognizers != nil {
mySubview.gestureRecognizers = nil
}
mySubview.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleTap:"))I wish I could create the gesture for the cell subview from the storyboard, but I just could not find a way that works. In my experiements, the storyboard gesture wouldn't perform a segue or call an IBAction..
2. Modify the solution to Problem 2: Provide a method by which the cell obtains a weak reference to the view controller when configuring the cell. When handling the tap from the gesture recognizer, perform the segue on the view controller, with the subview as the sender. I decided that, maybe a pair of protocols might be useful, so that the cell doesn't know it's really referencing a view controller, and so the view controller doesn't really know the cell view subclass, but this is probably overkill (I could reduce the number of protocols by one if I just let the cell view call the view controller's methods directly):
@objc public protocol ItemDetailDelegate {
func showItemDetail(detailIdentifier: String, anchorView: UIView)
}
@objc public protocol ItemDetail {
weak var detailSourceViewController: ItemDetailDelegate? { get set }
}The table view cell subclass calls showItemDetail when tapped, passing the subview to be used as the anchor for the popoverl:
weak var detailSourceViewController: ItemDetailDelegate?
func handleTap(source: UITapGestureRecognizer) {
detailSourceViewController?.showItemDetail("itemDetail", anchorView: mySubview)
}The view controller stitches itself to the cell in cellForRowAtIndexPath (or in a configure method if you have one):
if let itemDetail = cell as? ItemDetail {
itemDetail.detailSourceViewController = self
}And the view controller implements showItemDetail to perform the segue:
func showItemDetail(detailIdentifier: String, anchorView: UIView) {
self.performSegueWithIdentifier(detailIdentifier, sender: anchorView)
}3. New solution for Problem 3: In the view controller's prepareForSegue, for the segue identifier that matches, check if the segue's destination controller's presentation controller is a UIPopoverPresentationController. If it is, set its sourceView to the subview that got passed in as the sender for the segue.
if let popover = segue.destinationViewController.presentationController as? UIPopoverPresentationController {
popover.sourceView = sender as? UIView
}If there are other, cleaner ways to solve these problems, let me know what you find.