How do you go about creating an image carousel with SwiftUI, iOS 15.0+?

I need to create a carousel component with the following requirements (sorted by relevance):

Objectives

  1. Every image is 16:9 aspect ratio and resizes to fit the screen.
  2. Needs a zoom and pan functionality, possibly the same way as iOS Photos app.
  3. Needs to work both in landscape and portrait mode, with a smooth transition between orientations.
  4. When orientation changes, the image needs to be rotated to preserve the center of the image (like Photos app and hyperoslo/Lightbox)
  5. The component should only take the minimum necessary space. In most use cases, such component should have other subviews both above and below.
  6. Circularity. I would like the carousel to wrap around.

What I tried:

  1. 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).

  2. Implementing a single page (that is, an image view) using an UIScrollView and an UIViewRepresentable, then collecting them into an HStack.

    Unfortunately I need to use zoomScale and contentOffset properties of the UIScrollView outside of the UIViewRepresentable 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.

  3. Implementing the whole carousel using UIKit, and an UICollectionView, whose cells were an instance of UIScrollView.

    The problem is, the UIScrollView needs to recompute its constraints upon rotation but a cell is an instance of UIView, so it can't respond to rotations via viewWillTransition(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 same UICollectionView). Also when rotating, it looks like the UIScrollView of the visible cell is being recreated, making it impossible to preserve the image center (I use this subclass of UIScrollView for this purpose). And the UICollectionView 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.

Replies

Anyone?

  • Please I appreciate any general suggestion

Add a Comment

Please I appreciate any general suggestion