0

I have a question that is very similar to the one found here, only I'm coding in Swift 2.0 (their question/answer is Objective-C), and my case is slightly different.

I have a UICollectionView that is essentially a contact list that pulls from core data. When the user selects a person (or an item within the UICollectionView), I want to present a detail view of the contact. I have that view/segue created and hooked up within the Storyboard, but I'm having trouble passing the selected item to the detail view ViewController so that it knows what data to query from core data.

Here is a snippet of my code with descriptions on each:

First, on my "FamilyCollectionViewController" I have the following viewDidLoad method:

override func viewDidLoad() {
    super.viewDidLoad()

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let context:NSManagedObjectContext = appDelegate.managedObjectContext
    let fetchRequest = NSFetchRequest(entityName: "Family")
    fetchRequest.returnsObjectsAsFaults = false

    do {
        let results = try context.executeFetchRequest(fetchRequest)
        userNames = results as! [NSManagedObject]
    } catch let error as NSError {
        print("Could not fetch \(error), \(error.userInfo)")
    }
}

Here is the cellForItemAtIndexPath method from the same view controller:

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! FamilyCollectionViewCell

    let person = userNames[indexPath.row]
    cell.familyName!.text = person.valueForKey("name") as? String
    print(userNames)

    return cell
}

And here is the current didSelectItemAtIndexPath method (this may be where my problem is at, in combination with the prepareForSegue method):

override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    let selectedPerson = userNames[indexPath.row]

    print(selectedPerson)

    let selectedName = selectedPerson.valueForKey("name") as? String
    let selectedNumber = selectedPerson.valueForKey("phone") as? String
    let selectedEmail = selectedPerson.valueForKey("email") as? String

    print(selectedName)

}

I attempted to create something similar to the answer as provided in the aforementioned linked question, but it is so laden with errors its not useful at all (the way I created it that is). I've passed data before from other views (using the prepareForSegue method), but the nuance of it now being from a UICollectionView and more, using core data, I'm left stumped. Any support is greatly appreciated.

Community
  • 1
  • 1
skind
  • 173
  • 1
  • 2
  • 17
  • So your problem is passing the selected `userName` to the detail view controller? Where is your `collectionView(_:didSelectItemAtIndexPath:)` implementation? – paulvs Feb 21 '16 at 20:28
  • Hey @paulvs, I've added it to the question. I don't know that it calls everything that I need. The two print methods are simply to see if it was pulling anything it. They both don't print anything... – skind Feb 21 '16 at 20:38
  • If your delegate method is not called, have you set the `UICollectionView` delegate to self? – paulvs Feb 21 '16 at 20:44
  • I haven't, could that be the problem? What's the impact of that? – skind Feb 21 '16 at 20:50
  • Yes, without it, the `UICollectionView` doesn't know that your view controller subclass (i.e. self) is the class it should notify (via the `collectionView(_:didSelectItemAtIndexPath:)` method), that an item was selected. – paulvs Feb 21 '16 at 20:56
  • 1
    Okay that helps a little bit. It now will print the "selectedName" from the didSelectItemAtIndexPath method. From there, do I simply pass that variable to the prepareForSegue method and pass it to the new viewController? – skind Feb 21 '16 at 21:03
  • That's correct, you can pass it via a class-level global variable I suppose. – paulvs Feb 21 '16 at 21:09
  • Don't want to drag this into a whole new question/issue, but now I'm having trouble passing it. It looks like it runs the prepareForSegue method before the didSelectItemAtIndexPath method. I have print commands for the same variable out of both and it prints the name out of my didSelectItemAtIndexPath, but prints "blank" (the original designation set for the variable) when I print it there. – skind Feb 21 '16 at 21:25
  • That's happening because your segue is connected from the `UICollectionViewCell` prototype cell in the first view controller to the second view controller. This is wrong because it is conflicting with the `performSegueWithIdentifier(_:, sender)` you do in code. The correct solution is to attach the segue from the first view controller itself (not the cell) to the second view controller, and make sure it has a segue identifier. Then in the delegate method, set the selected cell index, and use `performSegueWithIdentifier(_:, sender)` to perform the segue. See my example below. – paulvs Feb 21 '16 at 22:07
  • Gotcha, that was exactly what was wrong. Impressive, considering I didn't have a screenshot of my storyboard but you still detected the issue. Thanks a ton, @paulvs! – skind Feb 21 '16 at 22:54

1 Answers1

2

Here's a complete example.

enter image description here

The contents of ViewController.swift

class ViewController: UICollectionViewController
{

    var selectedIndex: Int!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }


    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 100
    }

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ReuseID", forIndexPath: indexPath) as! CellCollectionViewCell
        cell.contentView.backgroundColor = UIColor.whiteColor()
        cell.label.text = "\(indexPath.row)"
        return cell
    }

    override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        selectedIndex = indexPath.item
        performSegueWithIdentifier("OtherSegueID", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "OtherSegueID" {
            let otherViewController = segue.destinationViewController as! OtherViewController
            otherViewController.selectedIndex = selectedIndex
        }
    }
}

The contents of CellCollectionViewCell.swift

class CellCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var label: UILabel!
}

The contents of OtherViewController.swift

class OtherViewController: UIViewController {

    var selectedIndex: Int!

    override func viewDidLoad() {
        super.viewDidLoad()

        title = String(selectedIndex)
    }
}
paulvs
  • 11,963
  • 3
  • 41
  • 66
  • 1
    You are simply awesome. I got the answer from your comment alone, but this answer only makes it clearer. Thanks a ton @paulvs, you are a huge help. – skind Feb 21 '16 at 22:53
  • No worries @skind, glad it helped! – paulvs Feb 21 '16 at 22:58