1

I have a horizontal scrollView which shows a bunch of images from a dynamic image array. When the total content width is greater than the screen width everything is fine. However if say there is only one image in the array, it will be leading aligned which is the default behavior.

I want the content to be centered when the total content width is less than screen width and not sure how to achieve that:

ScrollView(.horizontal) {
    HStack {
        ForEach(images.indices, id: \.self) { index in
            let image = images[index] {
                Image(uiImage(uiImage: image))
            }
        }
    }
    .frame(height: 200)
}

So in this case if there is only one image it is leading aligned but I want it to be centered until the content fills the screen then for it to scroll. I don't mind bounce or no bounce until it fills screen.

alionthego
  • 8,508
  • 9
  • 52
  • 125
  • 1
    Here is same for height https://stackoverflow.com/a/62878812/12299030 - you just need to replicate it for width. – Asperi Aug 12 '21 at 07:40
  • Other possible approach is to fit ScrollView to content when it is less then screen, like in https://stackoverflow.com/a/61185571/12299030. – Asperi Aug 12 '21 at 07:44

1 Answers1

0

You can use GeometryReader and set a minWidth on the content of the ScrollView.

Example (images swapped for colors for testing purposes):

struct ContentView: View {
    @State private var width: CGFloat?
    private let total = 10//0

    var body: some View {
        ScrollView(.horizontal) {
            HStack {
                ForEach(0 ..< total) { index in
                    Color.red.opacity(Double(index) / Double(total) + 0.05)
                        .frame(width: 20)
                }
//                ForEach(images.indices, id: \.self) { index in
//                    let image = images[index] {
//                        Image(uiImage(uiImage: image))
//                    }
//                }
            }
            .frame(height: 200)
            .frame(minWidth: width)
        }
        .widthReader { width in
            self.width = width
        }
    }
}

Custom widthReader(_:) modifier:

extension View {
    func widthReader(_ width: @escaping (CGFloat?) -> Void) -> some View {
        modifier(WidthReaderModifier(width: width))
    }
}

fileprivate struct WidthReaderModifier: ViewModifier {
    private struct WidthPreferenceKey: PreferenceKey {
        static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
            value = nextValue()
        }
    }

    let width: (CGFloat?) -> Void

    func body(content: Content) -> some View {
        content
            .background(
                GeometryReader { geo in
                    Color.clear
                        .preference(key: WidthPreferenceKey.self, value: geo.size.width)
                        .onAppear {
                            width(geo.size.width)
                        }
                }
            )
            .onPreferenceChange(WidthPreferenceKey.self) { width in
                self.width(width)
            }
    }
}
George
  • 25,988
  • 10
  • 79
  • 133