SwiftUI ScrollView 不会将内容与 GeometryReader 居中

SwiftUI ScrollView won't center content with GeometryReader

我在 SwiftUI 中有一个水平滚动视图,我想 'dynamically' 居中。滚动视图中的内容将是一个动态图表,用户可以通过改变年份来改变它的宽度(5、10、15 年图表水平增长)。

无论如何我有两个问题:

  1. 我在水平滚动视图中的内容似乎是左对齐的,任何使用填充将其居中的尝试只会使其偏离居中,并且难以考虑各种屏幕尺寸。

  2. 当我尝试使用 GeometryReader 时,底部图表的 'legend' 被推到页面底部,Spacer() 无法修复它。

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 是当前代码的样子,right 是我希望的样子,不管屏幕如何 size/iPhone型号。

注意这就是我使用滚动视图的原因:

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

GeometryReader 移到 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))
                }
            }
        }
    }
}

注意:水平滚动视图内容总是从左边开始(不是对齐方式)。如果您只想在屏幕上居中 HStack - 删除 ScrollView.

为了使您的图表在 ScrollView 中正确居中,请将内容的最小宽度设置为与 ScrollView 本身的宽度相同。为此,请在 ScrollView 内的 HStack 上调用 .frame(minWidth: geo.size.width)。可以找到此解决方案的完整解释 .

图例被推到屏幕底部的原因是 GeometryReader 本身就是一个视图,可以扩展以填充所有可用的 space。我可以想到两种解决方案来解决这个问题。

解决方案 1 - 静态图表高度

如果图表的高度是静态的,或者如果您在呈现视图之前知道高度,则可以使用该已知尺寸来设置 GeometryReader.

struct ChartView: View {   

    private let chartHeight: CGFloat = 300 // declare a height parameter

    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()
                       }
                    }
                    .frame(minWidth: geo.size.width)
                }
                .background(Color.gray)
            }
            .frame(height: chartHeight) // set the height of the GeometryReader
            
            //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))
            }
        }

    }

}

解决方案 2 - 动态图表高度

如果你想让图表的高度是动态的,让ScrollView根据内容来决定它的高度,你可以使用Spacers()并调用.layoutPriority(0)方法在他们上面,以便 GeometryReader 和 spacers 在展示他们的观点时得到同等的优先权。使用此解决方案,GeometryReader 只会在 ScrollView.

周围放置一个小的默认填充

如果您希望图例更接近 ScrollView,您可以选择使用负填充值。

struct ChartView: View {   

    var body: some View {

        VStack(alignment: .center) {

            Spacer().layoutPriority(0) // add a spacer above

            GeometryReader { geo in
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack {
                       ForEach(0..<self.chartSettings.getChartYears(), id:\.self) { index in
                           ColumnView()
                       }
                    }
                    .frame(minWidth: geo.size.width)
                }
                .background(Color.gray)
            }
            
            //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))
            }
            .padding(.top, -20) // optionally move the legend closer to the ScrollView

            Spacer().layoutPriority(0) // add a spacer below

        }

    }

}