Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - How to center a component in a horizontal ScrollView when tapped?

I have a horizontal ScrollView, and within it an HStack. It contains multiple Subviews, rendered by a ForEach. I want to make it so that when these Subviews are tapped, they become centered vertically in the view. For example, I have:

ScrollView(.horizontal) {
    HStack(alignment: .center) {
        Circle() // for demonstration purposes, let's say the subviews are circles
            .frame(width: 50, height: 50)
        Circle()
            .frame(width: 50, height: 50)
        Circle()
            .frame(width: 50, height: 50)
    }
    .frame(width: UIScreen.main.bounds.size.width, alignment: .center)
}

I tried this code:

ScrollViewReader { scrollProxy in
    ScrollView(.horizontal) {
        HStack(alignment: .center) {
            Circle()
                .frame(width: 50, height: 50)
                .id("someID3")
                .frame(width: 50, height: 50)
                .onTapGesture {
                    scrollProxy.scrollTo(item.id, anchor: .center)
                }
            Circle()
                .frame(width: 50, height: 50)
                .id("someID3")
                .frame(width: 50, height: 50)
                .onTapGesture {
                    scrollProxy.scrollTo(item.id, anchor: .center)
                }

            ...
        }
    }

But it seemingly had no effect. Does anyone know how I can properly do this?

like image 571
Nicolas Gimelli Avatar asked Oct 20 '25 04:10

Nicolas Gimelli


1 Answers

You can definitely do this with ScrollView and ScrollViewReader. However, I see a couple of things that could cause problems in your code sample:

  • You use the same id "someID3" twice.
  • I can't see where your item.id comes from, so I can't tell if it actually contains the same id ("someID3").
  • I don't know why you have two frames with the same bounds on the same view area. It shouldn't be a problem, but it's always best to keep things simple.

Here's a working example:

import SwiftUI

@main
struct MentalHealthLoggerApp: App {
    var body: some Scene {
        WindowGroup {
            ScrollViewReader { scrollProxy in
                ScrollView(.horizontal) {
                    HStack(alignment: .center, spacing: 10) {
                        Color.clear
                            .frame(width: (UIScreen.main.bounds.size.width - 70) / 2.0)
                        ForEach(Array(0..<10), id: \.self) { id in
                            ZStack(alignment: .center) {
                                Circle()
                                    .foregroundColor(.primary.opacity(Double(id)/10.0))
                                Text("\(id)")
                            }
                            .frame(width: 50, height: 50)
                            .onTapGesture {
                                withAnimation {
                                    scrollProxy.scrollTo(id, anchor: .center)
                                }
                            }
                            .id(id)
                        }
                        Color.clear
                            .frame(width: (UIScreen.main.bounds.size.width - 70) / 2.0)
                    }
                }
            }
        }
    }
}

Here you can see it in action:

[EDIT: You might have to click on it if the GIF won't play automatically.]

You can see the animated scrolling effect.

Note that I added some empty space to both ends of the ScrollView, so it's actually possible to center the first and last elements as ScrollViewProxy will never scroll beyond limits.

like image 147
theMomax Avatar answered Oct 21 '25 17:10

theMomax



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!