0

I have adopted an iOS project and need to make add an image method to an object. I was thinking something like this. However, how would I declare the image method to return a UIImage? or is this even possible?

From a larger perspective, these items will be in a UICollection view so I need to load the image to determine the height of a custom UICollectionViewCell.

class Item {
  var imageURL = "https://s3-us-west-1.amazonaws.com/bucket/images/26882/abc.jpg?1477323919"

  // probably need to change this
  func image() -> UIImage {
    URLSession.shared.dataTask(with: NSURL(string: imageURL)! as URL, completionHandler: { (data, response, error) -> Void in

      if error != nil {
        print(error)
        return
      }
      DispatchQueue.main.async(execute: { () -> Void in
        let image = UIImage(data: data!)
        return image
      })

    }).resume()
  }}
  }

edit #1

here's the consumer for this call - not sure if I can adjust this to make it ammenable to an async call in order to get the image. It might be easier to send metadata down in the JSON call rather than computing it via this async call.

extension MasterViewController: MosaicLayoutDelegate {
  func collectionView(_ collectionView: UICollectionView, heightForImageAtIndexPath indexPath: IndexPath, withWidth width: CGFloat) -> CGFloat {
    let item = items[indexPath.item]
    let image = item.image() // need to get this height


    let boundingRect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))
    let rect = AVMakeRect(aspectRatio: image.size, insideRect: boundingRect)
    return rect.height
  }
timpone
  • 19,235
  • 36
  • 121
  • 211

1 Answers1

0

Since it's async your method would need to accept an completion block that will be invoked once the image is downloaded. Something like:

func image(completionBlock: @escaping ((UIImage) -> Void)) {
    URLSession.shared.dataTask(with: NSURL(string: imageURL)! as URL, completionHandler: { (data, response, error) -> Void in

        if error != nil {
            print(error)
            return
        }

        let image = UIImage(data: data!)
        completionBlock(image!)
    }).resume()
}

Explanation for the @escaping attribute.

And then to use this method and display the image, you would do something like this (assuming self.imageView is where you want to display the image):

func displayImage() {
    self.image { (retrievedImage) in
        DispatchQueue.main.async {
            self.imageView.image = retrievedImage
        }
    }
}
Garfield81
  • 521
  • 2
  • 9
  • thx - unfortunately I'm not just putting this into a UIImageView - I need to download it for a collection view to calculate the size. I have included the code above and am not sure how to achieve this (if possible) - is it possible to adjust this for this type of call. I'm considering sending down meta information with it rather than calculating in app. – timpone Mar 23 '17 at 20:54
  • For use in collectionView the simpler solutions would be to either pre-download all the images so you can hand it off to the collectionView synchronously or resize all images to fit a predefined size. – Garfield81 Mar 23 '17 at 21:15
  • A little complex/fancy solution would be to display a fixed loading image and start downloading the image. Once the image is downloaded you can resize (even animate the resize) the cell to the image dimensions. See http://stackoverflow.com/questions/13780153/uicollectionview-animate-cell-size-change-on-selection – Garfield81 Mar 23 '17 at 21:17