I need to create a carousel component with the following requirements (sorted by relevance):
Objectives
- Every image is 16:9 aspect ratio and resizes to fit the screen.
- Needs a zoom and pan functionality, possibly the same way as iOS Photos app.
- Needs to work both in landscape and portrait mode, with a smooth transition between orientations.
- When orientation changes, the image needs to be rotated to preserve the center of the image (like Photos app and hyperoslo/Lightbox)
- The component should only take the minimum necessary space. In most use cases, such component should have other subviews both above and below.
- Circularity. I would like the carousel to wrap around.
What I tried:
-
Using a
TabView
with.tabViewStyle(PageTabViewStyle()).indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
modifiers.This didn't work: rotating the interface caused the view to get stuck between pages (it looks like it's a well known [bug]).(https://stackoverflow.com/questions/72435939/swiftui-tabview-is-not-working-properly-on-orientation-change).
-
Implementing a single page (that is, an image view) using an
UIScrollView
and anUIViewRepresentable
, then collecting them into anHStack
.Unfortunately I need to use
zoomScale
andcontentOffset
properties of theUIScrollView
outside of theUIViewRepresentable
itself. The net result was that.init()
was invoked for every image in the carousel at every rotation, causing severe stutters and an horrible rotation animation. -
Implementing the whole carousel using UIKit, and an
UICollectionView
, whose cells were an instance ofUIScrollView
.The problem is, the
UIScrollView
needs to recompute its constraints upon rotation but a cell is an instance ofUIView
, so it can't respond to rotations viaviewWillTransition(to:CGSize, with: any UIViewControllerTransitionCoordinator)
.In the
UICollectionView
itself, you can only access the visible cells (one at a time is visible), and cells are cached, so even after rotating, some occasionally are presented on screen with the same appearance as before the rotation (some do, some don't, in the sameUICollectionView
). Also when rotating, it looks like theUIScrollView
of the visible cell is being recreated, making it impossible to preserve the image center (I use this subclass ofUIScrollView
for this purpose). And theUICollectionView
is taking the full window size, not just the bare minimum necessary space.
Help:
With all of this in mind, what options do I realistically have? If necessary I can raise the minimum iOS version to 16.0, even though I guess it doesn't make any significative difference since SwiftUI introduced MagnifyGesture
only from iOS 17.0.