【问题标题】:View is re-initialized after DragGesture onChanged在 DragGesture onChanged 后重新初始化视图
【发布时间】:2020-05-31 17:54:56
【问题描述】:

以下代码显示了一个橙色屏幕,右下角有一个绿色圆圈。可以拖动圆圈。

import SwiftUI

struct DraggableCircle: View {
    @Binding var offset: CGSize
    @State private var previousOffset: CGSize

    var body: some View {
        Circle().fill(Color.green)
            .frame(width: 100)
            .offset(self.offset)
            .gesture(
                DragGesture(minimumDistance: 0, coordinateSpace: .local)
                    .onChanged { event in
                        print("\nDragGesture onChanged")
                        self.offset = CGSize(width: event.location.x - (event.startLocation.x - self.previousOffset.width),
                                             height: event.location.y - (event.startLocation.y - self.previousOffset.height))
                    }
            )
    }

    init(offset: Binding<CGSize>) {
        print("Init with offset \(offset.wrappedValue)")
        self._offset = offset
        self._previousOffset = State(initialValue: offset.wrappedValue)
        print("offset = \(self.offset), previousOffset=\(self.previousOffset)")
    }
}

struct ContentView: View {
    @State var circleOffset = CGSize()

    var body: some View {
        GeometryReader { reader in
            Rectangle().fill(Color.orange)
                .overlay(
                    DraggableCircle(offset: self.$circleOffset)
                )
                .onAppear {
                    self.circleOffset = CGSize(width: reader.size.width / 2,
                                               height: reader.size.height / 2)
                    print("size: \(reader)\n")
                }
        }
    }
}

如果您运行并点击绿色圆圈(开始拖动手势),控制台中会显示以下内容:

Init with offset (0.0, 0.0)
offset = (0.0, 0.0), previousOffset=(0.0, 0.0)
size: GeometryProxy(base: SwiftUI._PositionAwareLayoutContext(base: SwiftUI.LayoutTraitsContext(context: AttributeGraph.AttributeContext<SwiftUI.VoidAttribute>(graph: __C.AGGraphRef(p: 0x00007f84b6a05ff0), attribute: AttributeGraph.Attribute<()>(identifier: __C.AGAttribute(id: 42))), environmentIndex: 4), dimensionsIndex: 1, transformIndex: 3, positionIndex: 2), seed: 1, viewGraph: Optional(SwiftUI.ViewGraph))

Init with offset (187.5, 323.5)
offset = (187.5, 323.5), previousOffset=(187.5, 323.5)

DragGesture onChanged
Init with offset (0.0, 0.0)
offset = (0.0, 0.0), previousOffset=(0.0, 0.0)

我预期会发生的是,当你拖动圆圈时,它可以平滑地拖动到屏幕上的其他位置。

实际发生的情况是,当拖动开始时,再次调用DraggableCircle.init,这会重置偏移量并将圆放在中间。这是为什么呢?

注意:当您将@State previousOffset 移动到ContentView 时,问题就会消失。但我不明白为什么。

【问题讨论】:

    标签: swiftui draggesture


    【解决方案1】:

    我根据您的评论更改了代码。在我的代码中,这个拖动手势可以正常工作。我现在看到的唯一错误是第一次激活拖动手势。也许这会引导您找到所需的解决方案。

            struct DraggableCircle: View {
        @Binding var circleOffset: CGSize
        @State private var previousOffset: CGSize
    
        var body: some View {
            Circle().fill(Color.green)
                .frame(width: 100)
                .offset(self.circleOffset)
                .gesture(
                    DragGesture()
                        .onChanged { event in
                            print("\nDragGesture onChanged")
                                self.circleOffset = CGSize(width: event.location.x - (event.startLocation.x - self.previousOffset.width),
                                                           height: event.location.y - (event.startLocation.y - self.previousOffset.height))
                    }
                    .onEnded { event in
                        self.circleOffset = CGSize(width: event.location.x - (event.startLocation.x - self.previousOffset.width),
                                                   height: event.location.y - (event.startLocation.y - self.previousOffset.height))
    
                        self.previousOffset = self.circleOffset
                    }
            )
        }
    
        init(offset: Binding<CGSize>) {
            print("Init with offset \(offset.wrappedValue)")
            self._circleOffset = offset
            self._previousOffset = State(initialValue: offset.wrappedValue)
            print("offset = \(self.offset), previousOffset=\(self.previousOffset)")
        }
    }
    
    struct ContentView: View {
        @State var circleOffset = CGSize()
    
        var body: some View {
            GeometryReader { reader in
                Rectangle().fill(Color.orange)
                    .overlay(
                        DraggableCircle(offset: self.$circleOffset)
                    )
                    .onAppear {
                        self.circleOffset = CGSize(width: reader.size.width / 2,
                                                   height: reader.size.height / 2)
                        print("size: \(reader)\n")
                    }
            }
        }
    }
    

    【讨论】:

    • 很遗憾,无法从 ContentView 中删除 State 变量。原因是,在我的原始代码(不是我发布的压缩示例)中,其他视图依赖于它。
    • @BartvanKuik 我已经更改了代码,但目前还不是完美的解决方案。
    • 我看不出您当前的代码和我的代码有什么区别?我认为复制/粘贴出了点问题:)
    • @BartvanKuik Ahh 没有复制粘贴完全没问题。我在拖动手势中添加了onEnded 修饰符。它解决了将圆圈放在中间的拖动问题。这只是第一次发生。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 2013-08-13
    • 1970-01-01
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    相关资源
    最近更新 更多