I know how to animate the height change of a UITableViewCell using the method seen here: Can you animate a height change on a UITableViewCell when selected?
However, using that method, the UITableView will scroll at the same time, which I don't want it to do.
I have a UITableView with very few cells; it takes up less than the screen height. The bottom cell has a UITextField and, when it starts editing, I manually set the content offset of the UITableView so that the cell with the UITextField is scrolled to the top. Then, based on the input in the UITextField, I may want to increase the size of the UITableViewCell to show extra options, more or less.
The problem is that, when animating this change, it will reposition the UITableView so that my UITextField is no longer at the top.
This is what I'm doing, more or less:
self.customAmountCellSize = height;
[self.tableView beginUpdates];
[self.tableView endUpdates];
I have also tried
self.customAmountCellSize = height;
CGPoint originalOffset = self.tableView.contentOffset;
[self.tableView beginUpdates];
[self.tableView endUpdates];
[self.tableView setContentOffset:originalOffset animated:NO];
I want the row height animation, I do not want the UITableView to scroll as a result.
Any ideas?
It appears the problem you're encountering is that your table view is scrolled past the bottom so when you update its content it will attempt to fix that.
There are two approaches you could take to prevent scrolling:
Set the table view's content inset to be the height of the initial white space:
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, verticalGap, 0);
Add an empty footer view with the same height as the vertical gap:
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, verticalGap)];
In both cases, you will need to calculate the vertical space you are trying to achieve. You then need to restore the contentInset or tableFooterView to its original state when you are done.
I think the table view is scrolling because your text field is becoming the first responder, and not because of the cell height change. Try keeping the cell height the same and just adjusting the offset to be sure.
If I am correct, than here's the solution: UITableView automatically tries to scroll when your keyboard appears. To fix this, set the content offset to your desired offset in a dispatch to the main queue, which will fire at the beginning of the next runloop. Put the following code inside your response to a UIKeyboardWillShowNotification, or in a UITextFieldDelegate shouldBeginEditing method:
// Get the current offset prior to the keyboard animation
CGPoint currentOffset = self.tableView.contentOffset;
UIEdgeInsets currentInsets = self.tableView.contentInset;
__weak SomeTableViewControllerClass *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animationWithDuration:0 animations:{
// set the content offset back to what it was
weakSelf.tableView.contentOffset = currentOffset;
weakSelf.tableView.contentInset = currentInsets;
} completion:nil];
});
Similar fixes are sometimes necessary for the contentInset.bottom of a table view, depending on the frame of your UITableView and other factors.
Related
I have created a tableview in code as follows:
_myTableView = [[UITableView alloc] initWithFrame:
CGRectMake(160, 80, 140, 100) style:UITableViewStylePlain];
_myTableView.delegate = self;
_myTableView.dataSource = self;
_myTableView.scrollEnabled = YES;
[self.view addSubview:_myTableView];
It largely works as it should with the following exception. Because the results in the table vary, I manually adjust the height of the tableview so that it only takes up as much space as the returned rows need as follows:
-(void) changeTVHeight: (float) height {
//height calculated from number of items in array returned.
CGRect newFrame = CGRectMake(120, 80, 180, height);
self.myTableView.frame = newFrame;
}
This works great for shrinking the tableview if there aren't that many results.
However, if there are a lot of results, the tableview expands below the visible part of the screen or the keyboard. In this case, I would like to be able to scroll the Tableview to see the lower rows.
scrollEnabled is set to YES.
But while it does allow one to scroll a bit, the scroll is resisted so with effort you can scroll a little bit but due to rubber band effect you cannot get further than a few rows below the screen and you cannot tap on the lower rows.
I am using autolayout in storyboard for much of the screen. The overall screen scrolls fine but this merely moves the tableview anchored to the screen up and down. There are no constraints on this tableview but it is added as a subview of the view.
My question is how can I make the tableview scrollable so that it scrolls without resistance?
Thanks in advance for any suggestions.
Edit:
I tried adding the tableView to self.scrollView instead of self.view. This anchored the tableView to the scrollview so it is possible to scroll the whole screen down and see the bottom of the tableview. However, this is not ideal because the rest of the screen is empty way down and you can't see the context for the tableview. (It's an autocomplete for a textfield at top of screen.)
In contrast when the tableview is added to self.view, it is in correct place, it semi-scrolls or bounces. It just doesn't scroll down to where I need it to scroll.
You need to set a limit so that the table view cannot be larger than the view itself. Tableviews are built on UIScrollView and will handle scrolling on their own, you don't need to try to size it manually. The reason the table view bounces but doesn't scroll is because it is extending below the bottom of the screen. It wont scroll because it has already scrolled to the bottom, you just can't see it because it's outside of the superview.
-(void) changeTVHeight: (float) height {
CGFloat limitedHeight = MIN(height, self.view.frame.size.height)
CGRect newFrame = CGRectMake(120, 80, 180, limitedHeight);
self.myTableView.frame = newFrame;
}
I have a UITableViewController and when the user scrolls to the top, I want to add a bunch of cells above the first cell without affecting the current scroll position (just like iMessage)
I know I can use insertRowsAtIndexPaths to do this without reloading the data, but I just want to do it the simplest way possible first. So I am overwriting the data and calling reload, then calling setContentOffset, but no matter what I set the offset to, the table always reverts to the top.
// in an NSNotification handler
//...
dispatch_async(dispatch_get_main_queue(), ^{
// get old content height
CGSize oldContentSize = self.tableView.contentSize;
NSLog(#"old content height: %f", oldContentSize.height);
// update tableView data
self.messages = updatedMessages;
[self.tableView reloadData];
// get new content height
CGSize newContentSize = self.tableView.contentSize;
NSLog(#"new content height: %f", newContentSize.height);
// move to user scroll position
CGPoint newContentOffset = CGPointMake(0, newContentSize.height - oldContentSize.height);
[self.tableView setContentOffset:newContentOffset];
});
When I run this I get:
old content height: 557.765625
new content height: 1249.050781
but the tableView reverts to the top instead of maintaining its scroll position.
I found that if I call [self.tableView setContentOffset:newContentOffset animated:YES]; It always scrolls down to the correct position, but the movement is unacceptable.
How can I maintain the scroll position after reloading the tableView data?
I've looked at these:
setContentOffset only works if animated is set to YES
UIScrollView setContentOffset: animated: not working
UITableView contentOffSet is not working properly
but their solutions don't work in my case.
Try this
[self.tableView setContentOffset:newContentOffset animated:NO]
I have a UITableView with a UIView on top. I want the UIView to stick to the top as the tableView cells scroll over it.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (self.tableView.contentOffset.y > 0) {
CGRect newframe = self.publicTopView.frame;
newframe.origin.y = -self.tableView.contentOffset.y;
self.publicTopView.frame = newframe;
NSLog(#"After: %f", self.publicTopView.frame.origin.y);
}
}
You need to set your table view header view to the view you want on top.
Add this code to you viewDidLoad
self.tableView.tableHeaderView = self.publicTopView
I'm not certain what you're trying to accomplish, but I have a guess at what is wrong. As you scroll your contentOffset will continue to change and let's say your tableView has a content size height of 1500, then your contentOffset will eventually be larger than the height of your view controllers view. Now see that you are putting that contentOffset into the origin.y of your publicTopView. So your publicTopView could possibly be moving too much, even offscreen depending on how large your tableview's content size is.
I have a question about the usage of UITableView. I have added a UIView above the cells of my UITableView (see image).
This is very nice because I can add some images and labels there and it will scroll with the cells of the table view. Now I am calling some REST API to get an image which I want to add in this view above the cells. The problem now is that I dont know the height of the image, so I have to calculate it based on the aspect ratio which already works fine. When I add the image I can change its height correctly and move down labels and buttons BUT the image overlaps some of the visible cells.
My question: How can I move down the frame of the container? of the cells? dynamically based on my image respective View height?
I have tried to set the height of the View in the TableView but it has no effect. So I suppose that I have to set the y start position of the cells but I dont know how.
Do I need to set an y offset in the delegate method -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath ?
Any ideas?
I think the key to this is setting your view to be the table view's tableHeaderView after you change the size of the view. I did it like this in a test app,
-(void)layoutHeader {
self.label.text = #"This is a long text to see if it expands to take up multple lines. The quick red fox jumped over the lazy brown dog.";
[self.label setPreferredMaxLayoutWidth:self.tableView.frame.size.width];
CGRect stringRect = [self.label.text boundingRectWithSize:CGSizeMake(self.tableView.bounds.size.width - 40,CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName:self.label.font} context:nil];
CGRect headerFrame = self.header.frame;
headerFrame.size.height = stringRect.size.height + 40;
self.header.frame = headerFrame;
[self.tableView beginUpdates];
self.tableView.tableHeaderView = self.header;
[self.tableView endUpdates];
}
I called this with a delay from viewDidLoad as a test. The beginUpdates, endUpdates code isn't necessary if you don't want to see the rows move down to accommodate the new view size. The property, header, is an IBOutlet to the view I added to the top of the table view in IB, and "label" is a subview of that view.
I would personally just use tableView:viewForHeaderInSection: to build the view out, and in tableView:heightForHeaderInSection: calculate the new height and return that. That way you don't have to worry about bumping things down within the tableView since UITableView will handle the rest for you once you. Just make sure to call [_tableView reloadData]; on your tableView after you get the image.
I have some dynamic text content that should grow the cell vertically and I am doing it by calculating the height and returning that for the table cell. The table cell grows in size fine but the issue is growing the UIView inside the cell.
I set the height of the UIView inside the cell using the same method of calculating height using the following code but the UIView does not grow even after setting its frame. So below the businessDescriptionTextView is a Label and container is a UIView inside the cell. I am setting both of them and the getHeightBusinessDescription does return the correct height but for some reason it does not redraw
cell.businessDescriptionTextView.text = self.dealLocation.company.description;
cell.businessDescriptionTextView.lineBreakMode = NSLineBreakByWordWrapping;
cell.businessDescriptionTextView.numberOfLines = 0;
[cell.businessDescriptionTextView sizeToFit];
CGRect frame = cell.container.layer.frame;
frame.size.height = [self getHeightBusinessDescription] + 50;
cell.container.layer.frame = frame;
I have tried setNeedsDisplay on cell.container cell.businessDescriptionTextView and still no luck. I also tried the table reload
[self.tableView reloadRowsAtIndexPaths:array withRowAnimation:UITableViewRowAnimationNone];
With the beginUpdates and endUpdates right after I set the frame size and still nothing happened.