ScrollToRowAtIndexPath takes longer to scroll because of UITableViewAutomaticDimension - ios

Im trying to build a scroll within 977 strings of data in the array placed in UITableView , well I'm using the following to do automatic calculation for the
cell height : mytableView.rowHeight = UITableViewAutomaticDimension.
And using the following to scroll to index:
self.mytableView.scrollToRowAtIndexPath(NSIndexPath(forItem: placetogo, inSection: 0), atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
Whats happening here is the scroll is taking 16 seconds to scroll to that last index of the array which will be very annoying for the user, when i made the rowHeight = 400 or 300 static value , then it scroll with less than a second to last index.
My cells are different in there heights, so i cannot use the static cell height. I've made a research about it that tableview calls rowHeight frequently to calculate the height.which makes the scroll very slow, is there is any efficient way for that ?
I tried to split my data into 9 or 10 sections but still the same!

Related

UICollectionView ScrollToItem With Insets

I have a horizontally scrolling uicollectionview with insets I am setting through the uicollectionviewdelegateflowlayout method insetForSectionAt.
In trying to use the method .scrollToItem the scroll doesn't seem to take into account of the inset and only scrolls a part of the way.
Is there a suggested approach to account for the inset and have the scroll go all the way? It is just one section and I am trying to scroll from item 0 to item 1.
Another approach is to manually modify content offset based on a custom calculation.
let customPoint = CGPoint(x: customX, y: customY)
collectionView.setContentOffset(customPoint, animated: true)
Where customX and customY are calculated based on cell size and cell count + other factors such as insetForSection.

Swift Dynamically growing a cell depending on the text length

I have a TableViewController which contains 2 sections:
Section 1 - is a cell which is loaded from a xib file. This cell just contains a TextView.
Section 2 - Contains multiple cells populated from an Array.
The section 1 only exists if the master (previous) UITableView cell you select contains a certain piece of data.
All of the above works as expected, below is the parent view. The list of items come from a database, some items have a description, and some do not. For example below this image, you'll see the view is 'Classic Starters' is selected. Then below that, you'll see the view if 'Stir Frys' is selected. Stri Frys contains a description:
Now, what I want is, the description cell which is shows on the Stir Frys page, to automatically grow depending on the length of the text inside it. So if a description is 10 lines long, it will grow to show all 10 lines.
Does this have to be done programmatically, or is their a feature in XCode I'm missing ?
You can use UITableViewAutomaticDimension.
1)
Set properties estimatedRowHeight and rowHeight of your tableview, in viewDidLoad for example. Like this :
self.tableView.estimatedRowHeight = 50
self.tableView.rowHeight = UITableViewAutomaticDimension
2) Return UITableViewAutomaticDimension in your heightForRowAt delegate method :
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
First you need to do is to set estimatedRowSize to a value that best estimates most common size, and rowSize to UITableViewAutomaticDimension:
self.tableView.estimatedRowHeight = 44
self.tableView.rowHeight = UITableViewAutomaticDimension
In your case, since except the first one all the other cells are supposed to be the same, you can use the height of the rest of the cells as the estimatedRowHeight.
You don't have to implement heightForRowAt at all.
The second step you need to do is to setup proper constraints on the cells. That means you have to constrain the contents of the cell to the left, right, top and bottom of the cell, so that when contents grow, the cell will need to grow, too. Common mistake is to forget to constrain bottom, so then the cell does not grow and the contents leak through the bottom of the cell.
Third, since your dynamic cell contains UITextView, you need to make sure that it will grow with its text. That is not automatic. To achieve that, based on this answer, this should suffice (in the cell):
textView.isScrollEnabled = false
If you are using storyboard, just uncheck scroll enabled.

UICollectionView stick to a cell even if it's content offset may change

This is a hard one, I hope I can explain it properly.
All right, so the thing is that I have a UICollectionView where the height of the cell is dynamically changing using collectionView.collectionViewLayout.invalidateLayout() which guides into the sizeForItemAtIndexPath() method of UICollectionVieFlowLayout.
This works perfectly. There is new content, it resizes, moves the other cells according to it, etc. Now, my collection view's content size is probably pretty big, that means when I'm stopping at cell number, say, 23, and cell number 15 is not even displayed but is changing it's size, it is going to make my cell number 23 move up or down. Can I kind of throw an "anchor" to where I currently am? Here's the code that's resizing the cell, triggered by Firebase:
cell.resizeToText(text: text!)
let size = CGSize(width: self.view.frame.width, height: cell.mainView.frame.height + (cell.mainView.frame.origin.y*2.5))
let invalidationIndex = indexPath.row + 1
self.invalidationSizeDictionary[invalidationIndex] = size
let context = UICollectionViewLayoutInvalidationContext()
context.invalidateItems(at: [indexPath])
self.feedCollectionView.collectionViewLayout.invalidateLayout()
Question: is there a possibility to stay at my current offset position in the collectionView?

UITableView load more data when scrolling to top?

How could I load more data while scrolling to the top without losing the current offset oh the UITableView?
Here is what I am trying to achieve:
This is the whole set of data:
row 1
row 2
row 3
row 4
row 5
row 6
row 7
row 8*
row 9
row 10
row 11
row 12
row 13
row 14
row 15
Now, imagine that the user loaded the ones marked in bold and the offset is at row 8, if the user scroll up and reached row 7, I want to load and insert the rows from 1 to 5 without jumping from row 7. Keeping in mind that the user may be scrolling so when data reached the phone it is at row 6, so I can't jump it back to row 7, but keep the scroll smooth and natural (just how happen when you load more data while scrolling down, that the data is reloaded without the tableview jumping from between rows).
By the way, by offset I mean the contentOffset property of the UITableView.
Thanks for your help, I do really appreciate it!
When you are updating your data, you need to get the current offset of the tableView first. Then, you can add the height of the added data to the offset and set the tableView's offset like so:
func updateWithContentOffsset(data: [String]) {
guard let tableView = tableView else {
return
}
let currentOffset = tableView.contentOffset
let yOffset = CGFloat(data.count) * tableView.rowHeight // MAKE SURE YOU SET THE ROW HEIGHT OTHERWISE IT WILL BE ZERO!!!
let newOffset = CGPoint(x: currentOffset.x, y: currentOffset.y + yOffset)
tableView.reloadData()
tableView.setContentOffset(newOffset, animated: false)
}
You can also take a look at the gist that I created. Simply open up a new iOS Playground and copy-paste the gist.
The only thing you have to be aware of is that make sure you know your row height to add to the offset.
#Pratik Patel Bellow answer is best one.
Right down or call bellow function in the web-service response.
func updateWithScroll(data: [String]) {
guard let tableView = tableView else {
return
}
guard let currentVisibleIndexPaths = tableView.indexPathsForVisibleRows else {
return
}
var updatedVisibleIndexPaths = [IndexPath]()
for indexPath in currentVisibleIndexPaths {
let newIndexPath = IndexPath(row: indexPath.row + data.count, section: indexPath.section)
updatedVisibleIndexPaths.append(newIndexPath)
}
tableView.reloadData()
tableView.scrollToRow(at: updatedVisibleIndexPaths[0], at: .top, animated: false)
}
Now, imagine that the user loaded the ones marked in bold and the offset is at row 8, if the user scroll up and reached row 7, I want to load and insert the rows from 1 to 5 without jumping from row 7.
Loading data into the table as its needed is one of the things that UITableView does for you automatically. Don't try to insert rows into the table because they'll soon be needed as the user scrolls toward them -- the table view will request those rows from its data source as they're needed. You just need to make sure that your data source has the information it needs in order to fulfill the tables requests for cells as they arrive.
Things get a little more complicated if populating the rows requires making a network request for each row. Given your use of "load" in the question, I don't think that's what you're talking about, but in case that's your situation, here are some tips:
Request the data for as many rows as you reasonable can as early as you reasonably can. The amount of data displayed in a single row is typically small, so requesting a few hundred rows all at once shouldn't be a big deal.
If you don't have the data you need for a given row, make the necessary request, but return a cell immediately. The cell you return could use a spinner or other indication that the data is pending. When the request completes, you can tell the table to reload the appropriate row so that the proper content will display.

Insert in a tableView set to Autoresize cause a scroll issue

I've an issue for few days and really I can't explain why it goes like that.
I'm doing a chat, set up with a tableView, printing message into cell. These cells are designed with prototypes, there are 3 different type (but anyway it doesn't matter). Text is typed, message is send, cell is inserted in table and then we scroll to the bottom of the tableView.
As you know, in a chat view the container of the message has to fit this text (which is a view), and then the cell has to fit to this container (Label in orange, container in purple).
This container has a variable height and grow along the text, changing cell height.
I've set many constraint for auto-layout display but I didn't have different cell height than the height I initially set for it in the project (no adaptative behaviour). Chat Message were cut.
So, I've tried to set each rowHeight by myself using the method heightForRowAtIndexPath, calculating constraint along text size but it create a real bad behaviour (changing cells when scrolling for example). The size was often wrong calculated/recalculated.
That's why I'm finally using estimatedRowHeight set to 44 combine with UITableViewAutomaticDimension. Here it does the trick ! Waouh ! But it's not as good as expected..
When the table view appears all is good. Containers fit to their label, rows' height fit to their container and it's beautiful. The problem appears during an insert.
After many tests, I notice that this bad behaviour only appears when multilines label remains in the table view.
During an insert, each cell seems to resize to 44 before adapt its height to content, and then create a gap compared to previous state which create a strange scroll of the table view :
If I change the estimatedRowHeight it made this worst.
I can show my code if you want, but I don't think it will be very useful here because I'm not using complicated function.. only insert, automatic height for cells and scroll down which are functions delegate for tableView.
Can you help me please ? Really I don't know how to change that.. every chat application does the trick but I can't found out the way.
Thank you for answer, excuse my english level I'm a poor french student..
If you need some code comment I'll give it.
I think you can cache height value for every cell in cellForRowAtIndex method and in HeightForRowAtIndex set the corresponding height for that cell.
You can just do this way :
var cellCached = Dictionary<Int,AnyObject>()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
table.rowHeight = yourCell.yourLabel.frame.origin.y + yourCell.yourLabel.frame.height + 10
cellCached[indexPath.row] = yourTable.rowHeight
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(tableView == yourTable)
{
if(cellCached[indexPath.row] != nil)
{
return cellCached[indexPath.row] as! CGFloat
}
else
{
return 200
}
}
else
{
return UITableViewAutomaticDimension
}
}

Resources