Add New data from Bottom in TableView - ios

When i add some text in TableView, it will show from Top of the tableview but i want to show it from bottom of tableView.
1. My TableView Screen
When i add user entered text in array and refresh tableView it will load from Top of TableView like below image.
but i want to add it from bottom like below image.
Can you guys help me, how can i achieve this?
Thanks

I am able to bind Data from bottom of TableView. All credit goes to Rajeshkumar R for helping me.
Step 1, apply a transform to the table view rotating it 180deg
tableView.transform = CGAffineTransformMakeRotation(-M_PI);
Step 2, rotate your raw cell 180deg in tableView:cellForRowAtIndexPath:
cell.transform = CGAffineTransformMakeRotation(M_PI);
Step 3, reverse your datasource. If you're using an NSMutableArray insert new objects at location 0 instead of using AddObject...
Now, the hard part is remembering that left is right and right is left only at the table level, so if you use
[tableView insertRowsAtIndexPaths:targetPath withRowAnimation:UITableViewRowAnimationLeft]

you can use the function below. It worked for me.
func scrollToLastRow() {
let indexPath = NSIndexPath(row: messageHistoryArray.count-1, section: 0)
self.createChatTableView.scrollToRow(at: indexPath as IndexPath, at: .bottom, animated: true)
}

Related

Alternatives to tableView.scrollToRow()

Problem
I need to have a UITableView reload with a specific cell positioned at the top of the tableview. For instance, have the 3rd cell in the tableView appear at the top of the tableView, rather than the 1st.
The issue in doing this comes from not being able to use the
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
function, because I have a custom header that expands and collapse when the user scrolls. That means when I call the scrollToRow() function, my header collapses, messing things up.
Question
Is there any way to have a tableView open/reload with the 3rd cell positioned at the top of the tableView without using a "scroll" function?
Thanks!
You can follow below steps to fulfill your requirement:
Identify NSIndexPath of one of the visible cells.
Get its rectForRowAtIndexPath.
Get the current contentOffset of the table, itself.
Or, you can use scrollRectToVisible
This is how we use contentOffset:
mainTableView.setContentOffset(rectForRowAtIndexPath.point, animated: true)
And scrollRectToVisible:
mainTableView.scrollRectToVisible(rectForRowAtIndexPath.point, animated: true)

UICollectionView cell scrolling issue in ios

On reloading the UICollectionView some time CollectionView cell does not bounce back. see the image collectionCell with title "Bgrawal ji Sourabh" should not be at this much distance from right side.It should have bounce.
Try this:
[self.collectionView scrollToItemAtIndexPath:yourDesiredIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
Please add below code before when your reload your collectionView
let indexes = NSIndexPath(forItem: 0, inSection: 0)
self.collectionViewSecond.scrollToItemAtIndexPath(indexes, atScrollPosition: .None, animated: false)
It's work for me hope work for you also!
Make sure you have enabled these two option in the CollectionView attribute Inspector i.e. Bounce on Scroll and Bounce on zoom this will allow the collectionView to give a bounce feed back on the scroll.

Maintain UITableView cell position with dynamic height cells after reloading source data iOS

In chat application, I am implementing Load More button on top of chat table view.
When user taps load more button, older messages (plus all the current messages which are present on screen) get fetched from DB (using NSFetchedResultsController) and then I am calling reloadData() method on chat table view.
I want to set the scroll position of tableview to the row which was top row before reloading table.
All the message cells are dynamic in height according to the message text.
I am using auto layout on storyboard.
My code:
let oldContentheight = _chatTableView.contentSize.height
_chatTableView.reloadData()
executeOnMain {
if self.firstTimeFetching {
self.firstTimeFetching = false
self.scrollTableToLastRow(withAnimation: false)
//this is for going to last cell when we come to chat screen
}
else {
//after pressing load more button
let newContentHeight = self._chatTableView.contentSize.height
let newOffset = CGPointMake(0, newContentHeight - oldContentheight)
self._chatTableView.setContentOffset(newOffset, animated: false)
}
}
First, declare property to save the indexpath of top visible cell
#property(strong,nonatomic) *topVisibleIndexPath;
Then when you click the Load More button, you get the top visible cell IndexPath and save
NSArray *visibelIndexpaths = [yourTableView.indexPathsForVisibleRows];
if(visibelIndexpaths.count > 0)
self.topVisibleIndexPath = [visibelIndexpaths firstObject];
After reload, you call method :
[yourTableView scrollToRowAtIndexPath:self.topVisibleIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES]
Instead of using reloadData, consider doing batch updates. reloadData will load up new elements from your dataSource, but it will also have a side effect of resetting your scrollView's scroll position to the top.
What you want is to use this pattern:
tableView.beginUpdates()
tableView.insertRowsAtIndexPaths([/* new rows here as an array of NSIndexPaths */], withRowAnimation: .Automatic)
tableView.endUpdates()
where the insertRowsAtIndexPaths contain the new rows you want to add.

Insert rows at top of UITableView without modifying scroll position when using UITableViewAutomaticDimension

How can one insert rows into a UITableView at the very top without causing the rest of the cells to be pushed down - so the scroll position does not appear to change at all?
Just like Facebook and Twitter when new posts are delivered, they are inserted at the top but the scroll position remains fixed.
My question is similar to this this question. What makes my question unique from that question is that I'm not using a table with fixed row heights - I'm using UITableViewAutomaticDimension and an estimatedRowHeight. Therefore the answers suggested there will not work because I cannot determine the row height.
I have tried this solution that doesn't involve taking row height into consideration, but the contentSize is still not correct after reloading, because the contentOffset set isn't the same relative position - the cells are still pushed down past where they were before the insert. This is because the cell hasn't been rendered on screen so iOS doesn't bother to calculate the appropriate height for it until it's about to appear, therefore contentSize is not accurate.
CGSize beforeContentSize = tableView.contentSize;
[tableView reloadData];
CGSize afterContentSize = tableView.contentSize;
CGPoint afterContentOffset = tableView.contentOffset;
tableView.contentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + afterContentSize.height - beforeContentSize.height);
Alain pointed out rectForCellAtIndexPath which forces iOS to calculate the appropriate height. I can now determine the proper height for the inserted cells, but the scroll view's contentSize is still not correct, as is evidenced when I iterate over all cells and add up the heights which is a larger than contentSize.height. So ultimately when I set the contentOffset manually it's not scrolling to the correct location.
Original code:
//update data source
[tableView beginUpdates];
[tableView insertRowsAtIndexPaths:newIndexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
In this scenario, what can be done to achieve the desired behavior?
Late to the party but this works even when cell have dynamic heights (a.k.a. UITableViewAutomaticDimension), no need to iterate over cells to calculate their size, but works only when items are added at the very beginning of the tableView and there is no header, with a little bit of math it's probably possible to adapt this to every situation:
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
self.getMoreMessages()
}
}
private func getMoreMessages(){
var initialOffset = self.tableView.contentOffset.y
self.tableView.reloadData()
//#numberOfCellsAdded: number of items added at top of the table
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: numberOfCellsAdded, inSection: 0), atScrollPosition: .Top, animated: false)
self.tableView.contentOffset.y += initialOffset
}
Also, single row sections can accomplish desired effect
If a section already exists at the specified index location, it is moved down one index location.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/index.html#//apple_ref/occ/instm/UITableView/insertSections:withRowAnimation:
let idxSet = NSIndexSet(index: 0)
self.verseTable.insertSections(idxSet, withRowAnimation: .Fade)
I think your solution was almost there. You should have based the new Y offset on the "before" Y offset rather than the "after".
..., beforeContentOffset.y + afterContentSize.height - beforeContentSize.height
(you would need to save the beforeContentOffset point before the reload data though)
Meet the same problem and still do not find a elegant way to solve it.Here is my way,suppose I want to insert moreData.count cells above the current cell.
// insert the new data to self.data
for (NSInteger i = moreData.count-1; i >= 0; i--) {
[self.data insertObject:moreData[i] atIndex:0];
}
//reload data or insert row at indexPaths
[self.tableView reloadData];
//after 0.1s invoke scrollToRowAtIndexPath:atScrollPosition:animated:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:moreData.count inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
});
The advantage is fixed the position before and after inserting new data.The disadvantage is we can see the screen flash (the topmost cell shows and move up)
My working solution for iOS 13 & Swift 5:
Note: Only tested with dynamic cell heights (UITableViewAutomaticDimension / UITableView. automaticDimension).
P.S. None of the solutions posted here did work for me.
extension UITableView {
/**
Method to use whenever new items should be inserted at the top of the table view.
The table view maintains its scroll position using this method.
- warning: Make sure your data model contains the correct count of items before invoking this method.
- parameter itemCount: The count of items that should be added at the top of the table view.
- note: Works with `UITableViewAutomaticDimension`.
- links: https://bluelemonbits.com/2018/08/26/inserting-cells-at-the-top-of-a-uitableview-with-no-scrolling/
*/
func insertItemsAtTopWithFixedPosition(_ itemCount: Int) {
layoutIfNeeded() // makes sure layout is set correctly.
var initialContentOffSet = contentOffset.y
// If offset is less than 0 due to refresh up gesture, assume 0.
if initialContentOffSet < 0 {
initialContentOffSet = 0
}
// Reload, scroll and set offset:
reloadData()
scrollToRow(
at: IndexPath(row: itemCount, section: 0),
at: .top,
animated: false)
contentOffset.y += initialContentOffSet
}
}

Align row middle with UITableView middle

I have a UITableView that should show a particular row in its middle when it is first displayed. In viewDidLoad, I use scrollToRowAtIndexPath to achieve this:
// Center vertically on current row
let scrollIndexPath = NSIndexPath(forRow: currentRow, inSection: 0)
tableView.scrollToRowAtIndexPath(scrollIndexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: false)
This makes the top of the current row aligned with the middle of the table view meaning that the content of the row looks lower than it should be. How can I make the middle of the row align with the middle of the table view?
UITableView is a subclass of UIScrollView so you can use:
scrollRectToVisible:<#(CGRect)#> animated:<#(BOOL)#>
and calculate CGRect yourself, this way you have way more control than with using:
scrollToRowAtIndexPath

Resources