mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-06 07:57:42 +02:00
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user