Unable to reload collection view on successful image download - ios

I'm editing a record which has some text information and images. As I get images URL so I'm using SDWebImage to download images which are then displayed in a collectionView and it's vertical scrollable. So when the view is loaded I'm doing this in viewDidLoad:
for (index, _) in mediaFiles.enumerated()
{
let img = UIImageView()
if let url = NSURL(string: "\(baseURLGeneral)\(mediaFiles[index].imageFile ?? "")")
{
print(url)
img.sd_setShowActivityIndicatorView(true)
img.sd_setIndicatorStyle(.gray)
img.sd_setImage(with: url as URL, placeholderImage: nil, options: .refreshCached, completed: { (loadedImage, error, cache, url) in
self.imagesArray.append(loadedImage!)
DispatchQueue.main.async
{
self.photoCollection.reloadData()
}
})
}
}
This code as per my understanding is downloading the image from web and when the image is loaded it add the image in an imageArray which I've declared as var imagesArray: [UIImage] = [] and then reload the collection view.
As this is the edit screen so user can also add more images in imagesArray which will show with the downloaded images in the same array and can also remove images.
As per collectionView delegate and dataSource is concerned so I'm returning return imagesArray.count in numberOfItemsInSection.
In cellForItemAt after making a cell variable I've cell.imageAddedByUser.image = imagesArray[indexPath.row].
THE ISSUE which I'm having is that after downloading images in viewDidLoad() collectionView is not getting refreshed. But If I pop and then push view controller it shows the images.

Try to call it in viewDidAppear()
DispatchQueue.main.async { self.photoCollection.reloadData() }

Try to dispatch it in main Queue
DispatchQueue.main.async
{
self.photoCollection.reloadData()
}

Related

UIImage appears 2-3 seconds late on viewController swift

I have a UIImageView that I use for users profile pic. But, the image appears late on screen like 2 to 3 seconds. Here is how I download it:
#IBOutlet weak var ProfilePic: UIImageView!
override func awakeFromNib() {
FriendSystem.system.USER_REF.child(user.uid).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild("imageUrl"){
self.firebaseStorage.child("users").child(FriendSystem.system.CURRENT_USER_ID).getData(maxSize: 10*1024*1024, completion: { (data, error) in
let userPhoto = UIImage(data: data!)
self.ProfilePic.image = userPhoto
})
}
})
}
How can I solve this?
The code you're running runs on a different Thread, and according to Apple, all UI updates must run on the Main Thread. Here's a link that explains why all UI updates must be on the Main Thread
So in your code you just have to use DispatchQueue like this
DispatchQueue.main.async {
// please don't use implicit unwrapping of variables `data!` cause it may cause crashes, use if let statements instead
if let data = data {
let userPhoto = UIImage(data: data)
self.ProfilePic.image = userPhoto
}
}
On a different note:
Please read on Swift coding standards in this link
Well i actually solved it and I will post it here if newbies like me encounter the same problem. I used Firebase and SDWebImage. Here is the link This is from Firebase's official website.
// Reference to an image file in Firebase Storage
let reference = storageRef.child("images/stars.jpg")
// UIImageView in your ViewController
let imageView: UIImageView = self.imageView
// Placeholder image
let placeholderImage = UIImage(named: "placeholder.jpg")
// Load the image using SDWebImage
imageView.sd_setImage(with: reference, placeholderImage: placeholderImage)

Sporadic behaviour in UITableViewCells when loading images

I have a Table View that presents comments. Each comment includes a label and a UIImage that presents the authors profile image. There is no pattern to the way images are appearing when the table is loaded. Sometimes they load fine. Other times, the placeholder image appears and the image never loads at all. If I scroll, the behavior varies. I'm using AlamofireImage.
After some research I came across this post where the recommendation is to call
self.tableView.beginUpdates()
self.tableView.endUpdates()
in AlamofireImage's completion handler. This had no effect at all.
Here is my original code:
func tableView(_ tableView: UITableView, cellForRowAt.....{
...
let myURL = Comment.profileImageURL
if Comment.profileImageURL != nil {
profileImage.af_setImage(withURL: myURL!, placeholderImage: #imageLiteral(resourceName: "Profile Image"))
} else {
profileImage.image = UIImage(named: "Profile Image")
}
And here is the update based on this question's recommendation (last answer on page):
let myURL = comment.profileImageURL
if comment.profileImageURL != nil {
cell.profileImage.af_setImage(
withURL: myURL!,
placeholderImage: #imageLiteral(resourceName: "Profile Image"),
filter: nil,
imageTransition: UIImageView.ImageTransition.crossDissolve(0.5),
runImageTransitionIfCached: false) {
// Completion closure
response in
// Check if the image isn't already cached
if response.response != nil {
// Force the cell update
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
} else {
cell.profileImage.image = UIImage(named: "Profile Image")
}
====EDIT====
Adding additional information in the hopes it will help:
I have tried setting the profileImage.image = nil as suggested below. I have also cancel the download in prepareForReuse() as suggested below to no avail.
Before calling the image, I am doing some configuration on the UIImageView
profileImage.layer.cornerRadius = profileImage.bounds.size.width / 2
profileImage.clipsToBounds = true
profileImage.layer.borderWidth = 2
profileImage.layer.borderColor = UIColor(red:222/255.0, green:225/255.0, blue:227/255.0, alpha: 1.0).cgColor
The following is the imageView setting in XCode:
Finally the issue itself. About 50% of the time I move back and forth between views or scroll, placeholder images appear in place of the actual image. In fact. The place holders often appear on first load.
===EDIT#2===
I'm trying to follow Ashley Mills second suggestion. I check the Image URL in the completion handler of AlamofireImage and the results are odd. I just loaded the table view. All images loaded fine as shown below:
I print out the the image URL when the Completion Handler fires and it looks fine:
I'm in completion
Optional(http://www.smarttapp.com/Portals/0/Users/018/18/18/sballmer.jpg)
I'm in completion
Optional(http://www.smarttapp.com/Portals/0/Users/018/18/18/sballmer.jpg)
I'm in completion
Optional(http://www.smarttapp.com/Portals/0/Users/011/11/11/Elon%20Musk.jpeg)
I'm in completion
Optional(http://www.smarttapp.com/Portals/_default/Users/001/01/1/Martin%20Profile.jpg)
Using the Navigation Bar, I back out, then reload and the images are all place holder images as shown:
The print out is as follows:
I'm in completion nil
I'm in completion nil
I'm in completion nil
Finally this is the code where the image is loaded and the completion handler:
let myURL = Comment.profileImageURL
if Comment.profileImageURL != nil {
// profileImage.af_setImage(withURL: myURL!, placeholderImage: #imageLiteral(resourceName: "Profile Image"))
profileImage.af_setImage(
withURL: myURL!,
placeholderImage: #imageLiteral(resourceName: "Profile Image"),
filter: nil,
completion:{ image in
print("I'm in completion")
print(image.response?.url)
self.setNeedsLayout()
self.layoutIfNeeded()
})
} else {
profileImage.image = UIImage(named: "Profile Image")
}
I've tried everything at this point. Please help.
UITableViewController reuses cells. In order to prevent old images reappearing in cells that are not fully loaded yet, set the cell's image to nil before loading.
cell.profileImage.image = nil
let myURL = comment.profileImageURL
if comment.profileImageURL != nil {
[...]
} else {
cell.profileImage.image = UIImage(named: "Profile Image")
}
Each cell you display in image in can be reused if is scrolled off the screen. Using af_setImage will fetch the image, and display it when the response is received, but by that time the cell might be reused, and be trying to display an image at a different URL.
You can handle this in a couple of ways, but first thing to do is to add a Comment property to your cell, and handle the downloading there rather than in the view controller (you might find making all your IBOutlet properties fileprivate helps with this - it makes sure you're can't update the cell's UI from the view controller)
1) Cancel the download request when the cell is reused…
override func prepareForResuse() {
profileImage.af_cancelImageRequest
}
This should prevent the response for a previous URL being returned, but will mean the image isn't downloaded.
2) Check the URL that that the response is for is the one you're expecting.
var requestedURL: URL?
var comment: Comment? {
didSet {
if let myURL = Comment.profileImageURL {
requestedURL = myURL
// Fetch image from URL and when response is received,
// check self.myURL == response.url
} else {
profileImage.image = UIImage(named: "Profile Image")
}
}
}
This way the image will still be downloaded even if it's not displayed.

Asynchronously Load Images in UITableView While Downloading

I am wondering if there is any way to keep reloading a tableView as items are being downloaded.
There are images and info associated with those images. When the user first opens the app, the app begins downloading images and data using HTTP. The user can only see downloaded items in the tableView as they're being downloaded if he/she keeps leaving the viewController and coming back to it.
I have tried doing something like this:
while downloading {
tableView.reloadData()
}
, however, this uses too much memory and it crashes the app.
How can I asynchronously populate a tableView with images and data as they are being downloaded while still remaining in the tableViewController?
P.S. If you're interested in which libraries or APIs I'm using, I use Alamofire to download and Realm for data persistence.
The correct and usual way to do this is reload data into table, than delegate the single cell to load asyncronously the image from a link.
In swift, you can extend UIImage
extension UIImageView {
func imageFromUrl(urlString: String) {
if let url = NSURL(string: urlString) {
let request = NSURLRequest(URL: url)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
(response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
if let imageData = data as NSData? {
self.image = UIImage(data: imageData)
}
}
}
}
}
And in your CellForRowAtIndexPath load the image from link using something like this
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
cell.image.imageFromUrl(dataArray[indexPath.row].imageUrl)
return cell
}

Loading a preview of a website in a `UIImageView` (Swift)

I'm attempting to load a preview of a website into a UIImageView. I have the following method that is called when the requested website url is received:
func loadWebsite(item:ShareItem) {
activitySpinner.startAnimating()
imageView.setImageForURL(item.asset!.absoluteString) { (image) in
self.showActionButton(true)
self.activitySpinner.stopAnimating()
self.actionButton.titleLabel?.text = "Open"
}
}
I believe there's an error in my logic in the setImageForURL website, which is what is supposed to load the website into my custom image view class:
class MyCustomImageView:UIImageView, UIWebViewDelegate {
var webview:UIWebView?
var completion:((UIImage?)->Void)?
func setImageForURL(urlString:String, completion:((image:UIImage?)->Void)?) {
self.completion = completion
guard let url = NSURL(string: urlString) else {
self.completion?(nil)
return
}
let request = NSURLRequest(URL: url)
webview = UIWebView(frame: self.bounds)
webview!.delegate = self
webview?.scalesPageToFit = true
webview!.loadRequest(request);
}
Does anyone have some insight into how I'm approaching this incorrectly? All the methods are called correctly and in the right order, but no image is shown in the UIImageView.
You need to add the Webview to the image view at some point. By this I mean, using the addSubview method on the imageView, and all this before calling the loadRequest method on the webview. Your webview needs to have a "physical" frame and exist within a parent view before you can load the content.

Show thumbnail image while larger image is downloading asynchronously in Swift

I am using Haneke to download and show images asynchronously in CollectionView.
I want to show a smaller thumbnail type of image while the larger image is being downloaded for each cell.
Here's the code:
let imageUrl = NSURL(string: "imgUrlString")
let miniImageUrl = NSURL(string: "miniImgUrlString")
func onSuccess(image: UIImage?) {
cell!.imageView.hnk_setImageFromURL(imageUrl!)
}
cell!.imageView.hnk_setImageFromURL(miniImageUrl!, placeholder: nil, format: nil, failure: onFailure, success: onSuccess)
Also there is preloader image set on ImageView of Cell just after dequeing it.
cell?.imageView.image = UIImage(named: "preloader")
The Problem
The onSuccess handler is called, but the mini image is never visible. The image changes from "preloader" to "image", while I want the mini image to load soon and then change to original image.
What am I missing?

Resources