Table View Scrolling Issue with content offset - ios

I am trying to scroll my table view to show my last cell when keyboard is active.
This is what i am doing in tableView:cellForRowAtIndexPath:...
if (keyboard) {
CGFloat calculatedPosY = 70 * ([Array count]-1);
MyTable.contentOffset = CGPointMake(0.0, calculatedPosY);
}
its working correctly for first time but for second time when its reloading table its not scrolling. For third time its again scrolling and showing last cell of table. Alternatively the code is working and the log gives the same content offset (0,280).
Please tell were I am doing wrong.
Thanks in advance.

You need to do two things - 1) make sure the table view fits on the visible part of the screen not covered by the keyboard and 2) scroll the table to the last row.
To resize the view, I'd register to detect the keyboard appearing:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector (keyBoardWillShow:)
name: UIKeyboardWillShowNotification object: nil];
The keyBoardWillShow: method would be called when the keyboard appears and you can resize the tableview:
- (void)keyBoardWillShow:(NSNotification *)aNotification
{
NSValue *value = [[aNotification userInfo] objectForKey: UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [value CGRectValue];
CGFrame tableFrame = self.tableView.frame;
tableFrame.height -= keyboardRect.size.height
self.tableView.frame = tableFrame;
}
Lastly, scroll to the last cell (you could do this in keyBoardWillShow: if you like):
[tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:lastRowIndex inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];

Don't do it in tableView:cellForRowAtIndexPath:.. this method iterates through all array elements and creates cell. It means this calculation runs array[N] times.
Instead run it once after [self.tableView reloadData];
Or try this:
NSInteger *cells_count // You can have it from - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section.
//Run this after [tableView reloadData]
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:cells_count-1 inSection:0];
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated: NO];

I would suggest you use – scrollToRowAtIndexPath:atScrollPosition:animated:. Its a method in the UITableView.

Related

How to make sure changing collectionview cells appear at the center of the phone screen?

Scenario: I have a collection view that is reloaded every 15 seconds. There is no limit to the amount of cells that could be in this collection view, however, only one cell is highlighted at a time. There is only one section, and the collectionview scrolls horizontally. I need to make sure the highlighted cell is always in the center of the phone screen. For example, if the 24th cell is highlighted, it would be a bad user experience to have to scroll all the way until you find it. However, a completely different cell could be highlighted when the collection view gets reloaded in another 15 seconds.
See bottom portion of the image for a better idea of highlighted and unhighlighted cells.
Here's an idea of what I've tried that pertains to the index path of the highlighted cell and making sure it's in the center of the phone screen.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
EZPlayerCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"PlayerCellID" forIndexPath:indexPath];
NSDictionary *rowData = [[_gameBoardDictionary objectForKey:#"players"] objectAtIndex:indexPath.row];
if ([[rowData objectForKey:#"possession"] integerValue] == 1) {
cell.isHighlighted = YES;
} else {
cell.isHighlighted = NO;
}
if (cell.isHighlighted) {
self.highlightedCellIndexPath = [self.collectionView indexPathForCell:cell];
} else {
}
return cell;
}
- (void)viewDidLayoutSubviews {
[self.collectionView scrollToItemAtIndexPath:self.highlightedCellIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
I've tried a number of things, but this should provide a reference for my train of thought. Any help would be appreciated. Thank you so much!
Please add a observer for the "contentSize" keypath of the collection view.
[self.collectionView addObserver:self forKeyPath:#"contentSize" options:NSKeyValueObservingOptionOld context:NULL];
You can then move the scrolling code inside the observeValueForKeyPath method.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)changeDict context:(void *)context
{
[self.collectionView scrollToItemAtIndexPath:self.highlightedCellIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
Here's what solved the issue for me. Instead of trying to get the indexPath in cellForItemAtIndexPath, I got the indexPath based on my data and then scrolled to the cell at that indexPath.
NSArray *players = [_gameBoardDictionary objectForKey:#"players"];
self.itemNumber = [players indexOfObjectPassingTest:
^BOOL(NSDictionary *dict, NSUInteger idx, BOOL *stop)
{
return [[dict objectForKey:#"possession"] isEqual:#1];
}
];
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:self.itemNumber inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];

Scroll collection view with button action

I am having a collection view which shows 3 cells as visible. when i click the right arrow button, It should scroll to show the next 3 cells and when i click the left arrow button, It should scroll to show the previous 3 cells. In case if there is 2 cells behind, it should show those 2 cells with previous last visible cell.
can someone help with it?
You just need to do
[self.collectionView scrollToItemAtIndexPath:nextItem atScrollPosition:UICollectionViewScrollPositionBottom animated:YES];
where you need to pass indexpath for next item you want to scroll
I have found the solution.
- (void)viewDidLoad
{
oldrow=0;
}
- (IBAction)right_button:(id)sender
{
NSIndexPath *newIndexPath;
if (xr-1>0) {
oldsection = 0;
newIndexPath = [NSIndexPath indexPathForRow:oldrow+3 inSection:0];
oldrow=oldrow+3;
[collection_vw scrollToItemAtIndexPath:newIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
xr--;
xl++;
}
else if (y>0)
{
[_btn_right setHidden:YES];
oldsection = 0;
newIndexPath = [NSIndexPath indexPathForRow:array_stories.count-3 inSection:0];
oldrow=array_stories.count-3;
[collection_vw scrollToItemAtIndexPath:newIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}
}
where
xr=array_stories.count/3;
xl=0;
y=array_stories.count%3;

Behavior when inserting a row to a UITableView

I am making a chat application. When the keyboard appears, my UITableView and toolbar (with textfield and button) move up using the setFrame method.
The problem occurs when I am receiving a message from another user while I'm typing a message. When that happens (i.e. after an insertRowsAtIndexPaths to the UITableView), my UITableView and toolbar "reset" to their original positions.
Is this normal behavior caused by the system? I can't find anything in my code that would do this. I want that the toolbar remains visible so I can still type my message.
This is the code to add a convoItem (conversation item) to my array and UITableView:
- (void)insertConvoItem:(ConvoItem *)item
{
[_convoItems addObject:item];
// Update the table view
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:([self.convoItems count] - 1) inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
// Scroll to the bottom so we focus on the latest message
NSUInteger numberOfRows = [self.tableView numberOfRowsInSection:0];
if (numberOfRows) {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:(numberOfRows - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
It's normal. Receiving new message resets your table view to it's original position.

Select the newly added dynamic row in UITableView

I am working with a MasterDetail View using SplitViewController in iPad, My Master View has a tableView where I add rows dynamically. So the newly added row sits at zero postion. So what I want to achieve is that, I want to select the newly added row as soon as it is added to the Master table. Later when the user adds a new row, that row should be selected and the previously added row should be deselected.
For this I have written the code below
- (void)selectNewlyAddedRow
{
NSIndexPath* selectedCellIndexPath= [NSIndexPath indexPathForRow:0 inSection:0];
[m_tableView selectRowAtIndexPath:selectedCellIndexPath animated:TRUE scrollPosition:UITableViewScrollPositionNone];
[self tableView:m_tableView didSelectRowAtIndexPath:selectedCellIndexPath];
}
This code works if I write it in function ViewDidAppear, but if I write it in cellForRowAtIndexPath it doesnt work..
Please provide me correct understanding where I am going wrong.
Regards
Ranjit
viewDidAppear wont get call when you add new row so you just register for table reloaded notification and in method for that notification call select first row I hope it will help you....
- (void)selectFirstRow{
if ([self.tableView numberOfSections] > 0 && [self.tableView numberOfRowsInSection:0] > 0) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionTop];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}
}
Implement this delegate -
-(void)tableView:(UITableView )tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath)indexPath{
[NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:#"Table Reloaded" object:nil];
}
In your view controller add this line in viewdidload
-(void)viewDidLoad{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(tableReloaded:) name:#"Table Reloaded" object:nil];
}
-(void) tableReloaded:(NSNotification*)notification{
[self selectFirstRow];
}

Setting scroll position in UITableView

I have an application that works some what similar to how iPhone's Contact application works. When we add a new Contact user is directed to a view only screen with Contact information. If we select "All Contacts" from the navigation bar, user is navigated to list of all contacts where the recently added contact is in view.
We can move the view to a particular row using:
[itemsTableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionBottom];
... but it's not working. I'm calling this right after calling:
[tableView reloadData];
I think I'm not suppose to call selectRowAtIndexPath:animated:scrollPosition method here. But if not here, then where?
Is there any delegate method that gets called after the following method?
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
I think I've got a similar app: A list of item., tap '+' -> new screen, come back and see the updated list, scrolled to show the added item at the bottom.
In summary, I put reloadData in viewWillAppear:animated: and scrollToRowAtIndexPath:... in viewDidAppear:animated:.
// Note: Member variables dataHasChanged and scrollToLast have been
// set to YES somewhere else, e.g. when tapping 'Save' in the new-item view.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (dataHasChanged) {
self.dataHasChanged = NO;
[[self tableView] reloadData];
} else {
self.scrollToLast = NO; // No reload -> no need to scroll!
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (scrollToLast) {
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([dataController count] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
I hope this helps. If you add something in the middle of the list, you could easily scroll to that position instead.
You can try this , my application in some kind of similar to you when i click on a button scroll of uitableview is up.
[tableviewname setContentOffset:CGPointMake(0, ([arrayofcontact count]-10)*cellheight) animated:YES];
10 is for how many cell you want to scroll up
Hope this will help you:-)
Scrolling animation is inevitable when offset is set from viewDidAppear(_:). A better place for setting initial scroll offset is viewWillAppear(_:). You'll need to force the layout of a table view, because it's content size is not defined at that point.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.setNeedsLayout()
tableView.layoutIfNeeded()
if let selectedIndexPath = selectedIndexPath, tableView.numberOfRows(inSection: selectedIndexPath.section) > 0 {
tableView.scrollToRow(at: selectedIndexPath, at: .top, animated: false)
}
}
Do you have enough dummy contacts added to test the scrolling? It seems that only when your tableView is of a certain size the iPhone finds the inputus to scroll.
This is the code that works for me in my project. I use a previous button and therefore I scroll the tableview a little bit further down that it usually would go with UITABLEVIEWSCROLLPOSITION. (my previous button won't work if it can't "see" the previous cell.
Ignore some of the custom method calls.
- (void)textFieldDidBeginEditing:(UITextField *)textField {
//Show the navButtons
[self navButtonAnimation];
DetailsStandardCell *cell = (DetailsStandardCell *)textField.superview.superview.superview;
self.parentController.lastCell = cell;
//Code to scroll the tableview to the previous indexpath so the previous button will work. NOTE previous button only works if its target table cell is visible.
NSUInteger row = cell.cellPath.row;
NSUInteger section = cell.cellPath.section;
NSIndexPath *previousIndexPath = nil;
if (cell.cellPath.row == 0 && cell.cellPath.section != 0) //take selection back to last row of previous section
{
NSUInteger previousIndex[] = {section -1, ([[self.sections objectForKey:[NSNumber numberWithInt:section - 1]]count] -1)};
previousIndexPath = [[NSIndexPath alloc] initWithIndexes:previousIndex length:2];
}
else if (cell.cellPath.row != 0 && cell.cellPath.section != 0)
{
NSUInteger previousIndex[] = {section, row - 1};
previousIndexPath = [[NSIndexPath alloc] initWithIndexes:previousIndex length:2];
}
[self.theTableView scrollToRowAtIndexPath: cell.cellPath.section == 0 ? cell.cellPath : previousIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

Resources