0

I have a UIImageView and I want to append there images coming from a remote URL

Up to now it works fine with only one image. I just set cell.related.image to the new image and I return always 1 to the collectionView

How can I switch to appending an array of images?

Is there a way to do the appending in a function? Do I need always to do the appending inside collectionView ?

I want to do it inside an event listener in the future

var img_uls = ["url1" , "url2", "url3"]

func append_image(_ path: String, _ cell: RelatedCollectionViewCell) {
    let url = URL(string: path)

    DispatchQueue.global().async {
        let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
        DispatchQueue.main.async {
            cell.related.image = UIImage(data: data!)
        }
    }
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

    return img_uls.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {



    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RelatedCollectionViewCell", for: indexPath) as! RelatedCollectionViewCell

    append_image(img_uls[indexPath.row], cell);


    return cell
}
M1X
  • 4,971
  • 10
  • 61
  • 123

2 Answers2

1

Just have the array of String which contains Url of your image and pass it as in numberOfItems Method like this

var img_uls = ["url1" , "url2", "url3"]
//array of strings containing urls

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

    return img_uls.count 
}

Now you have to iterate your array img_uls, So that you can fetch Url from array and fetch the images and set in collectionView cell. So, do it like this

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RelatedCollectionViewCell", for: indexPath) as! RelatedCollectionViewCell

    append_image(img_uls[indexPath.row], cell);

// indexPath.row starts from (0) and goes to (img_uls.count - 1)

    return cell
}

Now you want to append the image into images array

var images = NSMutableData()

func append_image(_ path: String, _ cell: RelatedCollectionViewCell) {

    let url = URL(string: path)

    DispatchQueue.global().async {
        let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
        DispatchQueue.main.async {
           images.appendData(data)
           cell.related.image = UIImage(data: data!)
        }
    }
}

Instead of using your own append method, and handling that append and all, you can use Kingfisher or Haenke. They are lightweight and moreover manages caching. And hence your scrolling will be fast.

Aditya Srivastava
  • 2,630
  • 2
  • 13
  • 23
  • How do I append to `images` array? My append_image function accepts a string and a cell but you are calling it like `append_image(images[indexPath.row], cell);` Can you provide also the new `append_image` function please? – M1X Sep 03 '17 at 13:40
  • Where do you have your whole set of url of images which you want to populate in collectionview? – Aditya Srivastava Sep 03 '17 at 13:42
  • `var img_uls = ["url1" , "url2", "url3"]` in the view conroller – M1X Sep 03 '17 at 13:46
  • There are a coupe of errors: First in `images.append(data) // appending` i get `Argument type 'Data?' does not conform to expected type 'AnyObject'` Then in `append_image(images[indexPath.row], cell);` i get ` 'AnyObject' is not convertible to 'String'; did you mean to use 'as!' to force downcast?` – M1X Sep 03 '17 at 14:03
  • Can you post a complete answer please? Also you are not using `images` array at any place only appending to it – M1X Sep 03 '17 at 14:08
  • Though i solved your errors. Unable to understand your why to use `images`. Do you want caching the images and not to load asynchronously every time? If you want it use these libraries mentioned here. – Aditya Srivastava Sep 03 '17 at 14:10
  • No I don't want to do that. At least not now. It was there because of a previous example. I can remove the images array right? – M1X Sep 03 '17 at 14:12
  • Then your append function will be useless. Not useless, I mean you won't be appending then – Aditya Srivastava Sep 03 '17 at 14:13
  • I don't get it. Forget about that array and caching I just want to display an array of urls to my UIImageView. How can I do that please? Why do I need to do this always inside the collectionView function. can this be done from outside? Please post an updated answer – M1X Sep 03 '17 at 14:17
  • So remove that array and don't append anything into it. Why do I need to do this always inside the collectionView function---- Because here you iterate your `img_uls`(same as like for loop) but here you are loading your collectionViewCell also. That's why you have to do here only – Aditya Srivastava Sep 03 '17 at 14:22
  • Is there a way to do this from outside please? There will be more images coming from an event listener for ex? How can I move this outside collectionView? I come from Javascript and these things doesn't make sense to me :/ – M1X Sep 03 '17 at 14:25
  • append method can't be move outside of collectionview method – Aditya Srivastava Sep 03 '17 at 14:28
  • Then how do I load new images to UIImageView. for ex user click a button and i insert a new image – M1X Sep 03 '17 at 14:31
  • Call `collectionview.reloadData()` after you insert new image on button click – Aditya Srivastava Sep 03 '17 at 14:32
  • Can you please check my updated question. Can you make an example of appending this image `https://i.ytimg.com/vi/kWxHV9jkwSA/hqdefault.jpg` from outside, like faking a click event The whole idea is that when the app starts 10 images will be loaded. then more will be added – M1X Sep 03 '17 at 14:49
  • From where these 10images are coming from, probably you are calling some api? To append use [this link](https://stackoverflow.com/questions/24002733/add-an-element-to-an-array-in-swift). Probably i didn't got what you want to achieve. – Aditya Srivastava Sep 03 '17 at 15:11
  • exactly from api. When I'm saying append I mean appending the images to the UIImageView not to a variable. How can I put these images` var img_uls = ["url1" , "url2", "url3"]` to my UIImageView? – M1X Sep 03 '17 at 15:15
  • Call the api from where you get these 10 images and in the [closure](https://stackoverflow.com/questions/29824488/when-to-use-closures-in-swift) of api(i.e where you get response),say you will get 10-15images url's, append them to `img_uls` and call collectionview.reloadData() after appending. Do these steps and you are done. – Aditya Srivastava Sep 03 '17 at 15:20
  • Can you please update your answer. my `UIImageView` instance is this: `@IBOutlet weak var related: UIImageView!` and assume the array of urls is this `var img_uls = ["url1" , "url2", "url3"]` – M1X Sep 03 '17 at 15:26
0

Create a imageUrlArray variable of String which will hold the remote image URLs, then return the count of the array from your numberOfItemsInSection method. In collectionView:cellForItemAt function get the Url to display from the array using imageArray[indexPath.row] and pass the Url to the append_image(_ path: String, _ cell: RelatedCollectionViewCell) function. Better use third party image loading library like KingFisher or Nuke to better handle Asynchronous image downloading and caching.

class Example1: UIViewController, UICollectionViewDataSource {
 var imageArray: [String] = []

  override func viewDidLoad() {
        super.viewDidLoad()
  }

  func updateImages(_ images: [String]) {
   self.imageArray = images 
  }

  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return imageArray.count 
  }

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RelatedCollectionViewCell", for: indexPath) as! RelatedCollectionViewCell
    //append_image(imageArray[indexPath.row], cell)

    //Using Kingfisher 
    let url = imageArray[indexPath.row]
    cell.imageView.kf.setImage(with: url, placeholder: nil, options: [.transition(.fade(1))])
    return cell
  }
}
Suhit Patil
  • 11,748
  • 3
  • 50
  • 60
  • Is it better to use a library for this? Can you show me how do i implement kingfisher. I'm pretty new in swift – M1X Sep 03 '17 at 14:04
  • click on the link https://github.com/onevcat/Kingfisher and checkout the demo folder and documentation. If you have not used cocoapods before then visit this https://www.raywenderlich.com/156971/cocoapods-tutorial-swift-getting-started as well on how to use third party libraries in iOS projects. – Suhit Patil Sep 03 '17 at 14:09