So I do this to program a scroll in my table view controller to my first cell.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
Then I want to fetch the new frame of this view:
NSArray *a=[self.tableView visibleCells];
UIView *view = [a firstObject];
However, the frame that this view gives me (which is the first one in my array of cells) returns the position before the scroll occurred.
I have tried putting it in an animation block to make sure it has finished scrolling before it tries to find it's position
The frame is always related to the super view. If you are interested then debug (or print out) what the super view of the cell view is. It is not directly the table view. (Used to be until iOS 7 or 8 or so but now it is embedded in some view object that scrolls along with the cell.)
You could try fetching the frame of its super view.
However, you'd be saver comparing the origin of the cell with the base view (self.view from the UIViewController's point of view which is the UITableView in case of a standard UITableViewController). For doing so you can use the convertPoint:fromView or convertPoint:toView methods.
So it turns out it is because I had it wrapped in an animation within an animation and so the SECOND animation wasn't completing before it checked for the frame:
[UIView animateWithDuration:0.5 animations:^{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionBottom
animated:NO];
}completion:^(BOOL finished)
{
NSArray *a=[self.tableView visibleCells];
InstructionView *iv = [[InstructionView alloc] initWithTargetView:[a firstObject] andInstructionType:kNeedYouInstruction];
iv.delegate = self;
[iv show];
}];
changing the second animation to NO stopped my problem :D
Related
I have a button in a UITableViewCell which is stored as an ivar called currentButton. When that button is clicked a UIView containing a UIPickerView and UIToolBar is invoked and shown from the bottom of the screen. However, I have looked at other posts for hours and this following code still doesn't work. The cells sometimes scroll down below the keyboard and the cells all the way at the bottom of the UITableView do not scroll up enough to go on top of the UIView backgroundPickerView. This is my code:
CGPoint center = currentButton.center;
CGPoint rootViewPoint = [currentButton.superview convertPoint:center toView:self.view];
NSIndexPath *indexPath = [measurementTableView indexPathForRowAtPoint:rootViewPoint];
CGRect cellRect = [measurementTableView rectForRowAtIndexPath:indexPath];
if (cellRect.origin.y + cellRect.size.height >= backgroundPickerView.frame.origin.y) {
[measurementTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
CGPoint offset = measurementTableView.contentOffset;
offset.y += ((cellRect.origin.y - cellRect.size.height) - backgroundPickerView.frame.origin.y);
[measurementTableView setContentOffset:offset animated:YES];
}
Does anyone see what I am doing wrong here?
UITableViewCell are reused,keeping a reference of a UITableViewCell's subView is not a good approach.
If the special UITableViewCell is not in UITableView's visibleCells ,its frame is undefined.
A solution is:
Create a custom UITableViewCell that has the same structure as you need.
Keep a reference of the indexpath for the custom UITableViewCell.
Use this indexpath to do the work .
I hope this will work:
[measurementTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
CGPoint offset = measurementTableView.contentOffset;
offset.y += backgroundPickerView.frame.height;
This code worked for me. Uses a different approach than the ones suggested here and it's good for what I need.
CGRect buttonFrame = [currentButton convertRect:currentButton.bounds toView:measurementTableView];
NSIndexPath *indexPath = [measurementTableView indexPathForRowAtPoint:buttonFrame.origin];
CGRect backPickFrame = [backgroundPickerView convertRect:backgroundPickerView.bounds toView:measurementTableView];
if (buttonFrame.origin.y + buttonFrame.size.height >= backPickFrame.origin.y) {
measurementTableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, backgroundPickerView.frame.size.height, 0.0);
[measurementTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
} else {
[measurementTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
First problem is that UITableView is partially hidden under a keyboard. So when you trying scroll to bottom rows table will bounce back and required content is not visible.
To solve this problem when keyboard appears/hides you have two choices.
update value of self.tableView.contentInstet.bottom to take into account keyboard size
update frame of table, if you are using auto-layout then best approach will be update constant part of bottom constraint (IMO this is better approach).
After you did that you can do this:
CGRect rectToBeVisible = [currentButton convertRect: currentButton.bounds
toView: self.tableView];
[self.tableView scrollRectToVisible: rectToBeVisible animated: YES];
I could messed up view to which convertion should be performed, so experiment a bit, but general concept should be clear.
I have a UITableView and when the user swipes on one of the cells he can delete the cell (delete the object from the array).
But for example, when the user swipes on a cell in the middle of the table the animation is made on the last cell (the one on the bottom) like in this video:
https://vid.me/D0pi
Here is the code for this deletion:
//Removing cell from "AroundersTableView"(UITableView) with UITableViewRowAnimationLeft
[self.AroundersTableView beginUpdates];
[self.Arounders removeObjectAtIndex:cellRowToDelete];
[self.AroundersTableView deleteRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:cellRowToDelete inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];
[self.AroundersTableView endUpdates];
//Saving Arounders
[self saveArounders];
//Creating "newTableFrame"(CGRect) and setting it to current "AroundersTableView"'s frame will one less cell
CGRect newTableFrame=self.AroundersTableView.frame;
newTableFrame.origin.y+=44;
//Animating "AroundersTableView" to the new frame
[UIView animateWithDuration:0.5 animations:^{
self.AroundersTableView.frame=newTableFrame;
}];
//ReConfiguring "AroundersTableView"(UITableView) height
[self performSelector:#selector(configureTableViewsHeight) withObject:nil afterDelay:0.5];
//Reloading "AroundersTableView"(UITableView) data
[self.AroundersTableView reloadData];
Note: configureTableViewsHeight is just a method the makes the tableView height to be the cell.height*Arounders.count (cells height * number of cells).
I have no idea why it's happening.
Why is it? How can I make the animation on the correct cell (the all the cells above it are going down and hiding the red part)?
Thank you!
I have a UITableView where I add rows dynamically. Basically a chat app.
I need to scroll the UITableView to bottom after reloadData
My code is:
It scrolls to here:
https://dl-web.dropbox.com/get/Screen%20Shot%202014-12-05%20at%2013.04.23.png?_subject_uid=44249794&w=AADgJkJJOGK_ANH_cXWnxeXmhFmF5KHjZllg-kP66HARdw
When manually scrolling, I can scroll even further to here: https://dl-web.dropbox.com/get/Screen%20Shot%202014-12-05%20at%2013.05.19.png?_subject_uid=44249794&w=AADtQEfMHMy0Ounri5W3gBTLNgR4uckT_gBdYQel9vD1qQ
My code:
[_chatTable reloadData];
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: [[AppData getChatLog]count]-1 inSection: 0];
[_chatTable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
I need the UITableView to scroll all the way to the bottom.
Sorry,I couldn't open up the given links.Try this
UITableView *yourTableView = yourTableViewToBeSetHere;
CGRect lastCellRect = CGRectMake(0, CGFLOAT_MAX, 50, 50);
// Scroll to the particular rect
[yourTableView scrollRectToVisible:lastCellRect
animated:YES];
Good luck!
I am inserting new section (section contains 3 cells) top of UITableView while SCROLLING TOP.
[_mainTable beginUpdates];
[_mainTable insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
[_mainTable endUpdates];
Section gets inserting properly. But it takes me top of Table i.e. cell 0 or row 0. I want this transaction to be smooth. I can insert
[_mainTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] atScrollPosition:UITableViewScrollPositionBottom animated:NO];
after endUpdates but it shows quick jerk because it takes you top of Table then suddenly scroll it to your last position.
How can i make it smooth.
Thanks
I haven't done exhaustive testing on this, but this seems promising:
Identify NSIndexPath of one of the visible cells.
Get its rectForRowAtIndexPath.
Get the current contentOffset of the table, itself.
Add the section, but call reloadData instead of insertSections (which prevents jarring scrolling).
Get the updated rectForRowAtIndexPath that you got in step 2.
Update contentOffset by the difference of the result from step 5 and that of step 2.
Thus:
[self.sections insertObject:newSection atIndex:0]; // this is my model backing my table
NSIndexPath *oldIndexPath = self.tableView.indexPathsForVisibleRows[0]; // 1
CGRect before = [self.tableView rectForRowAtIndexPath:oldIndexPath]; // 2
CGPoint contentOffset = [self.tableView contentOffset]; // 3
[self.tableView reloadData]; // 4
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:oldIndexPath.row inSection:oldIndexPath.section + 1];
CGRect after = [self.tableView rectForRowAtIndexPath:newIndexPath]; // 5
contentOffset.y += (after.origin.y - before.origin.y);
self.tableView.contentOffset = contentOffset; // 6
I have problem with a long form in iOS. The form itself is done with a static table view. For the form I needed a toolbar with prev/next and done. In this project I added BSKeyboardControls, and I've allready done it myself in another project.
The toolbar works fine except when the next or previous textField/textView is out of sight. Then the focus on the former textField won't change and I'm still typing in this text field. When I now scroll manually and the targeted next textfield appears it magically becomes the first responder. This is of course not the expected behabier.
So I figured out I would have to scroll the table view to the respective fields before trying to set them as first responder. But I connot get this done.
I've tried to this with indexPathes
NSIndexPath *path = [self.tableView indexPathForCell:(UITableViewCell *)textField.superview.superview];
[self.tableView scrollToRowAtIndexPat:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
Although the super-superview is a valid (and the right) table cell, the path returned is nil. I also tried some variations
CGPoint point = [self.tableView convertPoint:textField.frame.origin toView:self.tableView];
NSIndexPath *path = [self.tableView indexPathForRowAtPoint:point];
and other variations with rects and indexPathsForRowsInRect.
The only way is to manually keep track of the textFields cell indexPaths, but that is no suitable solution in this case.
Anybody has an idea of how to achieve scrolling to an out of sight textField in a tableView?
Edit:
As Justin Paulson requested the code of keyboardControlsPreviousNextPressed:
- (void)keyboardControlsPreviousNextPressed:(BSKeyboardControls *)controls withDirection:(KeyboardControlsDirection)direction andActiveTextField:(id)textField
{
NSIndexPath *path = [self.tableView indexPathForCell:(UITableViewCell *)((UIView *)textField).superview.superview];
NSLog(#"path %# for cell %#", path, ((UIView *)textField).superview.superview);
[self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
[textField becomeFirstResponder];
}
As I said according to the output of the log the path is nil, whereas the super-superview is the rught table view cell
Thanks for trying to help me, i've a solution myself. In my keyboardControlsPreviousNextPressed i have now
[self.tableView scrollRectToVisible:((UIView *)textField).superview.superview.frame animated:YES];
[textField becomeFirstResponder];
This solution seems to work quite well. The only problem i've encountered is that sometimes the newly focused textField is hidden by the keyboard, so the tableViewController doesn't adjusts itself automatically. You could additionally scroll the row to the top to circumvent this in most cases.
NSIndexPath *path = [self.tableView indexPathForCell:(UITableViewCell *)textField.superview.superview];
[self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];