ios SwiftUI图表覆盖对象定位不正确

wnavrhmk  于 2023-04-08  发布在  iOS
关注(0)|答案(1)|浏览(168)

我正在努力在条形图上叠加一个用户可以点击和拖动以获得更多信息的图标。
我在以前的应用程序中使用了相同的代码(针对数据类型进行了修改),它运行得很好。这让我觉得框架中的某些东西在xcode 14.3中发生了变化。
用户应该能够点击和拖动,并看到数据的酒吧-在这种情况下,简单的年份和数字。
但是,当点击此处图表时,矩形指针的位置不正确,但气泡中报告的数据是正确的。在这种情况下,指针偏离一个条形,但这是巧合-如果有更多的数据点,错误将超过一个条形。
同样奇怪的是,如果我注解掉矩形指针的偏移量,它会消失。如果我将偏移量设置为0.0,它会消失。在这两种情况下,设备上的点击点或模拟器中的光标仍然显示正确的数据。
下面是代码的简化版本:

struct ContentView: View {

    @State private var showSelectionBar = false
    @State private var offsetX: Double = 0.0
    @State private var offsetY = 0.0

    @State private var thisSelectedYear = "2010"
    @State private var selectedYearGeneration: Double = 0.0

    let tempColors: [Color] = [.blue, .green, .yellow, .orange, .red]

    var body: some View {
    
        VStack(spacing: 10) {
            Text("Some Data")
                .font(.title2)
            GroupBox {
                Chart(Datum.exampleData) { item in
                    BarMark(
                        x: .value("Year", String(item.period)),
                        y: .value("Generation", (item.generation))
                    )
                    .foregroundStyle(by: .value("Type", item.type))
                }
            }//group box
            .frame(height: 500)
            .padding()
            .chartYScale(domain: 0...40)
            .chartYAxis {
                AxisMarks(
                    values: stride(
                        from: 0,
                        to: 50,
                        by: 10).map { $0 }
                )
            }//chart y axis
            .chartXAxis {
                AxisMarks(position: .bottom, values: .automatic) { value in
                    if ((value.index % 2) != 0) {
                        AxisValueLabel() {
                            if let strValue = value.as(String.self) {
                                Text(strValue)
                                    .font(.caption)
                                    .rotationEffect(Angle(degrees: 45))
                                    .padding(.top)
                            }//if let value as string
                        }//axis value label
                    }//if
                }//axis marks
            }//chart x axis
            .chartLegend(spacing: 20)
        
            .chartOverlay { pr in
                GeometryReader { geoProxy in
                
                    Rectangle()
                        .foregroundStyle(Color.orange.gradient)
                        .frame(width: 2, height: geoProxy.size.height * 0.95)
                        .opacity(showSelectionBar ? 1.0 : 0.0)
                    //this is the line
                        .offset(x: offsetX)
                    //above is the line
                    Capsule()
                        .foregroundStyle(.blue.gradient)
                        .frame(width: 160, height: 80)
                        .overlay {
                            VStack {
                                Text(thisSelectedYear)
                                Text("Generation: \(selectedYearGeneration, specifier: "%.0f")")
                            }
                            .foregroundStyle(.white)
                        }
                        .opacity(showSelectionBar ? 1.0 : 0)
                        .offset(x: offsetX - 50, y: offsetY - 100)
                    Rectangle().fill(.clear).contentShape(Rectangle()).gesture(DragGesture()
                        .onChanged { value in
                            if !showSelectionBar {
                                showSelectionBar = true
                            }
                            let origin = geoProxy[pr.plotAreaFrame].origin
                            let location = CGPoint(
                                x: value.location.x - origin.x,
                                y: value.location.y - origin.y
                            )
                            print(UIScreen.main.bounds.size.width)
                            print("location: \(location.x)")
                            offsetX = location.x
                            offsetY = location.y
                        
                            let (year, _) = pr.value(at: location, as: (String, Double).self) ?? ("-", 0)
                            print("year is" , year)
                        
                            thisSelectedYear = year
                        
                            let selectedDatum: Datum = Datum.exampleData.first(where: {
                                $0.period == Int(thisSelectedYear)
                            }) ?? Datum.exampleData[0]
                        
                            selectedYearGeneration = selectedDatum.generation
                        
                        }//on changed
                        .onEnded{ _ in
                            showSelectionBar = false
                        })//gesture
                }//geo proxy
            }//overlay
        
            Spacer()
        }//v
    }//body
}//struct


// MARK: - Datum
struct Datum: Identifiable {
    let id = UUID()
    let period: Int
    let type: String
    let generation: Double

    static let exampleData: [Datum] = [
        Datum(period: 2001, type: "A", generation: 10.0),
        Datum(period: 2002, type: "A", generation: 15.0),
        Datum(period: 2003, type: "A", generation: 5.0),
        Datum(period: 2003, type: "B", generation: 10.0),
        Datum(period: 2003, type: "C", generation: 10.0),
        Datum(period: 2004, type: "A", generation: 25.0),
        Datum(period: 2005, type: "A", generation: 30.0),
        Datum(period: 2006, type: "A", generation: 27.0),
        Datum(period: 2007, type: "A", generation: 22.0),
        Datum(period: 2007, type: "B", generation: 12.0),
        Datum(period: 2008, type: "A", generation: 17.0),
        Datum(period: 2008, type: "B", generation: 12.0),
        Datum(period: 2009, type: "A", generation: 12.0),
        Datum(period: 2010, type: "A", generation: 7.0)
    ]

}//struct

如有任何指导,敬请谅解。

i7uq4tfw

i7uq4tfw1#

当在SwiftUI中处理没有做它们应该做的事情的视图时,我的第一个设备是非常低技术的。为事物添加颜色以查看它们是如何布局的。你在代码中看不到的东西,在着色后变得非常明显。
在您测量阻力的Rectangle()上,我将.clear更改为.purple.opacity(0.4),这是我看到的:

这个问题有点突然出现在你面前。你有一个Chart在一个GroupBox中,周围有一个.frame(height: 500),然后是.padding()Rectangle()在这些修饰符之外,因此不受它们的约束,所以它最终是包括填充的总大小,而GroupBox有一个内部填充,所以它充当一个框架,然后.padding()增加了更大的差异。所以,你是正确的,因为它与酒吧的大小无关,而是事实上Rectangle()Chart()的大小不一样。
要修复它,只需移动.chartOverlay(),并且为了统一起见,移动GroupBox的其他图表修改器独立组件。

struct ContentView: View {
    @State private var showSelectionBar = false
    @State private var offsetX: Double = 0.0
    @State private var offsetY = 0.0

    @State private var thisSelectedYear = "2010"
    @State private var selectedYearGeneration: Double = 0.0

    let tempColors: [Color] = [.blue, .green, .yellow, .orange, .red]

    var body: some View {
    
        VStack(spacing: 10) {
            Text("Some Data")
                .font(.title2)
            GroupBox {
                Chart(Datum.exampleData) { item in
                    BarMark(
                        x: .value("Year", String(item.period)),
                        y: .value("Generation", (item.generation))
                    )
                    .foregroundStyle(by: .value("Type", item.type))
                }
                .chartYScale(domain: 0...40)
                .chartYAxis {
                    AxisMarks(
                        values: stride(
                            from: 0,
                            to: 50,
                            by: 10).map { $0 }
                    )
                }//chart y axis
                .chartXAxis {
                    AxisMarks(position: .bottom, values: .automatic) { value in
                        if ((value.index % 2) != 0) {
                            AxisValueLabel() {
                                if let strValue = value.as(String.self) {
                                    Text(strValue)
                                        .font(.caption)
                                        .rotationEffect(Angle(degrees: 45))
                                        .padding(.top)
                                }//if let value as string
                            }//axis value label
                        }//if
                    }//axis marks
                }//chart x axis
                .chartLegend(spacing: 20)
            
                .chartOverlay { pr in
                    GeometryReader { geoProxy in
                    
                        Rectangle()
                            .foregroundStyle(Color.orange.gradient)
                            .frame(width: 2, height: geoProxy.size.height * 0.95)
                            .opacity(showSelectionBar ? 1.0 : 0.0)
                        //this is the line
                            .offset(x: offsetX)
                        //above is the line
                        Capsule()
                            .foregroundStyle(.blue.gradient)
                            .frame(width: 160, height: 80)
                            .overlay {
                                VStack {
                                    Text(thisSelectedYear)
                                    Text("Generation: \(selectedYearGeneration, specifier: "%.0f")")
                                }
                                .foregroundStyle(.white)
                            }
                            .opacity(showSelectionBar ? 1.0 : 0)
                            .offset(x: offsetX - 50, y: offsetY - 100)
                        Rectangle().fill(.clear).contentShape(Rectangle()).gesture(DragGesture()
                            .onChanged { value in
                                if !showSelectionBar {
                                    showSelectionBar = true
                                }
                                let origin = geoProxy[pr.plotAreaFrame].origin
                                let location = CGPoint(
                                    x: value.location.x - origin.x,
                                    y: value.location.y - origin.y
                                )
                                print(UIScreen.main.bounds.size.width)
                                print("location: \(location.x)")
                                offsetX = location.x
                                offsetY = location.y
                            
                                let (year, _) = pr.value(at: location, as: (String, Double).self) ?? ("-", 0)
                                print("year is" , year)
                            
                                thisSelectedYear = year
                            
                                let selectedDatum: Datum = Datum.exampleData.first(where: {
                                    $0.period == Int(thisSelectedYear)
                                }) ?? Datum.exampleData[0]
                            
                                selectedYearGeneration = selectedDatum.generation
                            
                            }//on changed
                            .onEnded{ _ in
                                showSelectionBar = false
                            })//gesture
                    }//geo proxy
                }//overlay
            }//group box
            .frame(height: 500)
            .padding()

            Spacer()
        }//v
    }//body
}//struct

相关问题