i am using a collectionview inside a tablview cell, in which collectionview cells are having an imageview on which images are displaying from the array of URLs in cellForItemAtIndexPath. I am calling webservice inside tableview cell class to getting array of URLs.
My problem is that whenever i scrolls down or up tableview, collectionview cells are repeating, images of collectionview are repeating itself. so how to get rid out of this.
My Code is as follows -
inside response of webservice
let imgList : NSArray = arrReponseDetails.value(forKey: "imageList") as! NSArray
self.arrImgUrls.removeAll()
for dict in imgList[0] as! NSArray {
let dictionary = dict as! NSDictionary
let strUrl : String = dictionary["url_highRes"] as! String
let url : URL = URL(string: strUrl)!
self.arrImgUrls.append(url)
}
self.collectionView.reloadData()
code inside of cellForItemAtIndexPath
let cell : CollectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CollectionCell
let url : URL = self.arrImgUrls[indexPath.row]
cell.imgSticker.sd_setShowActivityIndicatorView(true)
cell.imgSticker.sd_setIndicatorStyle(.gray)
cell.imgSticker.sd_setImage(with: url, placeholderImage: nil)
cell.imgSticker.contentMode = .scaleAspectFit
return cell
I am not getting much insight from your question but i think problem is with caching of image with reusable cell.
Simply implement prepareForReuse() method in collectionView Cell class(CollectionCell).
override func prepareForReuse() {
super.prepareForReuse()
self.imgSticker = nil
}
Reference of prepareForReuse()
You need to clear it out the old content from the cell's view before setting new one as in some case some content might be left and that reported issue occurs.
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell : CollectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CollectionCell
cell.imageView.image = nil; //Remove the image (cache one)
}
Hope this helps!!
Hi You should add the below code on CollectionCell class:
- (void)prepareForReuse
{
[super prepareForReuse];
[[self imgSticker] setImage:nil];
}
Note: It would be better if you can set the placeholder image also.
I wouldn' use colelctionView inside tableView, it's really strange approach. I used to do it myself for the first time and it got me into trouble. Consider changing the logic for collectionView with custom layout...
But to answer your question, it's better to use this approach (check out this answer)
just set in the CellForRow the imageView method according to this answer:
https://stackoverflow.com/a/27712427/5774854
Related
My code is crashing with the following error in the line that gets the cell at the index path and I'm having trouble debugging it.
Thread: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP,subcode=0x0)
var cell = CollectionViewCell()
for row in 0...self.collectionNetList.count {
let indexpath = NSIndexPath.init(forRow: row, inSection: 0)
self.cell = collectionView?.cellForItemAtIndexPath(indexpath) as! CollectionViewCell
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ CRASHES HERE ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
cell.dlImage.image=UIImage(named: "ted") //Modify the custom pictures on the cell
}
However in this method the same instruction works as expected.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.cell = collectionView.cellForItemAtIndexPath(indexPath)! as! CollectionViewCell
}
CollectionViewCell is not registered. You need to call registerNib method of UICollectionView to register the custom cell in viewDidLoad or viewWillAppear method.
if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
let aCell = cell as! CollectionViewCell
aCell.dlImage.image=UIImage(named: "ted")
}
Such a right
I have created a tableview prototype cell in storyboard and I have added a button to cell and set its tag to indexpath.row. When I scroll my cells the scrolled cell on the top of tableview always set tag to zero instead of correct tag.
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("autoLoadReuseIndentifier", forIndexPath: indexPath)
print("indexpath :\(indexPath.row)")
cell.contentView.viewWithTag(100)?.layer.cornerRadius = 5
tableView.separatorStyle = UITableViewCellSeparatorStyle.None
let tempDict : NSDictionary = savedCardsArray.objectAtIndex(indexPath.row) as! NSDictionary
let bankName = cell.contentView.viewWithTag(102) as! UILabel
deleteButton = cell.contentView.viewWithTag(106) as? UIButton
deleteButton?.tag = indexPath.row
deleteButton?.addTarget(self, action: "deleteCard1:", forControlEvents: UIControlEvents.TouchUpInside)
print("delete button:\(deleteButton)")
// print("indexpath delete tag :\(deleteButton.tag)")
if(self.isSetUpAutoloadSelected){
deleteButton?.hidden = true
}else{
deleteButton?.hidden = false
}
return cell;
}
Whenever I scroll the cells, delete button tag is always set to zero.
If you should go with other way so use follow code and get indexPath.
func deleteCard1(_ sender:deleteCard) {
let buttonPosition:CGPoint = sender.convert(CGPointZero, to:self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}
I think you don't need to follow this approach because firstly you set button tag statically in storyboard and again you are change it's tag in cellforrowatindexpath so when you scroll, cell will never find button with tag 106.If you want to follow this approach then you need to create customButton and add Variable of type NSInteger or whatever you want and store indexpath.row into that variable of customButton.
Another Approach is Create CustomTableViewCell Class and create button outlet in this custom Cell class and set indexpath.row into button tag like this
CustomCellClassObject.buttonName.tag = indexPath.row
As Sumit said, it’s better to use a custom cell and create outlet for the buttons and labels, as fetching sub views using tags will be tough to maintain the code in the future.
Additionally, you don’t have to create the variable deleteButton, as I don’t see a valid purpose.
Assign tag to the button and add target in cellForRowAtIndexPath, it should work fine.
I have a string array of URLs, which drive the collection view cells shown on the master controller. My custom cell has an UIImageView whose image property is loaded in the background via a function call as follows:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CollectionViewCell
loadImageInBackground(NSURL(string: self.fotosString[indexPath.row])!, target: cell.image!)
return cell
}
My collectionViewController correctly displays all the images for the URLs I have in the array.
When I transition to the DetailView, I would like to use the data that was downloaded into the cell, rather than trigger a new download (even though the image would be in the cache) based on the URL string that is in the array. So I am doing the following:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showImage" {
let dvc = segue.destinationViewController as! DetailViewController
let indexPaths = self.collectionView?.indexPathsForSelectedItems()
let indexPath = indexPaths![0] as NSIndexPath
let cell = self.collectionView!.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CollectionViewCell
dvc.currentImage = cell.image.image
}
}
However, cell.image.image returns nil and therefore my approach isn't working.
I could do this in a more conventional way by setting up a class for my photo images like:
urlStr: String
theImage: UIImage
But I am wondering why I'm getting the nil, and how I can access the image data directly from the cell.
You shouldn't do what you want to / are trying to do. You should pass the image URL from your data model and use that.
Your problem is that you're creating / reusing a cell rather than finding the current one by doing:
let cell = self.collectionView!.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CollectionViewCell
where you should have:
let cell = self.collectionView!. cellForItemAtIndexPath(indexPath) as! CollectionViewCell
I am trying to display a progress indicator inside of a collectionView cell. To do this there is a background thread that sends a notification to the Main Thread to update the progress indicator.
In main view controller...
func updateProgressIndicator(notification:NSNotification){
let userInfo = notification.userInfo! as NSDictionary
let date = userInfo["date"] as! NSDate
let percentComplete = userInfo["percent"] as! Double
self.progressIndicator.text = "\(percentComplete)%" // this works
let dateIndex = self.calendarDates.indexOf(date)
let indexPath = NSIndexPath(forItem: dateIndex!, inSection: 0)
let cell = self.collectionView.dequeueReusableCellWithReuseIdentifier("DateCell", forIndexPath: indexPath) as! DateCell
cell.showProgress()
}
The function locates the indexPath for the cell to be updated. The cell's showProgress method is then called.
class DateCell: UICollectionViewCell {
#IBOutlet weak var dotGraph: DotGraphView!
func showProgress(){
print(" DateCell showProgress") // this does get printed
self.setNeedsDisplay()
}
override func drawRect(rect: CGRect) {
print(" DateCell drawRect") // this never gets printed
//drawing functions go here
}
}
The showProgress() method is called for the correct cell and the print message is displayed. When the showProgress method calls setNeedsDisplay() however, the drawRect function is never executed.
The only way I've gotten the cell to update is to fully reload the cell using reloadRowsAtIndexPaths, however this should be unnecessary.
Any ideas on how to get the drawRect function to be called?
You say that showProgress() is called for the correct cell, but that seems unlikely. When you call dequeueReusableCellWithReuseIdentifier(_:forIndexPath:), I would expect you to get a different instance of DateCell than the one the collection view is currently displaying. The one being displayed is in use, so it wouldn't be returned from a dequeue... method. You can test whether I'm correct about this by using NSLog on the cell. I expect the address of this one to be different than the one you returned in cellForItemAtIndexPath().
Rather than dequeuing the cell, you should just put the cell in a property so that everyone uses the same one. That's the cell you should return in cellForItemAtIndexPath() as well.
As Rob suggested, the dequeueReusableCellWithReuseIdentifier(_:forIndexPath:) was causing the problem. Storing the cell in a dictionary when it was created, allowed it to be referenced directly. This meant that the drawRect() would be called with setNeedsDisplay()
Below are the updated functions...
var calendarDateCells:[NSDate:DateCell] = [:]
func updateProgressIndicator(notification:NSNotification){
let userInfo = notification.userInfo! as NSDictionary
let date = userInfo["date"] as! NSDate
let myCell = self.calendarDateCells[date]
if(myCell != nil){
myCell!.showProgress()
}
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("DateCell", forIndexPath: indexPath) as! DateCell
let date = calendarDates[indexPath.item]
self.calendarDateCells[date] = cell
return cell
}
This lengthy title is roughly my problem. I started simple learning example using UICollectionView in Swift project.
I added CollectionView to ViewController created by template, assigned delegate and data source. Custom cell is created in storyboard. Reuse identifier is set.
Everything is fine so far. I have placed one UILabel in custom cell, and gave tag value 100.
Here's code of my ViewController: https://gist.github.com/tomekc/becfdf6601ba64d5fd5d
And interesting exceprt below:
func collectionView(collectionView: UICollectionView!, cellForItemAtIndexPath indexPath: NSIndexPath!) -> UICollectionViewCell! {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("element", forIndexPath: indexPath) as UICollectionViewCell
cell.backgroundColor = UIColor.yellowColor()
// list subviews
NSLog("--- ROW %d ---", indexPath.row)
printSubviews(cell)
if let labelka = cell.viewWithTag(100) as? UILabel {
labelka.text = String(format: "Row %d", indexPath.row)
NSLog("Found label")
}
return cell
}
func printSubviews(view:UIView) {
if let list = view.subviews as? [UIView] {
for uiv in list {
NSLog("%# tag:%d", uiv, uiv.tag)
printSubviews(uiv)
}
}
}
The problem is that cell.viewWithTag(100) returns nil until cell is reused. When I scroll the view so any of cells goes out of window and reuse is forced, viewWithTag(100) returns the label and I can set its value.
What's interesting, I put together similar example in Objective-C and there is no such problem. Even when built and run with XCode6 beta4.
I wonder if I missed something or this is wrong behavior?
Update: apparently I took too simplistic approach. When I created custom UICollectionViewCell subclass (as I usually do), result is correct.