Search code examples
iosswiftswiftuiscrollviewgeometryreader

SwiftUI ScrollView won't center content with GeometryReader


I have a horizontal ScrollView in SwiftUI that I would like to 'dynamically' center. the content within the scrollview will be a dynamic chart, and the user can change the width of it by changing years (5, 10, 15 year chart grows horizontally).

Anyway I have two problems:

  1. My content in the horizontal scrollview seems to be aligned left, and any attempt at centering it with padding is just leaving it off centered and makes it difficult to account for various screen sizes.

  2. When I try using a GeometryReader, my chart's 'legend' at the bottom gets shoved to the bottom of the page, Spacer() doesn't fix it.

struct ChartView: View {   
    var body: some View {
        VStack(alignment: .center) {
            GeometryReader { geo in
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack {
                       ForEach(0..<self.chartSettings.getChartYears(), id:\.self) { index in
                           ColumnView()
                       }
                    }
                }
                .background(Color.gray)
                .frame(maxHeight: geo.size.height/2)
            }
            
            
            //Chart Legend
            HStack{
                Rectangle()
                    .frame(width: 10, height: 10)
                    .foregroundColor(Color.init(#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)))
                Text("First Value")
                    .font(.system(size: 12))
                
                Rectangle()
                    .frame(width: 10, height: 10)
                    .foregroundColor(Color.init(#colorLiteral(red: 0.2263821661, green: 0.4659538441, blue: 0.08977641062, alpha: 1)))
                Text("Second Value")
                    .font(.system(size: 12))
            }
        }
    }
}

Left is what it looks like with current code and right is what I'm hoping it looks like regardless of screen size/iPhone model.

enter image description here

Note This is the reason I'm using a scrollview:

<iframe src='https://gfycat.com/ifr/DeficientNiceInchworm' frameborder='0' scrolling='no' allowfullscreen width='400' height='210'></iframe>


Solution

  • Move GeometryReader outside of VStack:

    struct ChartView: View {
        var body: some View {
            GeometryReader { geo in
                VStack(alignment: .center) {
                    ScrollView(.horizontal, showsIndicators: false) {
                        HStack {
                           ForEach(0..<self.chartSettings.getChartYears(), id:\.self) { index in
                               ColumnView()
                           }
                        }
                    }
                    .background(Color.gray)
                    .frame(maxHeight: geo.size.height/2)
    
                    //Chart Legend
                    HStack{
                        Rectangle()
                            .frame(width: 10, height: 10)
                            .foregroundColor(Color.init(#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)))
                        Text("First Value")
                            .font(.system(size: 12))
    
                        Rectangle()
                            .frame(width: 10, height: 10)
                            .foregroundColor(Color.init(#colorLiteral(red: 0.2263821661, green: 0.4659538441, blue: 0.08977641062, alpha: 1)))
                        Text("Second Value")
                            .font(.system(size: 12))
                    }
                }
            }
        }
    }
    

    Note: horizontal scroll view content always start from left (it is not alignment). If you want just center HStack on screen - remove ScrollView.