I am stuck with a problem. I want to populate a tableview with some text and a profile image that can change i use this function for it.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CommonCellView!
cell = self.tableView.dequeueReusableCellWithIdentifier("CommonCellView") as! CommonCellView
cell.nameLabel.text = self.userCollection[indexPath.row].display_name
cell.companyLabel.text = self.userCollection[indexPath.row].user_organisation
cell.profileImage.hnk_setImageFromURL(NSURL(string: self.userCollection[indexPath.row].profile_picture)!)
self.makeImageViewCircular(cell.profileImage.layer, cornerRadius: cell.profileImage.frame.height)
cell.profileImage.clipsToBounds = true
return cell
}
nothing to suprising here. But when i change my own profile picture then i send it to the API and revist this function it shows the cached image. So i tought i might try something a bit diffent why not get all the images for every cell using Alamofire.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CommonCellView!
cell = self.tableView.dequeueReusableCellWithIdentifier("CommonCellView") as! CommonCellView
cell.nameLabel.text = self.userCollection[indexPath.row].display_name
cell.companyLabel.text = self.userCollection[indexPath.row].user_organisation
cell.profileImage.image = UIImage()
//getting the cell image
Alamofire.request(.GET, self.userCollection[indexPath.row].profile_picture)
.response {(request, response, avatarData, error) in
let img = UIImage(data: avatarData!)
cell.profileImage.image = img
}
self.makeImageViewCircular(cell.profileImage.layer, cornerRadius: cell.profileImage.frame.height)
cell.profileImage.clipsToBounds = true
return cell
}
this works to a point where user scrolles very fast the image of a different user will be shown until the request gets fulfilled. Okey so make that happen somewere else and use an array for the images. I also tried that. but because its async the images would go into the array in the wrong order. So back to HanekeSwift. I read the documentation and saw i had a cache on disk but i could not clear or delete it.
to clear the cache i also tried:
NSURLCache.sharedURLCache().removeAllCachedResponses()
but i did not do a thing. it works in the Alamofire situation but its not a good solution either.
I want to use hanekeSwift because HanekeSwift is fast enough to get all the images. but i want to clear the cache everytime the contoller loads.
Any suggestions would be appreciated!
Cees
I found a the problem.
First i was running an older version of the pod. So after updating i could use the function.
Shared.imageCache.removeAll()
after you import haneke into the controller.
the final pice of code looked like this.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CommonCellView!
cell = self.tableView.dequeueReusableCellWithIdentifier("CommonCellView") as! CommonCellView
cell.nameLabel.text = self.userCollection[indexPath.row].display_name
cell.companyLabel.text = self.userCollection[indexPath.row].user_organisation
cell.profileImage.image = UIImage()
//getting the cell image
cell.profileImage.hnk_setImageFromURL(NSURL(string: self.userCollection[indexPath.row].profile_picture)!)
//deleting it from cache
Shared.imageCache.removeAll()
self.makeImageViewCircular(cell.profileImage.layer, cornerRadius: cell.profileImage.frame.height)
cell.profileImage.clipsToBounds = true
return cell
}
it works now but removing the cache all the time an other cell gets filled seems a bit overkill.
Related
I'm working on the app, which loads flags to a class using https://www.countryflags.io/ API. I am loading a flag when initializing the object using Alamofire get request.
The problem is that the first few TableView cells that are dequeued when starting the app are loaded without flags.
But when I scroll back after scrolling down, they load perfectly.
I thought that it is happening because the request is not processed quickly enough and the first flags are not ready to load before the start of dequeuing cells. But I have no idea how to setup something inside the getFlag() method to help me reload TableView data upon completion or delay dequeuing to the point when all flags are loaded.
Country class with getflag() method
import UIKit
import Alamofire
final class Country {
let name: String
let code: String
var flag: UIImage?
var info: String?
init(name: String, code: String, flag: UIImage? = nil, info: String? = nil) {
self.name = name
self.code = code
if flag == nil {
getFlag()
} else {
self.flag = flag
}
self.info = info
}
func getFlag() {
let countryFlagsURL = "https://www.countryflags.io/\(code.lowercased())/shiny/64.png"
Alamofire.request(countryFlagsURL).responseData { response in
if response.result.isSuccess {
if let data = response.data {
self.flag = UIImage(data: data)
}
}
}
}
}
cellForRowAt method
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let country = countries[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "Country", for: indexPath)
cell.textLabel?.text = country.name
if let flag = country.flag {
cell.imageView?.image = flag
} else {
cell.imageView?.image = .none
}
return cell
}
The init method of Country should not be initiating the asynchronous image retrieval. Instead, you should have the cellForRowAt initiate the asynchronous image retrieval.
But you shouldn’t just blithely update the cell asynchronously, either, (because the row may have been reused by the time your Alamofire request is done). And, a more subtle point, you’ll want to avoid having image requests getting backlogged if you scroll quickly to the end of the tableview, so you want to cancel pending requests for rows that are no longer visible. There are a number of ways of accomplishing all three of these goals (async image retrieval in cellForRowAt, don’t update cell after it has been used for another row, and don’t let it get backlogged if scrolling quickly).
The easiest approach is to use AlamofireImage. Then all of this complexity of handling asynchronous image requests, canceling requests for reused cells, etc., is reduced to something like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: “Country", for: indexPath)
cell.imageView.af_setImage(withURL: objects[indexPath.row].url, placeholderImage: placeholder)
return cell
}
Note, I’d suggest if you’re going to use the default table view cell, that you supply a placeholder image in this routine, like shown above. Just create a blank image (or whatever) that is the same size as your flags. This ensures that the cell will be properly laid out.
By the way, if you’d like to generate that placeholder image programmatically, you can do something like:
let imageSize = CGSize(width: 44, height: 44)
lazy var placeholder: UIImage = UIGraphicsImageRenderer(size: imageSize).image { _ in
UIColor.blue.setFill()
UIBezierPath(rect: CGRect(origin: .zero, size: imageSize)).fill()
}
Now, that creates a blue placeholder thumbnail that is 44×44, but you can tweak colors and size as suits your application.
I'm trying to load images extracted from the web URL into the image view of each cell.
However, when i scroll the table the screen will freeze as I believe it is attempting to grab the images for each cell 1 by 1.
Is there a way i can make it asynchronous? The resources available out there currently is outdated or incompatible(running obj c) as I'm running on Swift 2
The relevant code I'm using within the table view controller is below :
override func tableView(newsFeedTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let blogPost: BlogPost = blogPosts[indexPath.row]
cell.textLabel?.text = blogPost.postTitle
let unformattedDate = blogPost.postDate
//FORMATTING: Splitting of raw data into arrays based on delimiter '+" to print only useful information
let postDateArr = unformattedDate.characters.split{$0 == "+"}.map(String.init)
cell.detailTextLabel?.text = postDateArr[0]
let url = NSURL(string: blogPost.postImageUrl)
let data = NSData(contentsOfURL: url!)
cell.imageView!.image = UIImage(data: data!)//WHY SO SLOW!?
print(blogPost.postImageUrl)
return cell
}
Try this
var image: UIImage
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {() -> Void in
// Background thread stuff.
let url = NSURL(string: blogPost.postImageUrl)
let data = NSData(contentsOfURL: url!)
image = UIImage(data:data)
dispatch_async(dispatch_get_main_queue(), {() -> Void in
// Main thread stuff.
cell.imageView.image = image
})
})
Lets clean your code a bit. First of all, you are trying to declear ALL your cells in your viewController. That means your app is not trying to load every image one byt one, but more like everything all together.
You need to create a separate file called PostCell what is going to be a type of UITableViewCell.
Then you need to go to your prototype cell and connect those view elements to that PostCell just like you would add those to any other ViewController.
Now, here is new code to your cellForRowAtIndexPath function:
override func tableView(newsFeedTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let blogPost = blogPosts[indexPath.row]
if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? PostCell {
cell.configureCell(blogPost)
return cell
}
return UITableViewCell() // You need to do this because of if let
}
And declear this on that PostCell:
func configureCell(post: BlogPost) {
this.textLabel.text = post.postTitle
let postDateArr = unformattedDate.characters.split{$0 == "+"}.map(String.init)
this.detailTextLabel.text = postDateArr[0]
// I would add few if let declarations here too, but if you are sure all these forced ! variables do exciest, then its ok
let url = NSURL(string: blogPost.postImageUrl)
let data = NSData(contentsOfURL: url!)
this.imageView.image = UIImage(data: data!)
}
Or something along those lines. When you connect those elements to your cell, you will get proper variable names for those.
That SHOULD help. There are plenty of tutorials how to make a custom tableviewcell. Some of them advice to put all the declarations inside that cellForRowAtIndexPath, but I have found that it get's problematic very fast.
So...my advice in a nutscell...create a custom tableviewcell.
Hope this helps! :)
To load the image on every cell use SDWebImage third party library. You can add it using pods as put pod 'SDWebImage' It provides various methods to load the image with caching or without caching asynchronously. With caching you don't really need to worry about loading image data every time cell appears on the screen. Try this
override func tableView(newsFeedTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? PostCell {
--reset your cell here--
// cell.imageView.image = nil
}
cell.imageView.sd_setImageWithURL(YOUR_URL, placeholderImage: UIImage(named: "")) {
(UIImage img, NSError err, SDImageCacheType cacheType, NSURL imgUrl) -> Void in
// Do awesome things
}
-- configure your cell here --
}
I am trying to make a custom checkbox list using the UITableview that allows single selections (I should however be able to select as many options as I want) and a custom UITableCell.
My custom cell contains a Label and an ImageView and all I want is to change the image contained in the IV on tap.
I am able to change the image from unchecked to checked in my didSelectRowAtIndexPath without a problem but I am having problems changing it back from checked to unchecked.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
print("tableView -> didSelectRowAtIndexPath")
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CustomCellVC
cell.backgroundColor = UIColor.clearColor()
cell.ivRiskCellImage.image = UIImage(named: "CheckedBox")
}
I tried to play with the highlighted state of the ImageView by adding the checkedbox image as the highlighted image and then turning it on and off but it only enters the if but ignores the if else if clicked again
if(cell.ivCellImage.highlighted == false)
{
print("Highligth is OFF")
cell.ivCellImage.highlighted = true
}
else if(cell.ivCellImage.highlighted == true)
{
print("Highligth is ON")
cell.ivCellImage.highlighted = false
}
as well as checking what image is inside the IV, this if-else block being completely ignored
if(cell.ivRiskCellImage.image!.isEqual(UIImage(named: "UncheckedBox")))
{
print("Is unchecked!")
cell.ivRiskCellImage.image = UIImage(named: "CheckedBox")
}
else if(cell.ivRiskCellImage.image!.isEqual(UIImage(named: "CheckedBox")))
{
print("Is checked!")
cell.ivRiskCellImage.image = UIImage(named: "UnheckedBox")
}
Moreover, when I manage to display the checkedbox image on click I get something like this
while if I try setting the checked box for all the cells in cellForRowAtIndexPath by adding this
cell.ivRiskCellImage.image = UIImage(named: "CheckedBox")
the checkbox tick is no longer semitransparent but white, as it should be
Any ideas? I spent quite some time trying to solve this without any luck.
Instead of let cell = tableView.dequeueReusableCellWithIdentifier... I would use
let cell = tableView.cellForRowAtIndexPath(indexPath) as! CustomCellVC
This way you get a reference to the cell you want to change the image in. tableView.dequeueReusableCellWithIdentifier is meant to be used in delegate method tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) when you want to recycle your cells for better performance.
Also cell.ivRiskCellImage.image!.isEqual(UIImage(named: "UncheckedBox")) doesn't work, because UIImage(named: "UncheckedBox") creates a new UIImage, which isn't the same as the UIImage you want to check it against.
As fat i understand that you want to change image form Check and Uncheck and vice versa
There is mainly two methods of table view delegate
– tableView:didDeselectRowAtIndexPath:
– tableView:didSelectRowAtIndexPath:
reference link : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDelegate_Protocol/#//apple_ref/occ/intfm/UITableViewDelegate/tableView:didDeselectRowAtIndexPath:
Although you can do same thing in – tableView:didDeselectRowAtIndexPath:
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath)
{
print("tableView -> didSelectRowAtIndexPath")
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CustomCellVC
cell.backgroundColor = UIColor.clearColor()
cell.ivRiskCellImage.image = UIImage(named: "**unCheckedBox**")
}
I make a network call in ViewDidLoad to get objects (first 25), then I make another call in willDisplayCell to get the rest of the objects. I'm Using PINReMoteImage in the code below. It works, but the problem is that as you scroll through the collection view, a cell will have one picture then another picture will appear over it. How can I improve this UI? I thought updateWithProgress was supposed to deal with this by using a blur until the image is loaded but it doesn't seem to be working?
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! PinCollectionViewCell
if let pinImageURL = self.pins[indexPath.row].largestImage().url {
cell.pinImage?.pin_updateWithProgress = true
cell.pinImage?.pin_setImageFromURL(pinImageURL, completion: ({ (result : PINRemoteImageManagerResult) -> Void in
if let image = result.image {
self.imageArray.append(image)
}
}))
}
The problem is that I'm reusing cells, but I wasn't resetting the image. So I simply added cell.pinImage?.image = nil to the above. Once I found out that was the problem I added an UIActivityViewIndicatorViewto the cell and stop it when the image comes in.
so i'm kind of a noobe to iOS and Swift also (actually only one month and a half). So I'm trying to make a tableview displaying some images coming from the web, I managed to get the correct info from a web API and display them in the tableview. But, the tableview with online images seems to freeze, I should wait 2-3 seconds for it to move just a little bit and freeze again, which is very frustrating.
I know it has something to do with synchronize, I also tried dispatch_async(dispatch_get_main_queue()){}, and since I'm developing with Swift not ObJ, I tried an open source Web Image download helper from Github called Kingfisher, but it didn't solve my problem.
So, please, help me, and thanks.
To keep it simple, here's the demo code:
var arrays = [
"NPWR02174_00_013F09F4AE3D70230FA695261A8E994D275DF62333/C878A05EF327D7D42E2AF5E1EE15672A1509962C.PNG",
"NPWR04270_00_01C53CAC0BACAC6F052AE4B968370CF9B899DDA81E/C1E1A8AD62EDE2FB95C3BD562F2783F157A0D011.PNG",
"NPWR04472_00_01CB90FF28A2EBB55D210932F56935B377E7A28ECB/DCA2F66365777A7330F685CD8CD5F90F2FE62671.PNG",
"NPWR04841_00_017B982A2A44BB607DECD77484C4670CCB505B65E4/91824E16D16C2789CC34A2F5F5444CD30D71C8F1.PNG",
"NPWR05212_00_018ADD99CEA95C240EFDC92FB993F6BC8C0AF7904D/7266E29159EE69D31AA24FB94BC0E90F5306774A.PNG",
"NPWR05254_00_015EB28BB38A5580D6A73CA0BDFB2F8C6864F85F66/76ED779ECE98E63E8A1A229AE626F62E8325D703.PNG",
"NPWR05257_00_01CB48AFF2A9F0795B5569E1DABBC612E3FA13B79A/3524288D6B1E5B4F51CF7DEA4A3E621913A1BA8C.PNG",
"NPWR05326_00_01DF67B968910B06FDABD8A2C2C6AAB72E0DD47048/8AB6EC3EC992C35472F02DD0C8D4122302C802E1.PNG",
"NPWR05401_00_0188285B6025E65909B39E295D2542DF589EDCCC38/620822A98E3CFC1CFB3B5EDCF3F00B19A5B328C2.PNG",
]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrays.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableview.dequeueReusableCellWithIdentifier("gameCell")!
dispatch_async(dispatch_get_main_queue()){
let image = cell.viewWithTag(101) as! UIImageView
let imageString = arrays[indexPath.row] as! String
let imageURL = NSURL(string: "https://trophy01.np.community.playstation.net/trophy/np/"+imageString)!
let data = NSData(contentsOfURL: imageURL)!
image.image = UIImage(data: data)}
return cell
}
It is because you are downloading the image synchronously. This is the code line that does so.
let data = NSData(contentsOfURL: imageURL)
Try downloading the image asynchronously and then update the cell once image is downloaded. Checkout this answer How to async load images on dynamic UITableView?