I'm writing a Swift app that displays photos fetched from server on each cell of my UITableViewController.
So far my code looks as follows:
func tableView(detailsPanel: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = detailsPanel.dequeueReusableCellWithIdentifier("cell") as! DetailsCell
let test:SingleTest = self.items[indexPath.row] as! SingleTest
if(test.photo != "") {
cell.myPhoto.af_setImageWithURL(NSURL(string: test.photo)!)
}
}
Now, the problem is that not every cell has a photo stored in cell.photo. Some of them are empty strings (""). In that situation when I quickly scroll through the table view, I see that those empty UIImageViews are filled with photos from other cells.
The quick fix for that seems to be adding an else block:
if(test.photo != "") {
cell.myPhoto.af_setImageWithURL(NSURL(string: test.photo)!)
} //this one below:
else {
cell.myPhoto.image = UIImage(named: "placeholderImg")
}
Now whenever there is no photo, the placeHolderImg will be displayed there. But... is there a way of avoiding it and just do not display anything there? And by not displaying anything I mean not displaying images from different cells?
You are reusing your cells, thus the cell will still use the previous image that it has loaded unless you set it to nil or a placeholder image. However I believe you do not need to use an if statement. You can use the placeholderImage parameter of af_setImageWithURL.
cell.myPhoto.af_setImageWithURL(NSURL(string: test.photo)!, placeholderImage: image)
Related
I am settiing an image onto a tableViewCell with Alamofire
let messageImage = cell?.viewWithTag(6) as! UIImageView
messageImage.image = nil
messageImage.af_setImage(withURL: photoUrl! as URL)
return cell
I am appending each cell item to an array using Firebase:
ref.child("messages").child(ticker.current).queryOrderedByKey().observe(.childAdded, with: {snapshot in
let snapDict = snapshot.value as? NSDictionary
let photoUrl = snapDict?["photo_url"] as? String ?? ""
messageArray.insert(messageCellStruct(photoUrl: photoUrl), at: 0)
})
Then I am updating the image on the Same Exact URL with FirebaseStorage
When I re-call the cell ViewController that the TableViewCell is in, the image is not changed on the first couple of cells. But as I scroll to older cells the image is updated:
messageArray.removeAll()
tableView.reload()
If I rebuild the app all cell images are how they are supposed to be.
I am assuming this is because of an Observer error or Im not removing the observer. I really dont know.
1.) The cells share the same exact URL im just changing the data.
2.) It seems to be only working when a cell hasnt loaded yet(or been assigned).
3.) It works perfectly fine after I rebuild and run the app.
Maybe I need to clear the Alamofire cach?
It sounds like you need to set the old image to nil before you set the image in messageImage.af_setImage(withURL: photoUrl! as URL).
Cell's are supposed to get cleaned up before they are reused: Apple -PrepareForReuse but Apple also says:
For performance reasons, you should only reset attributes of the cell
that are not related to content, for example, alpha, editing, and
selection state. The table view's delegate in
tableView(_:cellForRowAt:) should always reset all content when
reusing a cell.
That basically means you Shouldn't set your imageView's image to nil in prepareForReuse BUT if you sub classed the cell you can create a function inside the subclass and in cellForRowAtIndePath call it before you run messageImage.af_setImage(withURL: photoUrl! as URL). Example Below:
Create subclass for cell and name it MyCustomCell
class MyCustomCell: UITableViewCell {
#IBOutlet weak var messageImage: UIImageView!
func setImageToNil(){
messageImage.image = nil
// I don't use AlamoFire and don't know anything about it but if they have something to set the imageView's image to nil you can also try using that instead
}
}
Inside your tableView's cellForRowAtIndexPath cast the cell then call setImageToNil() before you run messageImage.af_setImage
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// 0. cast the cell as MyCustomCell
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCustomCell", for: indexPath) as! MyCustomCell
// 1. call the setImageToNil() method and whatever image is inside messageImage will get set to nil every time the cell is reused
cell.setImageToNil()
// 2. the new image should always appear because the old image was set to nil
cell.messageImage.af_setImage(withURL: photoUrl! as URL)
return cell
}
Alamofire saves image cache locally.
Because I was changing data to the Same Exact URL I had to remove the cache.
UIImageView.af_sharedImageDownloader.imageCache?.removeAllImages()
UIImageView.af_sharedImageDownloader.sessionManager.session.configuration.urlCache?.removeAllCachedResponses()
I have an application wherein when tableview cell is clicked, another tableview is loaded and an api call is made. Based on the response from api, table view list is loaded and when a particular item in the second tableview is selected, there is a selected checkbox displayed just besides the tableview text label and at the same time database is updated with selected value,
so when I come back to the first tableview I display a label with selected item.
When the first tableview cell I clicked, api is called and results of api should be compared with the active list from database and that particular cell should remain selected.
When there is some item selected in first tableview and when i click on that particular cell, api results reload the tableview and selection for respective cell is not displayed.
Following is the code:
for selectedDict in (appDelegate?.selectedCategoryFilterArray)! {
let selectedUuid = selectedDict.categoryUuid
print("selectedUuid\(selectedUuid)")
for allDict in self.requestedFiltersArray!{
let allUuid = allDict.objectForKey("uuid") as? String
if selectedUuid == allUuid {
cell.imgSelected.image = UIImage(named: "radio_selected")
continue
}else{
cell.imgSelected.image = UIImage(named: "radio")
}
print("allUuid\(allUuid)")
}
}
This is not working as expected, no cell is displaying as selected even if their is a cell selected.
Where have you placed this code?
From what I think you might be trying to achieve, here is an approach that I would suggest.
For your second tableView which is loaded based on the API call results, I would add a String property called uuid to your custom UITableViewCell class.
Then when you call cellForRowAtIndexPath to populate your second tableView, instantiate each cell as your custom UITableViewCell and set its uuid property to the appropriate value based on the results array, using the indexPath.row.
After setting that, next you can run the for loop with your condition to match the cell's uuid property value against the selectedUuid value from your AppDelegate and thereafter set the image as per the logic.
A rough implementation:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier") as! YourCustomTableViewCell
let allDict = self.requestedFiltersArray[indexPath.row]
let allUuid = allDict.objectForKey("uuid") as? String
cell.uuid = allUuid
for selectedDict in (appDelegate?.selectedCategoryFilterArray)! {
let selectedUuid = selectedDict.categoryUuid
if selectedUuid = cell.uuid {
cell.imageSelected.image = UIImage(named: "radio_selected")
} else {
cell.imageSelected.image = UIImage(named: "radio")
}
}
}
I'm following a site to help learn swift and I'm getting confused about this part right here. Basically we added the if cell.imageview.image == nil statement so hat when the collection view loads and you scroll the image doesn't reload the filters. What I don't understand is if you scroll down a cell is reused for the bottom row, now why if I scroll back up doesn't it have to reload the filter? is that data saved somewhere so when I scroll up the properties don't have to repopulate? and If thats the case why would I have to use that if statement at all?
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) as! FilterCell
if cell.imageView.image == nil {
cell.imageView.image = placeholder
let filterQueue: dispatch_queue_t = dispatch_queue_create("filter queue", nil)
dispatch_async(filterQueue, { () -> Void in
let filterImage = self.filteredImageFromImage(self.thisFeeditem.thumbNail, filter: self.filters[indexPath.row])
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.imageView.image = filterImage
})
})
}
return cell
}
When a cell is reused, a cell object that was already allocated is just used again. Any properties or data that was set to it will remain.
When you scroll back up, the cell has already had it's image set, and so it won't reload the new filtered image.
Cells can be reused whether you scroll up or down. You should assume the cell returned is a cached version for a different item. Therefore it may already be bound with another cell's data and you'd want to always rebind the cell with the proper items' data.
I'm trying to create an autocompleter using iOS 8, Swift and Xcode 6.3
I have a problem that I'm trying to solve, but I gave up... I hope someone can help here. The problem is that (custom) UITableViewCell's are not displaying when the initial dataSource is empty. When adding data to datasource and reloading the tableView, the cells SHOULD display, but they don't... At least, the first time they don't... A second time, they DO... When I initialize the table with non-empty data, the problem doesn't occur. I guess something goes wrong with dequeueReusableCellWithIdentifier. In beginning, no reusable cells are found, or something. But I don't know why...
Relevant code, in ViewController.swift:
// filteredWords is a [String] with zero or more items
#IBAction func editingChanged(sender: UITextField) {
autocompleteTableView.hidden = sender.text.isEmpty
filteredWords = dataManager.getFilteredWords(sender.text)
refreshUI()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! AutocompleteTableViewCell
cell.title.text = filteredWords[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredWords.count
}
func refreshUI() {
self.autocompleteTableView.reloadData()
}
I created a sample project on github:
https://github.com/dirkpostma/swift-autocomplete
And a movie on YoutTube to show what goes wrong:
https://www.youtube.com/watch?v=ByMsy4AaHYI
Can anyone look at it and spot the bug...?
Thanks in advance!
You've accidentally hidden your cell.
Open Main.storyboard
Select Cell
Uncheck Hidden
Side note: As for why it's displaying the second time around with the cell hidden? It appears to be a bug. It should still be hidden (print cell.hidden, notice it's always true despite showing the text on the screen).
I think you need to change your code. Check out below code. It is because if you remember in Objective C you needed to check if the Cell was nil and then initialise it. The reuse identifier is usually reusing an already created cell, but on the first launch this does not work because there is no Cell to use. Your current code assumes always that the cell is created (re-used) because you are using ! in the declaration, so if you use the optional (?) it can be null and you then can create the cell
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? AutocompleteTableViewCell
if cell == nil
{
//You should replace this with your initialisation of custom cell
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")
}
cell.title.text = filteredWords[indexPath.row]
return cell
I'm using Parse. I have a column in my table to store images as PFFiles. I am using a PFQueryTableViewController. I want to display images in my Parse table's "image" column as thumbnails for each table view cell that has an image associated with it.
Here is the relevant cellForRowAtIndexPath code I'm suspicious of:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!, object: PFObject!) -> PFTableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("ListingCell", forIndexPath: indexPath) as PFTableViewCell
// This works as you can see "grapes" displayed on the screenshot I added.
var label = cell.viewWithTag(2) as UILabel
label.text = object["title"] as? String
// Accessing the PFImageView within my custom PFTableViewCell via a tag
let imageView = cell.viewWithTag(3) as PFImageView
// Extracting the image stored as a PFFile stored in the database
imageView.file = object["image"] as? PFFile // remote image
// Setting the actual thumbnail with the image when loaded
imageView.loadInBackground { (image: UIImage!, error: NSError!) -> Void in
imageView.image = image
}
return cell
}
I have 2 records stored in this Parse table, the first does not have an image and the second one does (hence why the "grapes" cell is empty). My question is, why is the image not displaying in the thumbnail (properly constrained) I created and rather taking up the entire screen? What am I doing wrong here? The even weirder part is that I used Xcode's view debugging to capture the view hierarchy and it shows it properly placed within the thumbnail. Any ideas?
You need to make sure your image mode is set to something like "Scale To Fill".