BLKFlexibleHeightBar
BLKFlexibleHeightBar copied to clipboard
how to fix tableview scrolling?
Before i integrated flexible bar, my table scrolled at correct position for cell via:
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
first reload - refresh cell and change the height of row, but scrolling after that is not working anymore. here is video: https://www.dropbox.com/s/bp937vhsm3izvru/bug%20scroll.mov?dl=0
correct position - when editor is fit totally to screen. How to fix it? i don't wanna to remove your bar, because i already integrated and liked abit :)
Hey @plandem, I'm not exactly sure what the issue is. After you change the height of one of your cells and refresh the table view, scrolling doesn't work afterward? Looking at your video, it looks like you scroll after the cell's height grows. It's tough for me to see what the issue is based on the video.
Can you clarify a bit more about what is happening after the cell height changes?
here is some snippets.
height:
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
indexPath = [tableView dataSourceIndexPathFromVisibleIndexPath: indexPath];
return ([indexPath compare:self.selectedIndexPath] != NSOrderedSame) ? cellHeightList : cellHeightListEditor;
}
select:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
indexPath = [tableView dataSourceIndexPathFromVisibleIndexPath: indexPath];
[self onSelectRow:indexPath];
}
real selection:
/**
* Select row
*/
-(void)onSelectRow:(NSIndexPath *)indexPath {
BOOL isSame = ([indexPath compare:self.selectedIndexPath] == NSOrderedSame);
@weakify(self);
[CATransaction begin];
[CATransaction setCompletionBlock: ^{
@strongify(self);
[CATransaction begin];
[CATransaction setCompletionBlock: ^{
@strongify(self);
if (!(isSame)) {
self.selectedIndexPath = indexPath;
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}];
[self.tagView collapsePanel];
[CATransaction commit];
}];
[self discardSelection];
[CATransaction commit];
}
so here is steps:
- on select i retain selected row
- i reload this row
- during reload i return new height for that row
- i scroll at that row (row already has new height)
Without your bar, scrolling worked as needed. but now it's not scrolled enough. and im not sure how to fix it.
Thanks for providing more info. I figured out the problem, and as far as I can tell, it has nothing to do with BLKFlexibleHeightBar. The problem happens when you set the contentInset
of your table view - something that you probably want to do when using BLKFlexibleHeightBar.
Here's a demo project that I made to test my hypothesis. I set the contentInset
in viewDidLoad:
.
https://www.dropbox.com/s/xctbj94fpdhn05s/TableViewTests.zip?dl=0
I don't have time to find a solution, but I think that manually setting the contentOffset
of your table view is the way to go. The table view's contentInset.top
property should be a part of this calculation. If you find a workaround using that idea or maybe a better one, let me know =)
but in other cases(i mean scrolling) contentInset is not a problem? do u dynamically recalc it?
well, i still puzzled. i just tried to log "contentOffset" and it's same after 'scrollToIndexPath'!!!
-(void)onSelectRow:(NSIndexPath *)indexPath {
BOOL isSame = ([indexPath compare:self.selectedIndexPath] == NSOrderedSame);
@weakify(self);
[CATransaction begin];
[CATransaction setCompletionBlock: ^{
@strongify(self);
[CATransaction begin];
[CATransaction setCompletionBlock: ^{
@strongify(self);
if (!(isSame)) {
self.selectedIndexPath = indexPath;
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[CATransaction begin];
[CATransaction setCompletionBlock: ^{
@strongify(self);
// [self.tableView setContentOffset:newOffset animated:YES];
NSLog(@"===== after %@", NSStringFromCGPoint(self.tableView.contentOffset));
}];
NSLog(@"===== before %@", NSStringFromCGPoint(self.tableView.contentOffset));
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
[CATransaction commit];
}
}];
[self.tagView collapsePanel];
[CATransaction commit];
}];
[self discardSelection];
[CATransaction commit];
}
looks like scrollToRowAtIndexPath does not touch the contentOffset
and im still sure it's your bar specific task (again). Because in other case i would not use contentInset, and would use tableViewController that already has autoAdjustContentInset. My idea - if you make user to do something (like add contentInset), then its your component specific after that. Because without this 'something' your component will not work correctly.
well, i gave up finally and move back to common table header. i tried alot, and still no success. The most problems at top of scrollview - when i try to expand row and bar is visible same time. Here is my last try:
-(void)onSelectRow:(NSIndexPath *)indexPath {
BOOL isSame = ([indexPath compare:self.selectedIndexPath] == NSOrderedSame);
@weakify(self);
[CATransaction begin];
{
[CATransaction setCompletionBlock:^{
NSLog(@"1 . discard phase done");
@strongify(self);
[CATransaction begin];
{
[CATransaction setCompletionBlock:^{
NSLog(@"2 . tagView phase done");
@strongify(self);
if (!(isSame)) {
[CATransaction begin];
{
[CATransaction setCompletionBlock:^{
NSLog(@"3 . reload phase done");
@strongify(self);
[CATransaction begin];
{
[CATransaction setCompletionBlock:^{
NSLog(@"4 . scroll phase done");
@strongify(self);
CGRect rowRect = [self.tableView rectForRowAtIndexPath:indexPath];
CGPoint offsetContent = self.tableView.contentOffset;
NSLog(@"===== after scroll");
NSLog(@"table. minY=%f, maxY=%f, height=%f, offset=%f", CGRectGetMinY(self.tableView.bounds), CGRectGetMaxY(self.tableView.bounds), CGRectGetHeight(self.tableView.frame), offsetContent.y);
NSLog(@"row. minY=%f, maxY=%f, height=%f", CGRectGetMinY(rowRect), CGRectGetMaxY(rowRect), CGRectGetHeight(rowRect));
CGFloat spaceLeft = CGRectGetMaxY(self.tableView.bounds) - CGRectGetMaxY(rowRect);
CGFloat progress = fabsf(spaceLeft) / self.headerView.maximumBarHeight;
NSLog(@"select progress = %f/%f", progress, spaceLeft);
if(spaceLeft < 0) {
NSLog(@"change progress to %f", MIN(progress, 1.0f));
// self.tableView.contentInset = UIEdgeInsetsMake(CGRectGetMaxY(self.flexibleHeightBar.frame), scrollView.contentInset.left, scrollView.contentInset.bottom, scrollView.contentInset.right);
// [self.tableView setContentOffset:(CGPoint){offsetContent.x, -(self.headerView.maximumBarHeight - fabsf(spaceLeft))} animated:YES];
[self.headerView.behaviorDefiner snapToProgress:MIN(progress, 1.0f) scrollView:self.tableView];
}
}];
CGSize sizeHeader = self.headerView.frame.size;
CGRect rowRect = [self.tableView rectForRowAtIndexPath:indexPath];
CGPoint offsetContent = self.tableView.contentOffset;
UIEdgeInsets insetContent = self.tableView.contentInset;
NSLog(@"===== before scroll");
NSLog(@"table. minY=%f, maxY=%f, height=%f, offset=%f", CGRectGetMinY(self.tableView.bounds), CGRectGetMaxY(self.tableView.bounds), CGRectGetHeight(self.tableView.frame), offsetContent.y);
NSLog(@"row. minY=%f, maxY=%f, height=%f", CGRectGetMinY(rowRect), CGRectGetMaxY(rowRect), CGRectGetHeight(rowRect));
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
} [CATransaction commit];
}];
self.selectedIndexPath = indexPath;
CGRect rowRect = [self.tableView rectForRowAtIndexPath:indexPath];
CGSize sizeHeader = self.headerView.frame.size;
CGPoint offsetContent = self.tableView.contentOffset;
UIEdgeInsets insetContent = self.tableView.contentInset;
NSLog(@"===== before reload");
NSLog(@"table. minY=%f, maxY=%f, height=%f, offset=%f", CGRectGetMinY(self.tableView.bounds), CGRectGetMaxY(self.tableView.bounds), CGRectGetHeight(self.tableView.frame), offsetContent.y);
NSLog(@"row. minY=%f, maxY=%f, height=%f", CGRectGetMinY(rowRect), CGRectGetMaxY(rowRect), CGRectGetHeight(rowRect));
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} [CATransaction commit];
}
}];
[self.tagView collapsePanel];
} [CATransaction commit];
}];
[self discardSelection];
} [CATransaction commit];
}
and my point is still same - it's bad idea to make people to reimplement TableView behaviors (like scrollToIndexPath) just to make your component work properly. So i will stay with common header now. I already spent more than enough time for it.
@plandem, this appears to be the intended behavior of UIScrollView. Play around with the demo I posted. If you set the contentInset
property of the UITableView to 200.0 and then do
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:10 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
Then item 10 will not be aligned to the top edge of the scroll view, but rather, the top inset. This seems to be the right behavior.
If you are proposing that I compensate for this built in, completely unrelated behavior of UIScrollView, then I'm afraid we fundamentally disagree about what the responsibilities of this project are.
I'll reopen this if you show me that this has something to do with my project specifically. For now, my research proves otherwise, so I think this issue should be closed.
man, i understand your position about that's it's not component specific. but let's consider this:
-
proposed that your component works fine with (UITableView, UICollectionView) - and here i don't even mention anything about UITableViewController.
-
we have UITableView(yes, it's scrollView inside, but does not matter here), for example. with built-in header and want to use your component to add some neat effects to his table's header. Again, proposed that your component works fine with UITableView.
-
we started to integrated you component and here is result: a) we have to move table's header to outside of table(in some cases it's not suitable, but nevermind) b) we have to set contentInset to make header looks like table's header c) if we have some advanced logic for table - sorting, expanding/collapsing rows (or any dynamic height for row that we can change runtime), then we will have troubles with you component, because we have to re-implement already built-in features that works correctly with table's header and footers, with any dynamic height and etc. d) you insist that it's not your component specific issue and i'm totally disagree with it. Because, truth is that your component is not working with UITableView/UICollectionView properly. Yes, at some cases it can work correctly, but it actually does not support it. That's my point. And i see only 2 ways to solve this disappointment: a) fix and make component to support UITableView/UICollectionView, respect specific features (like table's header) or b) fix the documentation and warn the potential users that this component partially supported UITableView/UICollectionView.
Because i disagree, that case when after integration something, i have to re-implement or replace built-in features - not component's specific issue. Know why? Because right now i moved back at table's header way and i have: a) properly working scrolling - built-in feature b) properly working expanding/collapsing - built in feature c) i just don't have neat effects of your component - external feature.
to summ: UITableView/UICollectionView are not UIScrollView. It's subclass of it with own features and workarounds about these features. If insist that, user must consider UITableView/UICollectionView as UIScrollView and forget about this features and must add own workarounds to get same functionalities, than i have nothing to say. That's my point.
BLKFlexibleHeightBar makes no attempt to adjust the scroll view content inset. Whatever value it's set to when you first configure the bar is what it will always be. This is the root of the issue you're experiencing.
Maybe the BLKFlexibleHeightBarBehaviorDefiner should adjust this? I'll look into it more when I get the chance. Although this is totally expected behavior of UIScrollView and all subclasses, the fact that BLKFlexibleHeightBar changes its height might indicate that it should also play some role in adjusting content insets.
I'll reopen so that others can see our conversation and maybe contribute a solution. I'll also look into a solution and try to find something appropriate once I get the chance.
Well, to support UITableView you must not consider table like isolated UIScrollView with only rows, because that's not true. That's UIScrollView that has: a) header, b) some rows c) footer. If i want to add some neat features to UITableView's header, then i must respect it and know about it. You advise move out header of UITableView, i.e. make it holds only rows. Sometimes it can work. I would advise to reconsider it and solve 2 cases:
- UIViewController with some UIView (header) and UIScrollView below. i.e. header is outside of scrollView.
- UIViewController with only UIScrollView with some UIView(header) inside of it. I.e. header is inside of scrollView.
We want to apply neat features to UIView(header) and make UIScrollView play with own offset/inset with respecting 'hierarchy' of that UIView to make these features alive.
I am not sure what the entire issue is since the conversation is huge, but can't you just scroll doing:
- (void)scrollRectToVisible:(CGRect)
and pass the rect you want to be visible? That way you don't have to deal with contentInset etc.
nope, if you will see commented code - i tried both: scrollRectToVisible and setOffset too. scrollToIndexRow works correctly, when the rest 'scrolls' way - not.
The issue is that the content inset doesn't change. This is related to another issue (on mobile so I can't link it). I'll definitely fix this but I'm pretty busy this week. There's a pull request open for a partial fix. Causes some issues with the square cash behavior defined though.
Hi All,
I have same issue when i'm using FlexibleHeader ios material design, when tableview contentInset changed and you need to programmatically scrollto a section the first row of that section will scroll to the contentInset top value. i tried almost everything with no success so if anyone has a solution for this please let me know