在 SwiftUI 中,如何在执行手势时持续更新视图?

In SwiftUI, how do I continuously update a view while a gesture is being preformed?

这是自学如何在 iOS 中创建基本绘画应用程序(如 MSPaint)的持续尝试的一部分。我正在使用 SwiftUI 和 CoreImage 来执行此操作。

虽然我全神贯注于 CoreImage 中的像素操作(我一直在研究 this),但我不确定如何向 SwiftUI 添加拖动手势以便我可以“绘画” ".

使用拖动手势,我想这样做:

onBegin 和 onChanged:

换句话说,随着我手指的移动不断更新图像。

更新:我查看了下面 Asperi 的响应,并在 .onAppear 下面添加了 .gesture。但是,这会导致警告“在视图更新期间修改状态,这将导致未定义的行为。”

struct ContentView: View {
    @State private var image: Image?
    @GestureState var location = CGPoint(x: 0, y: 0)

    var body: some View {
        VStack {
            image?
                .resizable()
                .scaledToFit()
        }
        .onAppear(perform: newPainting)
        .gesture(
            DragGesture()
                .updating($location) { (value, gestureState, transaction) in
                    gestureState = value.location
                    paint(location: location)
                }
        )
    }

    func newPainting() {
        guard let newPainting = createBlankCanvas(size: CGSize(width: 128, height: 128)) else {
            print("failed to create a blank canvas")
            return
        }
        
        image = Image(uiImage: newPainting)
    }

    func createBlankCanvas(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? {

        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
        color.set()
        UIRectFill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return image
    }

    func paint(location: CGPoint) {

    // do the CoreImage manipulation here

    // now, take the output of the CI manipulation and
    // attempt to get a CGImage from our CIImage

            if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
                // convert that to a UIImage
                let uiImage = UIImage(cgImage: cgimg)

                // and convert that to a SwiftUI image
                image = Image(uiImage: uiImage) // <- Modifying state during view update, this will cause undefined behavior.
            }
    } 

}

在哪里添加手势并让它重复调用 paint() 函数? 只要手势继续,如何让视图不断更新?

谢谢!

您不应将 SwiftUI 视图(如 Image)存储在 @State 变量中。相反,您应该存储 UIImage:

struct ContentView: View {
    @State private var uiImage: UIImage?
    @GestureState var location = CGPoint(x: 0, y: 0)

    var body: some View {
        VStack {
            uiImage.map { uiImage in
                Image(uiImage: uiImage)
                    .resizable()
                    .scaledToFit()
            }
        }
        .onAppear(perform: newPainting)
        .gesture(
            DragGesture()
                .updating($location) { (value, gestureState, transaction) in
                    gestureState = value.location
                    paint(location: location)
                }
        )
    }

    func newPainting() {
        guard let newPainting = createBlankCanvas(size: CGSize(width: 128, height: 128)) else {
            print("failed to create a blank canvas")
            return
        }
        
        uiImage = newPainting
    }

    func createBlankCanvas(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? {

        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
        color.set()
        UIRectFill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return image
    }

    func paint(location: CGPoint) {

    // do the CoreImage manipulation here

    // now, take the output of the CI manipulation and
    // attempt to get a CGImage from our CIImage

            if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
                // convert that to a UIImage
                uiImage = UIImage(cgImage: cgimg)
            }
    }

}