Collection View inside Table View Cell Slow Scroll Performance - ios

I have checked, tried many solutions on stackoverflow and from many website I searched.
My problem: I have a collection view inside table view cell. My data must be loaded 1 time in table view ( viewdidload).
When I pass data to collection view inside table cells, it's not appear because I need to reload collection view again.
So I do: Data -> inside cellforrow of table view -> Pass to collection view -> cell.collectionView.reloadData()
This cause a problem about scroll performance: collectionView.reloadData() call over and over again when I scroll ( this right because of resuable cells architecture of table view.
Here my code in cellforrow of tableView:
let cell = tableView.dequeueReusableCell(withIdentifier: SubCategoryCellID) as! SubCategoryCell
if isFetched {
cell.shimmeringView.isShimmering = false
cell.shimmeringImageView.isHidden = true
cell.shimmeringView.isHidden = true
cell.listLocation = nestedSubLocation[indexPath.item]
cell.collectionView.reloadData()
}else {
return cell
}
Solutions I tried:
+ Create static cells and get cells to display in cellForRow (it's totally fix scroll performance but I think it not good for memory management and it's not working if parent is collection view).
+ Reload data when scroll stop: it's still need to reloadData in loading first time and wrong data if I dont reload again.
To fix that, anyone have an clever approach to fix scroll performance ?

Put this code after cell.collectionView.reloadData()
cell.collectionView.scrollRectToVisible(CGRect.zero, animated: false)

Related

Is there any way to scroll to the selected UICollectionViewCell in a collection?

I've got a UICollectionView in a modal view controller in my app. When the modal view is brought up, one of the collection's cells is set as selected based on certain data I pass into the modal view from my home view.
I need to programmatically scroll the selected collection cell into view once the modal view with the collection appears... but using scrollToItem(at:at:animated:) while the collection view is being populated (in cellForItemAt) doesn't seem to work.
So while I could easily just use the indexPath available to me in cellForItemAt to scroll to the selected cell while populating the collection, since that isn't possible, I can't figure out how to scroll to the currently selected collection cell after the collection is fully populated and presented.
I can't even use a heavy-handed approach like looping through all collection cells and checking which is selected manually, since it doesn't appear possible to loop through cells that aren't currently visible.
Help?
Add a variable on Top.
private var didLayoutFlag: Bool = false
Just pass the index number (indexNO) in viewDidLayoutSubviews method. I am using horizontal scrolling.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if self.colllecitonView != nil {
if !self.didLayoutFlag {
self.colllecitonView.scrollToItem(at:IndexPath(item: indexNO, section:0), at:.right, animated: false)
self.didLayoutFlag = true
}
}
}

Simultaneously change display parameters on all table view cells

I am trying to implement a table view design where a user can click a button outside of a table view cell and the display mode of all the buttons should change. However this is not the 'selected' mode for a given cell (that will be yet a third state that becomes accessible via switching to this second state). What's the proper way to accomplish this?
I am using dequeueReusableCellWith so I don't want to simply cycle through every cell because some that are out of sight probably shouldn't be modified. I simply want any cell that is visible, or becomes visible, while the table view cell is in this second display mode to follow a second design rather than the first design.
The second design, for now, is being modified via a method I added to a subclass of UITableViewCell like so:
- (void) p_refreshDisplay {
if (self.editing) {
self.buttonToClearWidth.constant = 20;
self.buttonToClearLeadingWidth.constant = 20;
} else {
self.buttonToClearWidth.constant = 0;
self.buttonToClearLeadingWidth.constant = 0;
}
}
However, I'm not sure how to trigger this p_refreshDisplay for every visible (and to become visible) cell. It seems unwise to call this many times and refresh the table view. What would be the proper way to accomplish what I want to do?
You do what should be done for any table view change:
Update your data model or some flag as needed.
Either call reloadData on the table view or call reloadRowsAtIndexPaths:withRowAnimation: passing in indexPathsForVisibleRows as the list of rows to reload.
Implement cellForRowAtIndexPath to provide appropriate cells for the given data/flags.
It sounds like you should have a custom cell class that has one or more properties that can be set on the cell in cellForRowAtIndexPath so the cell can render itself properly based on the specified state.
You can achieve this by doing three things:
Establish some property that indicates the "mode" of the table, either a boolean or perhaps an enum if there are more than three states
Ensure that cellForRowAtIndexPath configures the cell appropriately based on the value of this property. This will ensure that newly displayed cells are configured correctly.
When the "mode" changes you can use the tableview's visibleCells property to update any currently visible cells:
for cell in tableview.visibleCells {
if let myCell = cell as? MyCustomCellClass {
myCell.setButtonStyle()
}
}

iOS tableview cell is empty at random

Screenshot of weird behavior
The screenshot tells is quite well. I have a tableview with dynamic custom cells. I added a println for one of the contents of the cell to check, if the labels are set. I can see in the debug log, that each cell has its content. Still, on the device there are empty cells at random, which means, the row, where no content appears, is changing a lot. Even just scrolling up and down makes the second row disappear, but the third row is filled. Scrolling again turns this around again. If I close the app and start it again, every row is filled correctly.
Here is the code for the cell generation:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Return a count picker cell
if countPickerTableRow == indexPath.row {
...
}
// Return a normal wish list entry cell
else {
let article = wishListEntries[indexPath.row]!
let cell = tableView.dequeueReusableCellWithIdentifier("ArticleCell", forIndexPath: indexPath) as! WOSArticleCell
// Correct the order in case a count picker cell was inserted
var row = indexPath.row
if countPickerTableRow != -1 && indexPath.row > countPickerTableRow {
row--
}
cell.setThePreviewImage(UIImage(data: article.thumbnail))
cell.setArticleName(article.name)
cell.setArticleDescription(article.text)
cell.setArticleNumber(article.number)
cell.setArticleCount(article.count as Int)
cell.setOrderInTable(row)
cell.setTableViewController(self)
cell.setNeedsDisplay()
cell.setInputAccessoryView(numberToolbar) // do it for every relevant textfield if there are more than one
println(String(indexPath.row) + " " + cell.nameLabel.text!)
return cell
}
}
In the custom cell class there is nothing special. Just a few outlets to the labels.
Here is a screen of the storyboard:
Storyboard
Can anyone please help me finding out what is going on here? I can't find the reason why the debug log can output the contents of a cell, but the device is not able to render them.
You should change the logic of your code. If the PickerCell comes up just call reloadData() and reload everything in the tableview. If the amount of rows you have is small this won’t be an issue and it’s not an expensive operation as you are not doing any heavy calculating during display.
If you need to update only a single cell because of changes you made in the PickerCell then you should be calling reloadRowsAtIndexPaths:withRowAnimation: with the indexPath of the cell to be updated.
Your issue is with your subclass WOSArticleCell. Have you implemented prepareForUse()? If you have, are you setting any properties to nil?
UITableViewCell Class Reference
Discussion
If a UITableViewCell object is reusable—that is, it has a reuse
identifier—this method is invoked just before the object is returned
from the UITableView method dequeueReusableCellWithIdentifier:. 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:cellForRowAtIndexPath:
should always reset all content when reusing a cell. If the cell
object does not have an associated reuse identifier, this method is
not called. If you override this method, you must be sure to invoke
the superclass implementation.

How to set the topmost cell (first row) of detail view's table view with master view's selected cell content?

Working on a social iPhone app using Swift (with a Storyboard) and Parse where users can create posts and comment on posts similar to the Facebook iOS app and other social network apps.
The app has an initial, master Home Feed page (which displays user posts) and a detail Reply page (which displays user comments). Both use the PFTableViewController class and each of those table views have their own PFTableViewCell implemented in separate swift files as the prototype cells.
I am trying to send the content (username, post message, timestamp, etc) of a selected post cell from the master Home Feed page to the detail Reply page's topmost cell (the first cell at the top of the table view).
My goal is to have it work similar to the way the Facebook, Instagram, and Twitter iOS apps have it - whenever a user selects a post in the main feed/timeline, the detail view is shown where the main post is at the top (but not pinned to the top - meaning it will scroll up/down with the tableview) and its comments are in the cells beneath it.
I appreciate your help and time! Thank you!
On Selection on left panel, you can make tableview on right panel scroll to top animated false.
After further research, one way to "hack" it is by doing something similar to the following:
1) Create a custom tableview cell for the top row (topmost cell) by implementing it in a separate Swift file, just as you may have done for the prototype cell.
2) Now you have two custom cells. So implement two function where in each you customize them the way that serves your apps requirements, etc and the cell.
3) In the cellForRowAtIndexPath method, use an if/else statement to return the appropriate tableview cell for row 0 (topmost/header cell), otherwise return the other prototype cell for any other row (i.e.: the cells beneath the header cell):
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
return headerCellAtIndexPath()
} else {
return commentCellAtIndexPath(indexPath)
}
}
func headerCellAtIndexPath() -> CommentsHeaderTableViewCell {
let headerCell = tableView.dequeueReusableCellWithIdentifier("topCell") as! CommentsHeaderTableViewCell
// Customize your headerCell...
return headerCell
}
func commentCellAtIndexPath(indexPath:NSIndexPath ) -> CommentTableViewCell {
let commentCell = tableView.dequeueReusableCellWithIdentifier("commentCell", forIndexPath: indexPath) as! CommentTableViewCell
// Customize your commentCell...
return commentCell
}

How to build swipe view controller in swift

I have a UITableViewController and when I click on it, it will show a DetailViewUIController.
I want to add a functionality which when I am in DetailViewUIController and I swipe to right, it will show the DetailViewUIController of next item and when left, it will show the previous item.
I find a link which kind of do that in swift. But it only has 3 static subviewcontroller.
https://medium.com/swift-programming/ios-swipe-view-with-swift-44fa83a2e078
The number of entries in my TableView can be pretty long, how can I do the same thing dynamically, i.e. without having static number of subviewcontroller created and add as 'addChildViewController'?
Update:
Thanks again #rdelmar for your help.
I am having trouble getting the ' set the contentOffset of the collection view to (collection view width) * selectedRow' to work.
In my prepareForSegue function, I have added:
x = Float(indexPath.row) * 375.0
var point = CGPointMake(CGFloat(x), 0)
println ("***x=")
println (x)
detailController.collectionView?.setContentOffset(point , animated: false)
where detailController is UICollectionViewController.
and in the
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as DetailViewCell
// Configure the cell
println("DetailViewController in cellForItemAtIndexPath()")
println(indexPath.row)
cell.myitem = allItems[indexPath.row]
return cell
}
And I always see cellForItemAtIndexPath trying to get 0th element of the allItems (that is the whole collections of all my objects.
So setContentOffset() does not change what I am displaying in the Detail View regardless which item I click in my TableView to launch the Detail View.
Any idea to solve this?
Thank you.
There are a lot of different ways you could do this. One way would be to make your detail view controller's view be a collection view (with paging enabled), whose cells are the same size as the screen. You would need to pass in the array that you use to populate your table so the collection view could populate its cells. This would be quite efficient, since the collection view would only ever need to instantiate two cells.

Resources