SwiftUI ScrollView不会使GeometryReader的内容居中

时间:2020-07-22 13:59:16

标签: ios swift swiftui scrollview geometryreader

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

无论如何我有两个问题:

  1. 我在水平滚动视图中的内容似乎向左对齐,任何尝试使用填充居中的尝试都只是使它偏离居中状态,从而难以考虑各种屏幕尺寸。

  2. 当我尝试使用GeometryReader时,图表底部的“图例”被推到页面底部,而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))
            }
        }
    }
}

是当前代码的外观,是我希望的外观,无论屏幕大小/ iPhone型号如何。

enter image description here

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

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

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

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

图例被推到屏幕底部的原因是GeometryReader本身就是一个视图,它会扩展以填充所有可用空间。我认为有两种解决方案可以解决此问题。

解决方案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和分隔符在布置其视图时具有相同的优先级。使用此解决方案,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

        }

    }

}