Open mreilly4 opened 8 years ago
What do you mean? Can you describe the issue?
When I scroll the tableView Vertically it is jumpy. Skips and freezes intermittently.
Great tutorial by the way. Easy to follow and it works fine just feel like there is something I can do to make it smoother.
Thanks a ton for the response!
Mike
On Sat, Aug 6, 2016 at 9:42 AM, Ash Furrow notifications@github.com wrote:
What do you mean? Can you describe the issue?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/issues/24#issuecomment-238026580, or mute the thread https://github.com/notifications/unsubscribe-auth/AFmm9rDJsXbdWyDoh56zV8xSYUt9iIPYks5qdJ1IgaJpZM4JeSjq .
Thanks :bow:
Dropping frames isn't good, hmm. I would need to profile using the Time Profile in Instruments.app to make sure, but this is the code that gets called for each cell, maybe there's something there we can optimize.
For example, the delegate and datasource are always the same, maybe re-setting them does something expensive?
if collectionView.delegate != dataSourceDelegate {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
}
That gives an error for some reason...
On Saturday, August 6, 2016, Ash Furrow notifications@github.com wrote:
Thanks 🙇
Dropping frames isn't good, hmm. I would need to profile using the Time Profile in Instruments.app to make sure, but this https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/blob/ec5af131532c833557c128e2e0643de478e0b573/Table%20View%20in%20a%20Collection%20View/TableViewCell.swift#L13-L17 is the code that gets called for each cell, maybe there's something there we can optimize.
For example, the delegate and datasource are always the same, maybe re-setting them does something expensive?
if collectionView.delegate != dataSourceDelegate { collectionView.delegate = dataSourceDelegate collectionView.dataSource = dataSourceDelegate }
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/issues/24#issuecomment-238026954, or mute the thread https://github.com/notifications/unsubscribe-auth/AFmm9njHulKMRUBB_gdrxGtrAeWooQzaks5qdJ74gaJpZM4JeSjq .
I am loading thumbnails to a UIImageView in each cell. Might be that but I previously just loaded them to static UIImageViews in a tableViewCell and there were no issues with performance there.
Thanks for your help.
On Sat, Aug 6, 2016 at 10:00 AM, Michael Reilly mreilly4@alumni.nd.edu wrote:
That gives an error for some reason...
On Saturday, August 6, 2016, Ash Furrow notifications@github.com wrote:
Thanks 🙇
Dropping frames isn't good, hmm. I would need to profile using the Time Profile in Instruments.app to make sure, but this https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/blob/ec5af131532c833557c128e2e0643de478e0b573/Table%20View%20in%20a%20Collection%20View/TableViewCell.swift#L13-L17 is the code that gets called for each cell, maybe there's something there we can optimize.
For example, the delegate and datasource are always the same, maybe re-setting them does something expensive?
if collectionView.delegate != dataSourceDelegate { collectionView.delegate = dataSourceDelegate collectionView.dataSource = dataSourceDelegate }
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/issues/24#issuecomment-238026954, or mute the thread https://github.com/notifications/unsubscribe-auth/AFmm9njHulKMRUBB_gdrxGtrAeWooQzaks5qdJ74gaJpZM4JeSjq .
Image views can be tricky, because they use UIImage
which does a lot of work under the hood. For example, displaying a JPEG requires it to be decompressed first. That's usually fast enough that it doesn't matter, but showing lots of images or showing large images (or both!) can cause performance issues.
What's the error you're getting?
I set my app up to load images from a cache. If the cache doesnt exists it loads them asynchroniously. Then updates the collectionViewCell. It shouldn't make the tableView lag or jump while scrolling...
I register my CollectionCell XIB in my custom tableViewCell file 'awakeFromNib()' function.
Is this expensive?
I also have to fetch from core data each time a tableViewcell is presented to find out how many collectionView Items i have. See below. Is anything here expensive?
extension NotesTableViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
var photoFilteredIndexRow = Int()
if (searchController.active && searchController.searchBar.text != "")
|| searchController.searchBar.text != "" {
photoFilteredIndexRow = filteredNotes[collectionView.tag]
} else {
photoFilteredIndexRow = collectionView.tag
}
// first get the photos for this cell
let noteCollection = notesFRC?.objectAtIndexPath(NSIndexPath(forRow:
collectionView.tag, inSection: 0)) as! Notes
setupPhotosFetchedResultsController(noteCollection)
print("We got this many in collection view for row \(collectionView.
tag) : ((photosFRC?.fetchedObjects?.count)!)")
return (photosFRC?.fetchedObjects?.count)!
}
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) ->
UICollectionViewCell {
var photoFilteredIndexRow = Int()
if (searchController.active && searchController.searchBar.text != "")
|| searchController.searchBar.text != "" {
photoFilteredIndexRow = filteredNotes[collectionView.tag]
} else {
photoFilteredIndexRow = collectionView.tag
}
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("\(
collectionCellIdentifier)", forIndexPath: indexPath) as! CollectionCell
configureTextForCollectionCell(cell, withIndexPath: indexPath,
collectionViewTag: photoFilteredIndexRow)
return cell
}
func configureTextForCollectionCell(cell: CollectionCell, withIndexPath
indexPath: NSIndexPath, collectionViewTag: Int) {
let noteCollection = notesFRC?.objectAtIndexPath(NSIndexPath(forRow:
collectionViewTag, inSection: 0)) as! Notes
setupPhotosFetchedResultsController(noteCollection)
if let photoItem = photosFRC?.objectAtIndexPath(indexPath) as?
Photos {
if let image = imageCache["\(photoItem.content)"] {
print("THERE IS A CACHE FOR COLLECTION!!")
cell.photoImage.image = image
} else {
urlString = photoItem.content as String
imgURL = NSURL(string: urlString)
loadImage(imgURL!, urlString: urlString, indexPath:
indexPath, cell: cell)
}
}
}
func loadImage(imgURL: NSURL, urlString: String, indexPath: NSIndexPath,
cell: CollectionCell) {
print("loading image!!!")
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue,
completionHandler: { (response, data, error) -> Void in
if error == nil {
// Convert the downloaded data in to a UIImage object
let image = UIImage(data: data!)
// Store the image in to our cache
self.imageCache[urlString] = image
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
if (self.searchController.active && self.
searchController.searchBar.text != "") || self.searchController.searchBar. text != "" {
for i in 0 ..< self.filteredNotes.count {
if self.filteredNotes[i] == indexPath.row {
self.filteredIndexPath = NSIndexPath(forRow:
self.filteredNotes[indexPath.row], inSection: 0)
}
}
} else {
self.filteredIndexPath = indexPath
}
if let cellToUpdate = cell as? CollectionCell {
cellToUpdate.photoImage.image = image
}
})
}
})
}
}
On Sat, Aug 6, 2016 at 10:23 AM, Ash Furrow notifications@github.com wrote:
Image views can be tricky, because they use UIImage which does a lot of work under the hood. For example, displaying a JPEG requires it to be decompressed first. That's usually fast enough that it doesn't matter, but showing lots of images or showing large images (or both!) can cause performance issues.
What's the error you're getting?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/issues/24#issuecomment-238028552, or mute the thread https://github.com/notifications/unsubscribe-auth/AFmm9p-tTgptAbnUmvH7Sy90IK_pE4y-ks5qdKbXgaJpZM4JeSjq .
Ash,
It does seem to be the
setCollectionViewDataSourceDelegate
function that is slowing everything down. Do you know where we might be able to call this in the tableView VC so it only happens once?
Thanks!
Mike
On Sat, Aug 6, 2016 at 10:30 AM, Michael Reilly mreilly4@alumni.nd.edu wrote:
I set my app up to load images from a cache. If the cache doesnt exists it loads them asynchroniously. Then updates the collectionViewCell. It shouldn't make the tableView lag or jump while scrolling...
I register my CollectionCell XIB in my custom tableViewCell file 'awakeFromNib()' function.
Is this expensive?
I also have to fetch from core data each time a tableViewcell is presented to find out how many collectionView Items i have. See below. Is anything here expensive?
extension NotesTableViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { var photoFilteredIndexRow = Int() if (searchController.active && searchController.searchBar.text !=
"") || searchController.searchBar.text != "" {
photoFilteredIndexRow = filteredNotes[collectionView.tag] } else { photoFilteredIndexRow = collectionView.tag }
// first get the photos for this cell
let noteCollection = notesFRC?.objectAtIndexPath(NSIndexPath(forRow:
collectionView.tag, inSection: 0)) as! Notes
setupPhotosFetchedResultsController(noteCollection) print("We got this many in collection view for row \(
collectionView.tag) : ((photosFRC?.fetchedObjects?.count)!)")
return (photosFRC?.fetchedObjects?.count)! } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) ->
UICollectionViewCell {
var photoFilteredIndexRow = Int() if (searchController.active && searchController.searchBar.text !=
"") || searchController.searchBar.text != "" {
photoFilteredIndexRow = filteredNotes[collectionView.tag] } else { photoFilteredIndexRow = collectionView.tag } let cell = collectionView.dequeueReusableCellWithReuseIdentifier("
(collectionCellIdentifier)", forIndexPath: indexPath) as! CollectionCell
configureTextForCollectionCell(cell, withIndexPath: indexPath,
collectionViewTag: photoFilteredIndexRow)
return cell } func configureTextForCollectionCell(cell: CollectionCell,
withIndexPath indexPath: NSIndexPath, collectionViewTag: Int) {
let noteCollection = notesFRC?.objectAtIndexPath(NSIndexPath(forRow:
collectionViewTag, inSection: 0)) as! Notes
setupPhotosFetchedResultsController(noteCollection) if let photoItem = photosFRC?.objectAtIndexPath(indexPath) as?
Photos {
if let image = imageCache["\(photoItem.content)"] { print("THERE IS A CACHE FOR COLLECTION!!") cell.photoImage.image = image } else { urlString = photoItem.content as String imgURL = NSURL(string: urlString) loadImage(imgURL!, urlString: urlString, indexPath:
indexPath, cell: cell)
} } } func loadImage(imgURL: NSURL, urlString: String, indexPath:
NSIndexPath, cell: CollectionCell) {
print("loading image!!!") let request: NSURLRequest = NSURLRequest(URL: imgURL) let mainQueue = NSOperationQueue.mainQueue() NSURLConnection.sendAsynchronousRequest(request, queue:
mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil { // Convert the downloaded data in to a UIImage object let image = UIImage(data: data!) // Store the image in to our cache self.imageCache[urlString] = image // Update the cell dispatch_async(dispatch_get_main_queue(), { if (self.searchController.active && self.
searchController.searchBar.text != "") || self.searchController.searchBar. text != "" {
for i in 0 ..< self.filteredNotes.count { if self.filteredNotes[i] == indexPath.row { self.filteredIndexPath = NSIndexPath(forRow:
self.filteredNotes[indexPath.row], inSection: 0)
} } } else { self.filteredIndexPath = indexPath } if let cellToUpdate = cell as? CollectionCell { cellToUpdate.photoImage.image = image } }) } }) }
}
On Sat, Aug 6, 2016 at 10:23 AM, Ash Furrow notifications@github.com wrote:
Image views can be tricky, because they use UIImage which does a lot of work under the hood. For example, displaying a JPEG requires it to be decompressed first. That's usually fast enough that it doesn't matter, but showing lots of images or showing large images (or both!) can cause performance issues.
What's the error you're getting?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/issues/24#issuecomment-238028552, or mute the thread https://github.com/notifications/unsubscribe-auth/AFmm9p-tTgptAbnUmvH7Sy90IK_pE4y-ks5qdKbXgaJpZM4JeSjq .
Actually, after messing with it more it seems the below code is holding up the show...
override func tableView(tableView: UITableView,
willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath:
NSIndexPath) {
guard let cell =
tableView.dequeueReusableCellWithIdentifier(notePhotoCollectionCellIdentifier) as? NotePhotoCollectionCell else { return }
print("in willDisplayCell")
cell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
}
override func tableView(tableView: UITableView,
didEndDisplayingCell cell: UITableViewCell,
forRowAtIndexPath
indexPath: NSIndexPath) {
guard let cell =
tableView.dequeueReusableCellWithIdentifier(notePhotoCollectionCellIdentifier) as? NotePhotoCollectionCell else { return }
storedOffsets[indexPath.row] = cell.collectionViewOffset
}
}
The tableView flies smoothly without this code. Only thing is the photos sorta get jumbled up and the collectionView show incorrect number of cells etc.
Any ideas?
Mike
On Sat, Aug 6, 2016 at 5:46 PM, Michael Reilly mreilly4@alumni.nd.edu wrote:
Ash,
It does seem to be the
setCollectionViewDataSourceDelegate
function that is slowing everything down. Do you know where we might be able to call this in the tableView VC so it only happens once?
Thanks!
Mike
On Sat, Aug 6, 2016 at 10:30 AM, Michael Reilly mreilly4@alumni.nd.edu wrote:
I set my app up to load images from a cache. If the cache doesnt exists it loads them asynchroniously. Then updates the collectionViewCell. It shouldn't make the tableView lag or jump while scrolling...
I register my CollectionCell XIB in my custom tableViewCell file 'awakeFromNib()' function.
Is this expensive?
I also have to fetch from core data each time a tableViewcell is presented to find out how many collectionView Items i have. See below. Is anything here expensive?
extension NotesTableViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { var photoFilteredIndexRow = Int() if (searchController.active && searchController.searchBar.text
!= "") || searchController.searchBar.text != "" {
photoFilteredIndexRow = filteredNotes[collectionView.tag] } else { photoFilteredIndexRow = collectionView.tag }
// first get the photos for this cell
let noteCollection = notesFRC?.objectAtIndexPath(NSIndexPath(forRow:
collectionView.tag, inSection: 0)) as! Notes
setupPhotosFetchedResultsController(noteCollection) print("We got this many in collection view for row \(
collectionView.tag) : ((photosFRC?.fetchedObjects?.count)!)")
return (photosFRC?.fetchedObjects?.count)! } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath)
-> UICollectionViewCell {
var photoFilteredIndexRow = Int() if (searchController.active && searchController.searchBar.text
!= "") || searchController.searchBar.text != "" {
photoFilteredIndexRow = filteredNotes[collectionView.tag] } else { photoFilteredIndexRow = collectionView.tag } let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
"(collectionCellIdentifier)", forIndexPath: indexPath) as! CollectionCell
configureTextForCollectionCell(cell, withIndexPath: indexPath,
collectionViewTag: photoFilteredIndexRow)
return cell } func configureTextForCollectionCell(cell: CollectionCell,
withIndexPath indexPath: NSIndexPath, collectionViewTag: Int) {
let noteCollection = notesFRC?.objectAtIndexPath(NSIndexPath(forRow:
collectionViewTag, inSection: 0)) as! Notes
setupPhotosFetchedResultsController(noteCollection) if let photoItem = photosFRC?.objectAtIndexPath(indexPath) as?
Photos {
if let image = imageCache["\(photoItem.content)"] { print("THERE IS A CACHE FOR COLLECTION!!"
)
cell.photoImage.image = image } else { urlString = photoItem.content as String imgURL = NSURL(string: urlString) loadImage(imgURL!, urlString: urlString, indexPath:
indexPath, cell: cell)
} } } func loadImage(imgURL: NSURL, urlString: String, indexPath:
NSIndexPath, cell: CollectionCell) {
print("loading image!!!") let request: NSURLRequest = NSURLRequest(URL: imgURL) let mainQueue = NSOperationQueue.mainQueue() NSURLConnection.sendAsynchronousRequest(request, queue:
mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil { // Convert the downloaded data in to a UIImage object let image = UIImage(data: data!) // Store the image in to our cache self.imageCache[urlString] = image // Update the cell dispatch_async(dispatch_get_main_queue(), { if (self.searchController.active && self.
searchController.searchBar.text != "") || self.searchController.searchBar .text != "" {
for i in 0 ..< self.filteredNotes.count { if self.filteredNotes[i] == indexPath.row { self.filteredIndexPath = NSIndexPath(forRow:
self.filteredNotes[indexPath.row], inSection: 0)
} } } else { self.filteredIndexPath = indexPath } if let cellToUpdate = cell as? CollectionCell { cellToUpdate.photoImage.image = image } }) } }) }
}
On Sat, Aug 6, 2016 at 10:23 AM, Ash Furrow notifications@github.com wrote:
Image views can be tricky, because they use UIImage which does a lot of work under the hood. For example, displaying a JPEG requires it to be decompressed first. That's usually fast enough that it doesn't matter, but showing lots of images or showing large images (or both!) can cause performance issues.
What's the error you're getting?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/issues/24#issuecomment-238028552, or mute the thread https://github.com/notifications/unsubscribe-auth/AFmm9p-tTgptAbnUmvH7Sy90IK_pE4y-ks5qdKbXgaJpZM4JeSjq .
Strange! I'd try commenting only one or the other out, to see if it's setting or getting that's causing the issue. It's not obvious to me why that would be slowing the performance down...
Ash,
So it appears as though setting th offset causes the collectionView in each cell to reloadData. reloadData is also being called. I am trying to find a way to do this as early as possible in the cell's reuse in order to have it reloaded before it appears and hopefully not cause the skipping effect when the cell appears in the TableView. If you have any ideas that would be great.
Hope you had a great weekend.
Thanks,
Mike
On Mon, Aug 8, 2016 at 9:31 AM, Ash Furrow notifications@github.com wrote:
Strange! I'd try commenting only one or the other out, to see if it's setting or getting that's causing the issue. It's not obvious to me why that would be slowing the performance down...
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/issues/24#issuecomment-238255762, or mute the thread https://github.com/notifications/unsubscribe-auth/AFmm9nVngWVRG1MSfQa1QoLVN7RyV0MJks5qdz2qgaJpZM4JeSjq .
Ah, good thinking! Good luck, let me know if I can help :bow:
Hello Ashfurrow, I seem to be having the same problem as mreily4. I have tried a number of debugging and can't seem to find and clues why there are jitters. Have you find any reason why?? Or any way to improve performance
Not sure of the cause, sorry. I don't have a tonne of time to work on this but if you have suggestions or ideas, please follow up :bow:
For me it was constantly fetching from Core Data too. So what I did was cache all my objects and images after the first load. Then checking the cache before initiating a fetch from Core Data each time I loaded cells.
Mike
On Wed, Feb 8, 2017 at 9:36 AM, Ash Furrow notifications@github.com wrote:
Not sure of the cause, sorry. I don't have a tonne of time to work on this but if you have suggestions or ideas, please follow up 🙇
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ashfurrow/Collection-View-in-a-Table-View-Cell/issues/24#issuecomment-278362448, or mute the thread https://github.com/notifications/unsubscribe-auth/AFmm9p6ZqRWYfgfkLOEXXetHa13GLC5mks5raeD1gaJpZM4JeSjq .
I noticed the jitter occurs when a new cell is inserted on the fly whilst scrolling the tableview and as we don't have control of when apple creates/removes a cell this happens quite often.
Currently I have a property (CGPoint *updatedContentOffset) which I observer in my tableView subclass and then in my collectionview subclass inside scrollViewDidScroll delegate I set this property with the latest contentoffset of the scrolling collectionview;
//TableView.m [sharedData addObserver:self forKeyPath:NSStringFromSelector(@selector(updatedContentOffset)) options:NSKeyValueObservingOptionNew context:nil];
//CollectionView.m
(void)scrollViewDidScroll:(UIScrollView *)scrollView{ if (hasBeganScrolling) return;
sharedData.updatedContentOffset = scrollView.contentOffset; }
this triggers the KVO in the tableView subclass where I set the contentOffset of all visible collectionsViews.
This has seemed to reduce the jitter quite significantly, however on first scroll I get the slightest of glitches which goes away on subsequent scrolls.
I believe that might be tableView trying to build up the suitable number if cells for the tableView height.
@mreily4 I cannot cache my data as it sensitive data and changes quite rapidly but I do hold the data in memory (i.e. BST and array)
If you do come across and better solution than mine please update. Thanks again
Anyone came up with a solution? Having same issue. vertical scroll has lag. Thanks
I have managed to get 56 fps on a grid design using these pointers; however as scenarios differ, you bottleneck/issue might not be solved with these so ill suggest profiling your app whilst making changes
not sure I understood the first argument. Can you explain?
In my case I was using a collectionview subclass and observing the contentoffset of the scrolling collectionview and setting other collectionviews accordingly. I noticed that setting the contentoffset of other collectionviews caused jitter so I set their bounds
Instead of: otherCollectionView.contentOffset = scrollingCollectionView.contentOffset
You do: otherCollectiinView.bounds = (CGRect){scrollingCollectionView.contentOffset, .....}
@Auxton great suggestions.
In my case, your tips were not enough. The lag was caused by having cornerRadius
implemented in the storyboard. Core Animation was redrawing the corners on every call of cellFoRowAtIndexPath
. I've made the views in Photoshop and added them as image views and everything works perfect now.
Anythoughts what could be make it smother as it reuses cells?
Thanks!