DrWhat
DrWhat

Reputation: 2490

Change value / image in view controller by selecting a cell within a collection view within an embedded container

In a detail view controller, I've a 'featureImage' in the top left, and a thin horizontal strip of images below this. The strip of images is an embedded container view managed by a custom CollectionViewController, which shows an array of images. The initial featureImage is the first image in an array of images[0], and the same array is passed to the collection view.

I'd like the featureImage to update to the same image if a cell in the container view is selected / tapped.

I guess I need to call the delegate method didSelectItemAtIndexPath, which will give me the indexPath. Right? But then how do I pass the indexPath, which is already from a delegate, back to the detail view controller.

EDITED - The code shows code overlap and differences between Responder Chain AND delegate approaches. Uncommented in the didSelectItemAtIndex path, the Responder Chain approach works, while the delegate approach does not.

Protocol defined and included at top of DetailViewController (I doesn't seem to matter which file the protocol is in, and is only typed to class to allow the delegate property to be 'weak'). Needed for both approaches.

protocol FeatureImageController: class {
  func featureImageSelected(indexPath: NSIndexPath)
}
class DetailViewController: UIViewController, FeatureImageController {

Delegate property declared in the custom UICollectionViewController class. Only needed for delegate approach.

 weak var delegate: FeatureImageController?

Delegate property initiated in the DetailViewController. Only needed for delegate approach.

    override func viewDidLoad() {
    super.viewDidLoad()

    let photoCollectionVC = PhotoCollectionVC()
    photoCollectionVC.delegate = self as FeatureImageController ... }

The Responder Chain (active) OR the delegate approach (commented out) within the collection view controllers didSelectItemAtIndexPath method.

    override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
    if let imageSelector =  targetForAction("featureImageSelected:", withSender: self) as? FeatureImageController {
        imageSelector.featureImageSelected(indexPath)
    }
//        self.delegate?.featureImageSelected(indexPath)
}

Delegate method in DetailViewController. Needed for both.

func featureImageSelected(indexPath: NSIndexPath) {
    record?.featureImage = record?.images[indexPath.row]
    self.configureView()
}

Upvotes: 0

Views: 273

Answers (1)

Sean G
Sean G

Reputation: 499

The communication of data selection between View Controllers in my experience can best be achieved in two ways- the delegation or responder chain route. Either way the first step would be creating a protocol that your DetailViewController will adhere to. Something like:

protocol FeatureImageController: class {
    func featureImageSelected(image: UIImage)
}

Your DetailViewController would then implement this function and use it to change the 'feature image'. How this is communicated then depends on whether you use delegation or the responder chain.

Delegation If you prefer to use delegation then declare a delegate property on your CollectionViewController like so:

weak var delegate: FeatureImageController?

then in didSelectItemAtIndexPath you would determine the selected image using the provided indexPath and pass it to your delegate:

delegate?.featureImageSelected(selectedImage)

where selectedImage is the image selected from the collection view.

Responder Chain If you decide to use the responder chain then you need not declare a delegate property. Instead you would ask for the first target that responds to your protocol method. So inside didSelectItemAtIndexPath you would say:

if let imageController = targetForAction("featureImageSelected:", withSender: self) as? FeatureImageController {
   imageController.featureImageSelected(selectedImage)
}

Both methods (delegation or responder chain) allow the collection view controller to pass its selection to the detail controller. The delegation route is more common in the Framework but I find as we use containers within containers more often it becomes pretty nasty to properly manage the chain of delegates without an amount of 'coupling' I'm not comfortable with. The responder chain, on the other hand, is already provided by the framework to 'dig' into the hierarchy of controllers to find one willing to handle your action.

Upvotes: 2

Related Questions