scrollToItem() causes compositional layout to glitch when using paging - ios

I'm using UICollectionViewCompositionalLayout and the *UICollectionLayoutSectionOrthogonalScrollingBehavior to build a carousel.
The scrollToItem() method will correctly scroll to the item at the specified indexPath but will create a top-inset (animates it smoothly). It doesn't inset the rest of the content though, which will make the cell overlay subsequent content. (Here a section footer with a page control)
This top-inset will be the size of a section header + the contentInset of the layout section.
Any subsequent scrolling initiated by the user will cause the layout to update and make the cell jump to the correct position.
The problem occurs only when using one of the .paging behaviours for orthogonal scrolling.
I've tried to mess with the layout item in visibleItemsInvalidationHandler but I have not found a way to distinguish whether the handler is called because the user scrolled or because the scrollToItem() method was called programmatically.
Check out the sample project on github
I'm using Xcode 13.0 and iOS 15.0

Related

UICollectionView header height animation

I have the following scenario...
When I initially load a UICollectionView, I need it to slide up from the bottom of the screen without a header. This is pretty easy.
There is an "Add" cell that takes the user through the process of adding an item. At the end of this process, we display the list again, but this time with a header. The header needs to fade in and, at the same time, the updated list slides up from the bottom.
The requirement is that the header scroll with the list after both are in place, which is pretty much the default behavior.
The problem I'm having is coming up with a workable method for animating the list slide while the header is displayed.
One thought is simply animating the height of the header. Basically, start it with a height equal to the view height, then animate it to its final size. This would automatically draw the rest of the list up, making it look as though it were sliding in.
I've tried several variations of this method with no success. I can set the height without a problem, but I haven't been able to animate it.
I had thought just returning the appropriate height from referenceSizeForHeaderInSection and reloading the data would handle it. At least that's what I gather from SO messages. I've also tried invalidating the layout and performBatchUpdates.
Would this be simpler if I placed the contents of my header in the first row of the collection view and then try animating the height of row 0?
I'm not sure which is the best strategy.
OK, I found the following which was easily adapted to my scenario...
iOS Animating UITableView Header
Here's a Wayback Machine link to the original post. It may take a little while to load. Be patient.
Wayback Machine: iOS Animating UITableView Header
And here's a GitHub link with sample code...
GitHub: MichiganLabs / AnimatingTableViewHeader

UIStackView layout issues when changing size class in UITableViewCell

I'm having layout issues with my horizontal UIStackView in a custom UITableViewCell when showing/hiding arrangedSubviews when the horizontalSizeClass changes.
My stack view contains a number of subviews, each of which, depending on cell configuration and size class, are either hidden or shown. The UIStackView is designed to handle arranging the shown views, but upon rotation, layout issues arise.
Issues:
Sometimes, the appropriate subviews are either not shown or not hidden when they ought to be.
Sometimes, the subviews are inappropriately laid out, not filling the stack view's width.
Attempts:
I've attempted a number of things to address the layout:
Overriding viewWillTransitionToSize:transitionCoordinator to reload the table and/or force layout
Overriding viewWillTransitionToTraitCollection:withTransitionCoordinator to reload the table and/or force layout
Overriding layoutSubviews to re-configure the stack view's arrangedSubviews
Calling [self setNeedsLayout], [self layoutIfNeeded] after configuring the cell
Forcing layout in other places
Changing subview layout constraint priorities to 999
Limiting the UILabels to 1 line, and setting a preferredMaxLayoutWidth
Adjusting the contentCompressionResistance and contentHuggingPriority on views
Using a static value for rowHeight instead of UITableViewAutomaticDimension
Etc.
Nothing appears to fix the issues.
Furthermore, even when cells are scrolled off/on screen, prepared for reuse, and re-configured, issues either persist, go away, or new issues are introduced, despite the fact that I'm properly resetting the cell on prepareForReuse.
Sample Project
I've created a sample project to illustrate the layout issues. At this point, I'm unsure if UIStackView is buggy or if I'm misusing it.
Sample Project: https://github.com/bradgmueller/StackViewTest
The sample project uses a custom UITableViewCell with views configured in the xib. Row objects are generated with different configurations to illustrate the dynamic layouts the cell should adopt:
Indented / not indented
Showing separator or not
Showing / hiding a "like" button
Showing / hiding a "Share" button
Showing / hiding an Info button, where one info button exists for UIUserInterfaceSizeClassCompact and another for UIUserInterfaceSizeClassRegular
A text label exists with text indicating which of the views ought to be displayed, to help illustrate when views are inappropriately shown/hidden. Also, a red background view exists behind the UIStackView to illustrate when the stack view is failing to Fill the width.
Screenshots:
Initial layout - no issues
After rotating - issues marked with red "X"
I'd appreciate any insight, thanks in advance!!
Generally stack views are pretty good, but when they get more complicated and manage more views, they appear to be less reliable.
I found that the best way to avoid the layout issues is to do one or two things:
Use multiple stack views. Instead of bunching all of my views into the same stack view, having multiple smaller stack views seems to be more reliable, as each is managing fewer views.
Remove / Re-insert views, rather than hide them. Though stack views are supposed to treat "hidden" views as if they are removed from the hierarchy, I got much better layout results when I actually removed them from the hierarchy.
Ex:
More reliable layout results:
if (hideSubview == YES) {
[subview removeFromSuperview];
} else {
[stackView insertArrangedSubview:subview atIndex:0];
}
Not as reliable:
subview.hidden = hideSubview;

CSStickyHeaderFlowLayout Header Frame

EDIT: I have create a very small app that represents perfectly the problem. https://drive.google.com/file/d/0B6sI4Feh1HJUb3pGa2pBUmY4QW8/view?usp=sharing
In the sample app, we just need to scroll down then press the button on the top bar to see the problem I am having
I am using https://github.com/jamztang/CSStickyHeaderFlowLayout to have sticky headers behaviors (like default UITableView) inside my collection view.
It works pretty well when scrolling through the collection view. I have a search bar outside the collectionview that allows users to filter the data with search text, everytime the user enters a letter, I'm refreshing the collectionview's data with the found data.
The problem is let's say there is currently 4 sections inside the collection view and that it is scrolled completely at the bottom. When I input a certian letter, it filters out everything but a single item (with a single header). The content size then changes for the collection view and displays the proper data, but then the header is too low (see screenshot).
I have investigated inside the flowlayout and inside the layoutAttributesForElementsInRect and I see it actually set the frame's origin Y to 0 (like it should be), but it seems the collectionview doesn't use this value and uses the previous one (which was when the collection view was scrolled down).
Any idea what could cause the UICollectionView to not use the desired frame inside layoutAttributesForElementsInRect ?
I tried to fix this issue, but it looks like it's impossible till UICollectionViewLayout mechanism is black box for us. Custom layout correctly returns updated frame to layout engine on reloadData:
But hidden engine not even asking attributes if they are equal. In your case you can use simple workaround. Just update contentOffset after reload.:
[self.collectionView reloadData];
self.collectionView.contentOffset = CGPointZero;

iOS8 When using VoiceOver content of the collection view is scrolled to beginning

I have simple collection view created programmatically with build in horizontal layout and 50 items. When I enable VoiceOver to speak content of each cell, scroll position is changed to beginning on first item.
I would like to preserve scroll position. Am I missing something when VoiceOver is enabled?
With voice over disabled it works without issues.
Good to mention it works without issues on iOS7. I am using xcode 6.

Strange behavior with horizontal scrolling UITableView (in Monotouch)

We have a UITableView that we have configured to act like a Grid, allowing both horizontal and vertical scrolling. We accomplish this by dynamically changing the ContentSize in a custom UITableView's LayoutSubviews method, which helps with autorotation, scrolling, etc.
Everything works as expected except on a couple of our larger grids. When these grids are scrolled horizontally (swipe left), as soon as the ContentOffset.X is greater than or equal to the Bounds.Width, the table view disappears. It is still present and receives input, but nothing is painted. On swiping back to the right, as soon as the width threshold is crossed, everything is repainted.
In situations where the size of the grid is less than half of the configured UITableView width, this issue does not occur. We cannot change the size of the grids because they are configured by customers in the field who expect the data to be available.
I have checked and/or removed as much of our custom drawing code as possible and the issue is still occurring. Does anyone have any ideas?
That is because although the UITableView inherits from the UIScrollView, it is only meant to have a single column, hence it draws its cells according to its Bounds.
If you want to create a grid-like control with the UITableView, I suggest using multiple table views in a parent UIScrollView side-byside and change that one's ContentSize.
I came across this same issue and posted a bug report (10561166) back in December on the Apple Developer site.
I received a response today from Apple in which they stated that
UITableView is not designed or intended for horizontal scrolling.
(I had since changed my design not to require the horizontal scrolling.)

Resources