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)
}
Related
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.
I'm new at Xcode and swift and ran into this bug. I've searched around a bit and could not find anything on this topic. I have an extension for UIImage that allows me to cache images to the phone here :
import UIKit
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView{
func loadImageUsingCacheWithUrlString(urlString : String)
{
self.image = nil;
// check cache for image first
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = cachedImage;
return
}
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
//download hit an error
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
if let downloadedImage = UIImage(data: data!){
imageCache.setObject(downloadedImage, forKey: urlString as AnyObject)
self.image = downloadedImage
}
}
}).resume()
}
}
It is not loading the image into a table views image view:( Ignore random text )
Table view not loading image
Here is also the UItableView from the main.storyboard:
Updated main.storyboard screen shot
Here is my cellForRowAt: indexPath method where the image is suppose to be loaded:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellid" , for: indexPath) as! CustomChatTableViewCell;
let gray = UIColor(red:0.94, green:0.94, blue:0.94, alpha:1.0)
let red = UIColor(red:1.00, green:0.22, blue:0.37, alpha:1.0)
let message = messages[indexPath.item]
if message.toId == user?.toId{
cell.messageBackground.backgroundColor = red
cell.messageLabel.textColor = UIColor.white
}
else{
cell.messageBackground.backgroundColor = gray
cell.messageLabel.textColor = UIColor.black
}
cell.messageLabel.text = message.text
if let imageUrl = message.imageUrl{
print(imageUrl)
cell.messageImage.loadImageUsingCacheWithUrlString(urlString: imageUrl)
cell.messageImage.isHidden = false;
cell.messageLabel.isHidden = true
//cell.messageBackground.isHidden = true;
}
else
{
cell.messageImage.isHidden = true;
cell.messageLabel.isHidden = false
cell.messageBackground.isHidden = false;
}
return cell;
}
Expected Result:
Images load into cells
Observed Result
Images dont load into the cells :(
these lines of code :
if let imageUrl = message.imageUrl{
print(imageUrl)
cell.messageImage.loadImageUsingCacheWithUrlString(urlString: imageUrl)
Actually print a valid URL string for an image on my firebase database, which is confusing because It is not loading the image.
Important
I use the loadImageUsingCacheWithUrlString method in other parts of my project and it works fine so I don't think its the method.... whats going on?? thank you so much if you can solve this you are an amazing coder!!
I can put an image in the main.storyboard and it works... so I dont know what could be going wrong... :(
screen shot of updated main.storyboard
Image seems to be fine in Extension :
code with breakpoint and console showing
Not sure If the image Is being covered Up in Capture View Hierarchy :
View Hierarchy
In your storyboard, the imageView messageImage is a subview of the view messageBackground. In your if-statement in cellForRow method, you are setting the messageBackground to be hidden
if let imageUrl = message.imageUrl{
print(imageUrl)
cell.messageImage.loadImageUsingCacheWithUrlString(urlString: imageUrl)
cell.messageImage.isHidden = false;
cell.messageLabel.isHidden = true
cell.messageBackground.isHidden = true; //THIS IS THE CULPRIT
}
Since messageBackground is hidden, it's subviews are hidden as well. Might need to rethink your business logic here.
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
}
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
}
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.