Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

Swift : how to display an image from a collectionView controller to a detail viewController

Hello

I have a collectionView wich is populated by images from a NSMutableArray of Parse.com PFObject. Each object have an image.

What i want to achieve is to click on a particular picture in the collectionView and display it bigger in a detailViewController. The probleme is i don't know how to pass the good picture.

I used this method :

     override func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
      let userPost: AnyObject  = self.timeLineData.objectAtIndex(indexPath.row)
      let viewController:ImageDetailTableViewController = ImageDetailTableViewController()
      viewController.modalPresentationStyle = UIModalPresentationStyle.Custom
      viewController.transitioningDelegate = self
      viewController.timeLineData = userPost as NSMutableArray
      self.presentViewController(viewController, animated: true, completion: {})
  }

But i have an error :

 Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'

Any idea ?

5 Answers

ok since your detail view controller is just going to display data from a single post, you dont need a Mutable Array property in your detail VC, just a PFObejct property

class ImageDetailTableViewController: UITableViewController {

var photoPassed : UIImageView = UIImageView()

var post: PFObject!

then in your selection method you can pass the selected post to the detail view controller. you could try getting the cell using cellForItemAtIndexPath

override func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
    //get the post
    let userPost: PFObject  = self.timeLineData.objectAtIndex(indexPath.row)
    let viewController:ImageDetailTableViewController = ImageDetailTableViewController()
    viewController.modalPresentationStyle = UIModalPresentationStyle.Custom
    viewController.transitioningDelegate = self

    //set the detail post
    viewController.post = userPost 
    self.presentViewController(viewController, animated: true, completion: {})

what kind of objects are stored in timelineData?

you have this here:

let userPost: AnyObject  = self.timeLineData.objectAtIndex(indexPath.row)

which sets the post, however the type of that object is generic. it would be helpful if we knew what was in there.

then in this line you set a property of the next view controller using the userPost, however if userPost is not an array thats not going to work:

viewController.timeLineData = userPost as NSMutableArray

if timeLineDate does indeed contain mutable array objects then you most likely need to use:

let userPost: NSMutableArray  = self.timeLineData.objectAtIndex(indexPath.row)

This how my collectionView is set up :

   import UIKit



  class UserPostsCollectionViewController: UICollectionViewController, UIViewControllerTransitioningDelegate {



var userPassed : PFUser = PFUser()

var timeLineData:NSMutableArray = NSMutableArray ()
var followedFriends:NSMutableArray = NSMutableArray ()




func loadPost () {

    timeLineData.removeAllObjects()




    var findPosts:PFQuery = PFQuery(className: "UserPost")
    findPosts.whereKey("from", equalTo: userPassed)
    findPosts.cachePolicy = kPFCachePolicyNetworkOnly


    findPosts.orderByDescending("createdAt")

    //run the query




    findPosts.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
        if !(error != nil) {
            //found posts
            for object  in objects  {

                self.timeLineData.addObject(object)


                self.collectionView?.reloadData()
                println("Image post loaded")

            }

        } else {
            // Log details of the failure
            println("error loadind posts ")

        }

    }

}

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

       cell.backgroundColor = UIColor.lightGrayColor()

    let userPost  = self.timeLineData.objectAtIndex(indexPath.row) as PFObject

    //define the imagepost

    if let imagesPost:PFFile = userPost["imageFile"] as? PFFile  {


        imagesPost.getDataInBackgroundWithBlock{(imageData:NSData!, error:NSError!)-> Void in

            if (error == nil) {

                 var image:UIImage = UIImage(data: imageData)!
                println("image from posts finded")


                cell.imagePost.image = image as UIImage



            }
            else {
                println("error")
            }
        } }


    return cell
}


override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(true)


    loadPost()

}

override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    //#warning Incomplete method implementation -- Return the number of sections
    return 1
}


override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    //#warning Incomplete method implementation -- Return the number of items in the section
    return self.timeLineData.count
}



override func viewDidLoad() {
    super.viewDidLoad()


}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
    let userPost: AnyObject  = self.timeLineData.objectAtIndex(indexPath.row)
    let viewController:ImageDetailTableViewController = ImageDetailTableViewController()
    viewController.modalPresentationStyle = UIModalPresentationStyle.Custom
    viewController.transitioningDelegate = self
    viewController.timeLineData = userPost as NSMutableArray
    self.presentViewController(viewController, animated: true, completion: {})
}

}

I set up the detail VC like this :

   import UIKit

  class ImageDetailTableViewController: UITableViewController {


var photoPassed : UIImageView = UIImageView()

var timeLineData:NSMutableArray = NSMutableArray ()


override func viewDidLoad() {
    super.viewDidLoad()


}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete method implementation.
    // Return the number of rows in the section.
    return 1
}


override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell : ImageDetailTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as ImageDetailTableViewCell

    let userPost  = self.timeLineData.objectAtIndex(indexPath.row) as PFObject


    if let imagesPost:PFFile = userPost["imageFile"] as? PFFile  {


        imagesPost.getDataInBackgroundWithBlock{(imageData:NSData!, error:NSError!)-> Void in

            if (error == nil) {

                var image:UIImage = UIImage(data: imageData)!
                println("image from posts finded")


                cell.imageDetail.image = image as UIImage
                cell.imageDetail.backgroundColor = UIColor.lightGrayColor()


            }
            else {
                println("error")
            }
        } }

    println(cell)
    return cell

}
override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(true)


}

}

In fact timeLineData is a NSMutableArray containing PFobject with PFiles (the picture that populate the Table)

The best way would be to pass the picture without having to call Parse another time in the detailViewController.

But i don't know how to do it so i decided to pass the PFobject and then make another call to Parse.com to get the PFfile. I'm sure we can do it easier...

Ok it work but now it s my xcode beta version who make the service kit bug every 5 min... Gona install the previous version. Thank Stone