我在SwiftUI中有一个表示股票assets资源 价格的图表.用户可以在呈现与拖动手势一起移动的覆盖线视图的图形上拖动.此覆盖视图的目的是确定用户正在分析的图形的特定点.

当用户拖动图形时,一切工作正常,视图出现,但覆盖线视图不稳定,不能顺畅地移动.唯一的动画是显示覆盖和隐藏它,这只在手势的开始和结束时完成,所以我不确定为什么我会得到这个错误的用户界面.

如果我将这条线拖来拖go 几秒钟,控制台中会出现以下警告:

无效的示例AnimatablePair<AnimatablePair<CGFloat,CGFloat>,AnimatablePair<CGFloat,CGFloat;>(第一:SwiftUI.AnimatablePair<CoreGraphics.CGFloat,核心图形.CGFloat>(第一:11.0,第二:0.0),第二:SwiftUI.AnimatablePair<CoreGraphics.CGFloat,核心图形.CGFloat>(第一:0.0,第二:0.0),时间(秒:0.0)>上次时间(秒:0.01664249994792044)`

代码:

 GraphView(coin: data).frame(height: 300)
        .gesture(DragGesture().onChanged({ value in
            if abs(value.translation.width) > abs(value.translation.height) {
                withAnimation { showPlot = true }
                barTranslation = value.location.x
            }
        }).onEnded({ value in
            withAnimation { showPlot = false }
        }).updating($isDrag, body: { value, out, _ in
            out = true
        }))
        .onChange(of: isDrag) { _ in
            if !isDrag {
                showPlot = false
            }
        }
        .overlay(alignment: .bottomLeading) {
            VStack(spacing: 0){
                Text(currentPlot)
                    .font(.caption.bold())
                    .foregroundStyle(.gray)
                    .padding(.bottom, 35)
                    .position(x: barTranslation)
                Rectangle()
                    .fill(.gray)
                    .frame(width: 1, height: 300)
                    .position(x: barTranslation)
                Spacer()
            }
            .opacity(showPlot ? 1 : 0)
        }

推荐答案

Instead of using withAnimation inside the DragGesture().onChanged, try and update the position of the overlay directly. That should give you a smoother drag experience.
And keep the animations for showing and hiding the overlay in the .onEnded part of the gesture.

GraphView(coin: data).frame(height: 300)
    .gesture(DragGesture().onChanged({ value in
        if abs(value.translation.width) > abs(value.translation.height) {
            showPlot = true
            barTranslation = value.location.x
        }
    }).onEnded({ value in
        withAnimation { showPlot = false }
    }).updating($isDrag, body: { value, out, _ in
        out = true
    }))
    .onChange(of: isDrag) { _ in
        if !isDrag {
            withAnimation { showPlot = false }
        }
    }
    .overlay(alignment: .bottomLeading) {
        VStack(spacing: 0){
            Text(currentPlot)
                .font(.caption.bold())
                .foregroundStyle(.gray)
                .padding(.bottom, 35)
                .position(x: barTranslation)
            Rectangle()
                .fill(.gray)
                .frame(width: 1, height: 300)
                .position(x: barTranslation)
            Spacer()
        }
        .opacity(showPlot ? 1 : 0)
    }

这里,覆盖视图(barTranslation)的位置直接在DragGesture().onChanged闭合内更新,而不在withAnimation中包裹它.这一更改确保了随着拖动手势的更改,覆盖的位置会立即更新,这应该会提供更流畅的体验.

.onChanged({ value in
    if abs(value.translation.width) > abs(value.translation.height) {
        showPlot = true
        barTranslation = value.location.x
    }
})

动画现在只应用于手势的.onEnded部分.这确保动画仅用于在拖动手势开始和结束时显示和隐藏覆盖,而不是try 在拖动过程中设置位置更改的动画.

.onEnded({ value in
    withAnimation { showPlot = false }
})

即使在删除With动画时,该错误仍会发生.而且,警告仍然打印了很多.

Make sure the gesture state is managed correctly. Sometimes, improper handling of gesture states can lead to unexpected behaviors. You might want to explicitly manage the state of the drag gesture.
The placement of animation modifiers can have unforeseen effects. Try removing any global or implicit animations applied to the parent view that might be affecting the overlay's behavior.
Redundant or conflicting updates to state variables like showPlot and barTranslation can cause issues.

该警告消息指示可动画值存在问题.调试这些值以查看它们是否以意外的方式更新.可能position(x: barTranslation)接收的值无效或不符合预期.

有时,与使用.position(x:)相比,使用.offset(x:)表示移动视图可以提供更流畅的动画和更新..offset修改器将视图从其原始位置移动,这样可以更有效.

您可以try 使用.offset修改.overlay部分:

.overlay(alignment: .bottomLeading) {
    VStack(spacing: 0){
        Text(currentPlot)
            .font(.caption.bold())
            .foregroundStyle(.gray)
            .padding(.bottom, 35)
            .offset(x: barTranslation - initialPosition)
        Rectangle()
            .fill(.gray)
            .frame(width: 1, height: 300)
            .offset(x: barTranslation - initialPosition)
        Spacer()
    }
    .opacity(showPlot ? 1 : 0)
}

initialPosition表示覆盖视图的初始位置.这一更改确保barTranslation相对于其原始位置更新视图的位置.

如果这些方法仍然无法解决问题,则可能需要查看整个视图层次 struct 以及手势如何与SwiftUI视图的其他部分交互.

Ios相关问答推荐

iphone 11 和 iphone 12 对于相同的布局显示不同的结果

ITMS-90432:将应用上传到应用store 时出现问题

如何在 SwiftUI 中创建显示当前电池电量的电池徽章

在 SwiftUI 中打开 PDF

滑动以返回特定的 SwiftUI 视图

-1103 错误域=NSURLErrorDomain 代码=-1103资源超过最大大小iOS 13

如何在通用应用程序中同时支持 iPad 和 iPhone 视网膜图形

Watchkit AppIcon - 名为AppIcon的应用图标集没有任何适用的内容

针对 Xcode 中的 iPad 选项

SwiftUI NavigationView navigationBarTitle LayoutConstraints 问题

将自定义对象保存到 NSUserDefaults

如何以编程方式切换 UISegmentedControl?

以编程方式 Select UITextField 中的所有文本

UITextView 从文本的底部或中间开始

iOS7 UITextView contentsize.height 替代

UISegmentedControl 以编程方式更改段数

使用 swift 3 在 UIView 上添加阴影

找不到开发者磁盘映像

zoom UIButton 动画 - Swift

这是一个什么样的讽刺错误?