Crash on collection view - ios

I am having 2 structs. The data from these are added to an array called productData1. This array has images as well as some details like name, rate etc. The images and the data name, rate etc.are to be displayed in a collection view. In the collectionview cellForItemAt... this is how I'm showing the data..
let productObject = productData1[indexPath.row]
cell.nameLabel.text = productObject.name
cell.rateLabel.text = productObject.theRate
This is working fine. And the images I'm trying to show like so...
if let data = try? Data(contentsOf: productObject.images[0].url) {// Crashed here..
let img = UIImage(data: data)
self.arrayOfURLImages.append(img!)
cell.recipeImageView.image = self.arrayOfURLImages[indexPath.row]
}
But when I try to display the images like this, the images do get displayed in the collectionview. But when I scroll them, I get a crash in this line if let data = try? Data(contentsOf: productObject.... saying fatal error: Index out of range. Hope somebody can help...
EDIT This is the full code in cellForItemAt...
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath as IndexPath) as! RecipeCollectionViewCell
let productObject = productData1[indexPath.row]
cell.nameLabel.text = productObject.name
cell.rateLabel.text = productObject.theRate
if let data = try? Data(contentsOf: productObject.images[0].url) {
let img = UIImage(data: data)
print(img!)
self.arrayOfURLImages.append(img!)
cell.recipeImageView.image = self.arrayOfURLImages[indexPath.row]
}
cell.sellButton.addTarget(self,action: #selector(SellBtnTapped(_:)),for: .touchUpInside)
return cell
}

Related

Swift: How to increase loading speed of images from documents?

My app saved photo to the local documents folder and I used the UICollectionView to display all the image from that folder. But whenever I try to open the CollectionView it often took several seconds to open. I'm thinking that maybe the image files are too big, each photo is around 10MB. I also tried using thumbnails to display in collectionview but it still too slow. Any idea how to speed that up?
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SPCell
// Configure the cell
cell.imageView.image = loadImage(fileName: self.fileURLs[indexPath.row])
return cell
}
func loadImagesFromDocuments(){
let fileManager = FileManager.default
let documentsURL = NSHomeDirectory() + "/Documents/Secure/"
do {
fileURLs = try fileManager.contentsOfDirectory(at: URL(string: documentsURL)!, includingPropertiesForKeys: nil)
} catch {
print("Error while enumerating files : \(error.localizedDescription)")
}
}
func loadImage(fileName: URL) -> UIImage? {
do {
let imageData = try Data(contentsOf: fileName)
return UIImage(data: imageData)
} catch {
print("Error loading image : \(error)")
}
return nil
}
Current problem is that you load the image every time the cell appears so Instead of
var fileURLs = [URL]()
Make it
var fileImages = [UIImage]()
Then inside viewDidLoad
fileImages = fileURLs.compactMap { self.loadImage(fileName: $0) }
you are synchronously loading images while returning every cell at indexPath.
cell.imageView.image = loadImage(fileName: self.fileURLs[indexPath.row])
Instead, you can create a custom UICollectionViewCell implementation with a variable in global scope say imageURL.
after cell initialization, do this:
cell.imageURL = self.fileURLs[indexPath.row]
and, in ViewDidLoad() of your custom class, add this line:
self.imageView.image = loadImage(fileName: self.imageURL)
doing so, will lead to images being loaded in each custom implementation of cell unblocking the thread which is invoking the DataSource of your CollectionView.

Error loading image from Firebase to my Table View

I want to load my images from Firebase to my Table View but I get the error:
Cannot convert value of type 'String' to expected argument type 'URL'
When I print the object on its own it is definitely a URL.
This is what my code looks like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FeedItem", for: indexPath) as! FeedItem
//TODO: Guard...
let postImage = postArray [indexPath.row]
let postImageURL = postImage.postImageURL
let data = Data(contentsOf: postImageURL) // Line with Error
cell.postImage.image = UIImage (data: data)
return cell
}
To display the image in your cell, you need to convert the URL string into an actual URL object, which you can do via:
let postImage = postArray[indexPath.row]
if let postImageURL = URL(string: postImage.postImageURL)
{
do {
let data = try Data(contentsOf: postImageURL)
cell.postImage.image = UIImage (data: data)
} catch {
print("error with fetching from \(postImageURL.absoluteString) - \(error)")
}
}
And as rmaddy implies, your performance is not going to be very good (because depending on how far away the remote server is or how slow the internet is), the synchronous "Data(contentsOf:" call might take an unacceptably long time to succeed. I'm just providing this answer so you will be able to see something in your own testing, but I wouldn't use this in production code.
Try to replace the Data fetch with an asynchronous URLSession task, and you can find much more information in this very related question.

swift downloading url and populating uiimageview

I'm having difficulty getting each image of my tableviewcell to show the thumbnail of my blog. the link is valid as i had it print to screen to make sure it works here is the block of code causing my brain aneurysm
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let blogPost: BlogPost = blogPosts[indexPath.row]
//cell.textLabel?.text = blogPost.postTitle
postImageLink = blogPost.postImageLink
cell.textLabel?.text = blogPost.postImageLink
if postImageLink != "" && cell.imageView?.image != nil {
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
cell.imageView?.image = UIImage(data: NSData(contentsOfURL: NSURL(string:self.postImageLink)!)!)
}
} else {
cell.imageView?.image = UIImage(named: "IMG_0079")
}
return cell
}
i set the text label to postimagelink only to ensure it was parsing the correct code for future usage.
To be clear the problem is the thumbnails of my blog won't load. Only the preset image incase a thumbnail isn't present..postImageLink is a unique url for each thumbnail that I parsed.
(yeah i know i can write a parser but can't solve this problem =(..)
any help is appreciated before I throw my laptop off the roof thanks!
here is image
-rookieprogrammer
try to make two blocks instead, to retrieve and set the image
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// retrieve image from url
let image = UIImage(data: NSData(contentsOfURL: NSURL(string:self.postImageLink)!)!)
dispatch_async(dispatch_get_main_queue(), { Void in
// set image in main thread
cell.imageView?.image = image
})
}
can read more here
Your code is working. It's just that should be doing it in the main_queue, UI main thread, to edit any UI objects.
dispatch_async(dispatch_get_main_queue()) {
() -> Void in
// retrieve image from url
let image = UIImage(data: NSData(contentsOfURL:NSURL(string:self.postImageLink)!)!)
cell.imageView?.image = image
}

How can I add an image to my table cell in swift?

I'm following this tutorial and I'm creating cells for each json entry. Everything works fine without photos:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CELL")
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")
}
let user:JSON = JSON(self.items[indexPath.row])
// print(user)
let picURL = "/Users/K/Desktop/aproject/apps/address/addr/Images.xcassets/TabMap.imageset/up_dark.png"//just for tests - some random png
let url = NSURL(string: picURL)
let data = NSData(contentsOfURL: url!)
cell!.textLabel?.text = user["description"].string
// cell?.imageView?.image = UIImage(data: data!)
return cell!
}
, but when I add photos (uncomment this line):
cell?.imageView?.image = UIImage(data: data!)
then I'm getting error:
fatal error: unexpectedly found nil while unwrapping an Optional value
My plan is to pass there an url instead of hardcoded photo (so using user["photo"] instead of the link), but for tests I pasted the url to my custom graphic.
How can I add it next to text?
For the local image:
cell!.imageView?.image = UIImage(named:"ImageName")
A couple things:
Why does the line before have "cell!" and the commented out line is "cell?". Also, is this a table view subclass? Are you sure your imageView outlet is connected?
More importantly, you should do an error check when loading your image data.
That is:
if let data = NSData(contentsOfURL: url!)
{
cell!.imageView?.image = UIImage(data: data)
}

Swift - Adding image to tableview cell from asset library

I am trying to add an image to a tableview cell but not having much luck getting it to display.
The image is being loaded from the file system (I println() the result) as a UIImage but I cannot seem to get it into the cell. Placing a println() after the closure shows me that the images are all loaded after the cell has been returned.
Here is my code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
let note = notes[indexPath.row]
let nsURL = NSURL(string: note.valueForKey("url") as! String)!
var loadError: NSError?
var image: UIImage?
assetsLibrary!.assetForURL(nsURL, resultBlock: { (asset) -> Void in
if let ast = asset {
let iref = ast.defaultRepresentation().fullResolutionImage().takeUnretainedValue()
image = UIImage(CGImage: iref)
println("The loaded image: \(image)")
}
}, failureBlock: {(error) -> Void in
loadError = error
})
cell.textLabel!.text = note.valueForKey("title") as? String
cell.imageView!.image = image
return cell
}
When I replace the closure with the following to load a image from the project itself it shows in the table. This leads me to believe it not due to an issue with the way the story board is set up.
UIImage(named: transportItems[indexPath.row])
Any help would be much appreciated.
It doesn't work that way. cellForRowAtPathIndex is a synchronous routine. assetForURL is an asynchronous routine. It will return the data long time after cellForRowAtPathIndex has returned.
Here's what you should do: Have a method cachedAssetForURL which returns the asset immediately, or returns nil. If it returns an asset, store it. Remember this has to be as efficient as possible, because this is called while the user scrolls up and down through the images.
If the method returns nil, trigger a download in the background. When that download finishes, don't even try to store the image in the cell - by this time, the same cell could display an entirely different object! Instead store the data so that cachedAssetForURL will be able to return the asset, and invalidate the row of your table view.
Fire a notification in the block (it's asynchronous), manage it by setting your imageview and reload your view.
The underlying issue I was encountering, as pointed out by #gnasher729, was the mishmash of synchronous and asynchronous calls. Rewriting the code to take this into account and to include a cache, I have the following working solution.
var cachedImages = [String:UIImage]()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
let note = notes[indexPath.row]
let url = note.valueForKey("url") as! String
cell.textLabel!.text = note.valueForKey("title") as? String
cell.imageView!.image = UIImage(named: "note-icon")
if let image = cachedImages[url]{
cell.imageView!.image = image
} else {
let nsURL = NSURL(string: url)!
var loadError: NSError?
assetsLibrary!.assetForURL(nsURL, resultBlock: { (asset) -> Void in
if let ast = asset {
let image = UIImage(CGImage: ast.defaultRepresentation().fullResolutionImage().takeUnretainedValue())
self.cachedImages[url] = image
dispatch_async(dispatch_get_main_queue(), {
if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {
cellToUpdate.imageView?.image = image
}
})
}
},failureBlock: {(error) -> Void in
loadError = error
})
}
return cell
}
This could be further improved to take more off of the main thread.

Resources