Merge pull request #1239 from vector-im/riot_1194

Improve tab filter
This commit is contained in:
giomfo
2017-05-23 21:18:13 +02:00
committed by GitHub
3 changed files with 114 additions and 187 deletions
+2 -3
View File
@@ -45,7 +45,6 @@
@property (weak, nonatomic) IBOutlet UIView *stickyHeadersBottomContainer;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *stickyHeadersTopContainerHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *stickyHeadersBottomContainerHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *stickyHeadersBottomContainerBottomConstraint;
/**
If YES, the table view will scroll at the top on the next data source refresh.
@@ -94,9 +93,9 @@
- (void)resetStickyHeaders;
/**
Update the sticky headers display.
Prepare the sticky headers display.
*/
- (void)updateStickyHeaders;
- (void)prepareStickyHeaders;
/**
Refresh the cell selection in the table.
+109 -181
View File
@@ -124,17 +124,6 @@
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Adjust Bottom constraint to take into account tabBar.
[NSLayoutConstraint deactivateConstraints:@[_stickyHeadersBottomContainerBottomConstraint]];
_stickyHeadersBottomContainerBottomConstraint = [NSLayoutConstraint constraintWithItem:self.bottomLayoutGuide
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.stickyHeadersBottomContainer
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:0.0f];
[NSLayoutConstraint activateConstraints:@[_stickyHeadersBottomContainerBottomConstraint]];
self.recentsTableView.accessibilityIdentifier = @"RecentsVCTableView";
// Register here the customized cell view class used to render recents
@@ -277,8 +266,12 @@
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
dispatch_async(dispatch_get_main_queue(), ^{
[self refreshStickyHeadersContainersHeight];
[self refreshStickyHeadersContainersHeight];
});
}
#pragma mark - Override MXKRecentListViewController
@@ -312,10 +305,13 @@
isRefreshPending = NO;
// Force reset existing sticky headers if any
[self resetStickyHeaders];
[self.recentsTableView reloadData];
// Check conditions to display the fake search bar into the table header
if (_enableSearchBar && !_enableStickyHeaders && self.recentsSearchBar.isHidden && self.recentsTableView.tableHeaderView != tableSearchBar)
if (_enableSearchBar && self.recentsSearchBar.isHidden && self.recentsTableView.tableHeaderView == nil)
{
// Add the search bar by hiding it by default.
self.recentsTableView.tableHeaderView = tableSearchBar;
@@ -328,7 +324,7 @@
_shouldScrollToTopOnRefresh = NO;
}
[self updateStickyHeaders];
[self prepareStickyHeaders];
// In case of split view controller where the primary and secondary view controllers are displayed side-by-side on screen,
// the selected room (if any) is updated and kept visible.
@@ -338,30 +334,13 @@
}
}
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
{
// Deduce the bottom constraint for the table view (Don't forget the potential tabBar)
CGFloat tableViewBottomConst = keyboardHeight - self.bottomLayoutGuide.length;
// Check whether the keyboard is over the tabBar
if (tableViewBottomConst < 0)
{
tableViewBottomConst = 0;
}
// Update constraints
_stickyHeadersBottomContainerBottomConstraint.constant = tableViewBottomConst;
// Force layout immediately to take into account new constraint
[self.view layoutIfNeeded];
}
- (void)hideSearchBar:(BOOL)hidden
{
[super hideSearchBar:hidden];
if (!hidden && !_enableStickyHeaders)
if (!hidden)
{
// Remove the fake table header view
// Remove the fake table header view if any
self.recentsTableView.tableHeaderView = nil;
self.recentsTableView.contentInset = UIEdgeInsetsZero;
}
@@ -409,24 +388,11 @@
{
_enableStickyHeaders = enableStickyHeaders;
if (enableStickyHeaders)
// Refresh the table display if it is already rendered.
if (self.recentsTableView.contentSize.height)
{
// Add a table header view in order to hide the current section header stuck in floating mode at the top of the table.
// This section header is handled then by a sticky header.
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.recentsTableView.bounds.size.width, self.stickyHeaderHeight)];
self.recentsTableView.tableHeaderView = headerView;
self.recentsTableView.contentInset = UIEdgeInsetsMake(-self.stickyHeaderHeight, 0, 0, 0);
[self.recentsSearchBar setShowsCancelButton:NO animated:NO];
[self refreshRecentsTable];
}
else
{
self.recentsTableView.tableHeaderView = nil;
self.recentsTableView.contentInset = UIEdgeInsetsZero;
}
[self refreshRecentsTable];
}
- (void)setStickyHeaderHeight:(CGFloat)stickyHeaderHeight
@@ -462,13 +428,13 @@
}
[displayedSectionHeaders removeAllObjects];
firstDisplayedSectionHeaderPosY = 0;
self.recentsTableView.contentInset = UIEdgeInsetsZero;
}
- (void)updateStickyHeaders
- (void)prepareStickyHeaders
{
// Force reset existing sticky headers if any
[self resetStickyHeaders];
// We suppose here [resetStickyHeaders] has been already called if need.
NSInteger sectionsCount = self.recentsTableView.numberOfSections;
@@ -575,135 +541,84 @@
{
if (_enableStickyHeaders)
{
// Check whether the full table content is visible.
if (self.recentsTableView.contentSize.height + self.recentsTableView.contentInset.top + self.recentsTableView.contentInset.bottom <= self.recentsTableView.frame.size.height )
{
// No sticky header is required. Hide them to prevent from flickering in case of vertical bounces.
self.stickyHeadersTopContainerHeightConstraint.constant = 0;
self.stickyHeadersBottomContainerHeightConstraint.constant = 0;
return;
}
// Retrieve the first and the last headers actually visible in the recents table view.
// Caution: In some cases like the screen rotation, some displayed section headers are temporarily not visible.
UIView *firstDisplayedSectionHeader, *lastDisplayedSectionHeader;
NSUInteger lowestSectionInBottomStickyHeader = NSNotFound;
CGFloat containerHeight;
CGFloat maxVisiblePosY = self.recentsTableView.contentOffset.y + self.recentsTableView.frame.size.height - self.recentsTableView.contentInset.bottom;
// Retrieve the first header actually visible in the recents table view.
// Caution: In some cases like the screen rotation, some displayed section headers are temporarily not visible.
UIView *firstDisplayedSectionHeader;
for (UIView *header in displayedSectionHeaders)
{
if (!firstDisplayedSectionHeader)
if (header.frame.origin.y + header.frame.size.height > self.recentsTableView.contentOffset.y)
{
if (header.frame.origin.y + header.frame.size.height > self.recentsTableView.contentOffset.y)
{
firstDisplayedSectionHeader = lastDisplayedSectionHeader = header;
}
}
else
{
if (header.frame.origin.y < maxVisiblePosY)
{
lastDisplayedSectionHeader = header;
}
else
{
break;
}
firstDisplayedSectionHeader = header;
break;
}
}
if (firstDisplayedSectionHeader)
{
// Consider the first visible section header to update the height of the top container of the sticky headers.
// Check whether the header positon is not floating.
if (firstDisplayedSectionHeader.frame.origin.y == firstDisplayedSectionHeaderPosY)
// Initialize the top container height by considering the headers which are before the first visible section header.
containerHeight = 0;
for (UIView *header in _stickyHeadersTopContainer.subviews)
{
if (header.tag < firstDisplayedSectionHeader.tag)
{
containerHeight += self.stickyHeaderHeight;
}
}
// Check whether the first visible section header is partially hidden.
if (firstDisplayedSectionHeader.frame.origin.y < self.recentsTableView.contentOffset.y)
{
// Compute the height of the hidden part.
CGFloat delta = self.recentsTableView.contentOffset.y - firstDisplayedSectionHeader.frame.origin.y;
if (delta < 0)
{
delta = 0;
}
// Compute the top container height.
containerHeight = 0;
for (UIView *header in _stickyHeadersTopContainer.subviews)
if (delta < self.stickyHeaderHeight)
{
if (header.tag < firstDisplayedSectionHeader.tag)
{
containerHeight += header.frame.size.height;
}
else if (header.tag == firstDisplayedSectionHeader.tag)
{
if (delta < header.frame.size.height)
{
containerHeight += delta;
}
else
{
containerHeight += header.frame.size.height;
}
}
containerHeight += delta;
}
else
{
containerHeight += self.stickyHeaderHeight;
}
}
if (containerHeight)
{
self.stickyHeadersTopContainerHeightConstraint.constant = containerHeight;
self.recentsTableView.contentInset = UIEdgeInsetsMake(-self.stickyHeaderHeight, 0, 0, 0);
}
else
{
// Update the first displayed header position.
firstDisplayedSectionHeaderPosY = firstDisplayedSectionHeader.frame.origin.y;
// The first displayed header position is floating, that means the header is stuck at the top of the table.
// We hide its higher part with the sticky header. The lower part of the header is still visible if its height
// is higher than self.stickyHeaderHeight.
containerHeight = 0;
for (UIView *header in _stickyHeadersTopContainer.subviews)
{
if (header.tag <= firstDisplayedSectionHeader.tag)
{
containerHeight += header.frame.size.height;
}
}
self.stickyHeadersTopContainerHeightConstraint.constant = containerHeight;
self.stickyHeadersTopContainerHeightConstraint.constant = 0;
self.recentsTableView.contentInset = UIEdgeInsetsZero;
}
// Consider the last visible section header to update the height of the bottom container of the sticky headers.
containerHeight = 0;
CGRect bounds = self.stickyHeadersBottomContainer.frame;
bounds.origin.y = 0;
// Look for the lowest section index visible in the bottom sticky headers.
CGFloat maxVisiblePosY = self.recentsTableView.contentOffset.y + self.recentsTableView.frame.size.height - self.recentsTableView.contentInset.bottom;
UIView *lastDisplayedSectionHeader = displayedSectionHeaders.lastObject;
for (UIView *header in _stickyHeadersBottomContainer.subviews)
{
if (header.tag == lastDisplayedSectionHeader.tag)
if (header.tag > lastDisplayedSectionHeader.tag)
{
// Compute the height of the hidden part
CGFloat delta = (lastDisplayedSectionHeader.frame.origin.y + header.frame.size.height) - maxVisiblePosY;
if (delta < 0)
{
delta = 0;
}
if (delta < header.frame.size.height)
{
bounds.origin.y = header.frame.origin.y + header.frame.size.height - delta;
containerHeight += delta;
}
else
{
bounds.origin.y = header.frame.origin.y;
containerHeight += header.frame.size.height;
}
}
else if (header.tag > lastDisplayedSectionHeader.tag)
{
containerHeight += header.frame.size.height;
maxVisiblePosY -= self.stickyHeaderHeight;
}
}
if (self.stickyHeadersBottomContainerHeightConstraint.constant != containerHeight)
for (NSInteger index = displayedSectionHeaders.count; index > 0;)
{
self.stickyHeadersBottomContainerHeightConstraint.constant = containerHeight;
self.stickyHeadersBottomContainer.bounds = bounds;
lastDisplayedSectionHeader = displayedSectionHeaders[--index];
if (lastDisplayedSectionHeader.frame.origin.y + self.stickyHeaderHeight > maxVisiblePosY)
{
maxVisiblePosY -= self.stickyHeaderHeight;
}
else
{
lowestSectionInBottomStickyHeader = lastDisplayedSectionHeader.tag + 1;
break;
}
}
}
else
@@ -726,26 +641,43 @@
}
self.stickyHeadersTopContainerHeightConstraint.constant = containerHeight;
// Update the bottom container of the sticky headers.
containerHeight = 0;
CGRect bounds = self.stickyHeadersBottomContainer.frame;
for (UIView *header in _stickyHeadersBottomContainer.subviews)
if (containerHeight)
{
if (header.tag > section)
{
if (header.tag == section + 1)
{
bounds.origin.y = header.frame.origin.y;
}
containerHeight += header.frame.size.height;
}
self.recentsTableView.contentInset = UIEdgeInsetsMake(-self.stickyHeaderHeight, 0, 0, 0);
}
self.stickyHeadersBottomContainerHeightConstraint.constant = containerHeight;
self.stickyHeadersBottomContainer.bounds = bounds;
else
{
self.recentsTableView.contentInset = UIEdgeInsetsZero;
}
// Set the lowest section index visible in the bottom sticky headers.
lowestSectionInBottomStickyHeader = section + 1;
}
}
// Update here the height of the bottom container of the sticky headers thanks to lowestSectionInBottomStickyHeader.
containerHeight = 0;
CGRect bounds = _stickyHeadersBottomContainer.frame;
bounds.origin.y = 0;
for (UIView *header in _stickyHeadersBottomContainer.subviews)
{
if (header.tag > lowestSectionInBottomStickyHeader)
{
containerHeight += self.stickyHeaderHeight;
}
else if (header.tag == lowestSectionInBottomStickyHeader)
{
containerHeight += self.stickyHeaderHeight;
bounds.origin.y = header.frame.origin.y;
}
}
if (self.stickyHeadersBottomContainerHeightConstraint.constant != containerHeight)
{
self.stickyHeadersBottomContainerHeightConstraint.constant = containerHeight;
self.stickyHeadersBottomContainer.bounds = bounds;
}
}
}
@@ -1194,7 +1126,6 @@
if (!firstDisplayedSectionHeader || section < firstDisplayedSectionHeader.tag)
{
[displayedSectionHeaders insertObject:view atIndex:0];
firstDisplayedSectionHeaderPosY = view.frame.origin.y;
}
else
{
@@ -1216,10 +1147,6 @@
{
[displayedSectionHeaders removeObjectAtIndex:0];
// Update first displayed section position
firstDisplayedSectionHeader = displayedSectionHeaders.firstObject;
firstDisplayedSectionHeaderPosY = firstDisplayedSectionHeader.frame.origin.y;
[self refreshStickyHeadersContainersHeight];
}
else
@@ -1242,7 +1169,11 @@
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self refreshStickyHeadersContainersHeight];
dispatch_async(dispatch_get_main_queue(), ^{
[self refreshStickyHeadersContainersHeight];
});
[super scrollViewDidScroll:scrollView];
@@ -1254,6 +1185,9 @@
{
// Hide the search bar
[self hideSearchBar:YES];
// Refresh display
[self refreshRecentsTable];
}
}
}
@@ -1698,12 +1632,6 @@
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
[super scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
if (_enableStickyHeaders && _enableSearchBar && targetContentOffset->y + scrollView.contentInset.top <= 0 && scrollView.contentSize.height)
{
// Show the search bar
[self hideSearchBar:NO];
}
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
@@ -15,8 +15,8 @@
<outlet property="recentsSearchBarHeightConstraint" destination="jCU-cq-2OA" id="w76-xb-0xq"/>
<outlet property="recentsSearchBarTopConstraint" destination="aRg-Nz-enq" id="gI4-54-oEm"/>
<outlet property="recentsTableView" destination="orV-HH-88x" id="lgA-2k-pXJ"/>
<outlet property="recentsTableViewBottomConstraint" destination="62D-eM-GTg" id="Ate-Y7-20d"/>
<outlet property="stickyHeadersBottomContainer" destination="EXH-mK-0eB" id="95Y-KP-bwF"/>
<outlet property="stickyHeadersBottomContainerBottomConstraint" destination="yI8-Pp-wkV" id="9BC-o2-qJZ"/>
<outlet property="stickyHeadersBottomContainerHeightConstraint" destination="SNq-Js-N7s" id="vom-iM-s6W"/>
<outlet property="stickyHeadersTopContainer" destination="JJC-Bw-6sa" id="JIy-sf-4Ya"/>
<outlet property="stickyHeadersTopContainerHeightConstraint" destination="xT1-rL-nCC" id="VaK-0W-2Mi"/>
@@ -61,18 +61,18 @@
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="orV-HH-88x" secondAttribute="bottom" id="62D-eM-GTg"/>
<constraint firstItem="JJC-Bw-6sa" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="8yW-fA-QxN"/>
<constraint firstItem="orV-HH-88x" firstAttribute="top" secondItem="JJC-Bw-6sa" secondAttribute="bottom" id="IMR-dV-gUS"/>
<constraint firstItem="EXH-mK-0eB" firstAttribute="bottom" secondItem="orV-HH-88x" secondAttribute="bottom" id="KXF-3M-x1M"/>
<constraint firstItem="orV-HH-88x" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="NaR-eJ-WMj"/>
<constraint firstAttribute="trailing" secondItem="EXH-mK-0eB" secondAttribute="trailing" id="P7b-WG-atu"/>
<constraint firstItem="EXH-mK-0eB" firstAttribute="top" secondItem="orV-HH-88x" secondAttribute="bottom" id="Qo8-nH-lh3"/>
<constraint firstItem="Zbr-9e-VZh" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="aRg-Nz-enq"/>
<constraint firstItem="Zbr-9e-VZh" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="fty-XB-tTr"/>
<constraint firstAttribute="trailing" secondItem="Zbr-9e-VZh" secondAttribute="trailing" id="rKb-TZ-sap"/>
<constraint firstItem="JJC-Bw-6sa" firstAttribute="top" secondItem="Zbr-9e-VZh" secondAttribute="bottom" id="vW3-kY-qjQ"/>
<constraint firstItem="EXH-mK-0eB" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="wSx-rS-Lv5"/>
<constraint firstAttribute="trailing" secondItem="orV-HH-88x" secondAttribute="trailing" id="yBp-63-kZi"/>
<constraint firstAttribute="bottom" secondItem="EXH-mK-0eB" secondAttribute="bottom" id="yI8-Pp-wkV"/>
<constraint firstAttribute="trailing" secondItem="JJC-Bw-6sa" secondAttribute="trailing" id="zTe-LM-S3a"/>
</constraints>
</view>