DragGesture.onChange:for 循环使 CPU 过载并冻结应用程序

问题描述 投票:0回答:1

我有一个应用程序,您可以在其中使用点进行绘制,需要下面提供的代码,以便当一条线(已绘制)与当前线相交时,当前线被删除。

当我尝试在

DragGesture().onChanged
中执行此操作时,CPU 过载,并且线条绘制有延迟。

如何解决这个问题?

import SwiftUI
import CoreGraphics

struct Drawing1 {
    var points1: [CGPoint] = [CGPoint]()
}
struct Drawing2 {
    var points2: [CGPoint] = [CGPoint]()
}

struct ContentView: View {
    @State private var currentDrawing1: Drawing1 = Drawing1()
    @State private var drawings1: [Drawing1] = [Drawing1]()
    @State private var currentDrawing2: Drawing2 = Drawing2()
    @State private var drawings2: [Drawing2] = [Drawing2]()
    var body: some View {
        GeometryReader { geo in
            
            
                
                    Path { path1 in
                        for drawings1 in self.drawings1 {
                            self.add1(drawings1: drawings1, toPath1: &path1)
                        }
                        self.add1(drawings1: self.currentDrawing1, toPath1: &path1)
                        
                        
                    }
                    .stroke(Color.orange, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
                
               
                    Path { path2 in
                        for drawings2 in self.drawings2 {
                            self.add2(drawings2: drawings2, toPath2: &path2)
                        }
                        self.add2(drawings2: self.currentDrawing2, toPath2: &path2)
                        
                    }
                    .stroke(Color.black, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
                
            
            Image("example1")
                .resizable( resizingMode: .stretch)
                .scaledToFit()
                .clipped()
                .position(x:200, y:200)
                .frame(width: 100, height:100)
                .gesture(
                    DragGesture()
                        .onChanged({ value1 in
                            let currentPoint1 = value1.location
                            self.currentDrawing1.points1.append(currentPoint1)
                            
                            
                        }  )
                        .onEnded({ value1 in
                            
                            self.drawings1.append(self.currentDrawing1)
                            self.currentDrawing1 = Drawing1()
                        }
                                )
                    
                )
            
            Image("example2")
                .resizable( resizingMode: .stretch)
                .scaledToFit()
                .clipped()
                .position(x:100, y:100)
                .frame(width: 100, height:100)
            
                .gesture(
                    DragGesture()
                        .onChanged({ value2 in
                            let currentPoint2 = value2.location
                            self.currentDrawing2.points2.append(currentPoint2)
                            
                            
                            if drawings1.count > 0
                            {
                                let points2 = currentDrawing2.points2
                                let points1 = drawings1[0].points1
                                if points2.count > 0
                                {
                                    
                                    for i in 0..<points2.count-1 {
                                        let current2 = points2[i]
                                        let next2 = points2[i+1]
                                        for i in 0..<points1.count-1 {
                                            let current = points1[i]
                                            let next = points1[i+1]
                                            
                                            
                                            let delta1x = next.x - current.x
                                            let delta1y = next.y - current.y
                                            let delta2x = next2.x - current2.x
                                            let delta2y = next2.y - current2.y
                                            
                                            // create a 2D matrix from our vectors and calculate the determinant
                                            let determinant = delta1x * delta2y - delta2x * delta1y
                                            
                                            if abs(determinant) < 0.0001 {
                                                // if the determinant is effectively zero then the lines are parallel/colinear
                                                //return nil
                                            }
                                            
                                            // if the coefficients both lie between 0 and 1 then we have an intersection
                                            let ab = ((current.y - current2.y) * delta2x - (current.x - current2.x) * delta2y) / determinant
                                            
                                            if ab > 0 && ab < 1 {
                                                let cd = ((current.y - current2.y) * delta1x - (current.x - current2.x) * delta1y) / determinant
                                                
                                                if cd > 0 && cd < 1 {
                                                    // lines cross
                                                    
                                                    self.currentDrawing2.points2.removeAll()
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                            )
                        .onEnded( { value2 in
                            self.drawings2.append(self.currentDrawing2)
                            self.currentDrawing2 = Drawing2()
                        })
                )
            
            
            
            
            
            
            
        }
    }
    private func add1(drawings1: Drawing1, toPath1 path1: inout Path) {
        let points1 = drawings1.points1
        if points1.count > 1 {
            for i in 0..<points1.count-1 {
                let current = points1[i]
                let next = points1[i+1]
                path1.move(to: current)
                path1.addLine(to: next)
                
            }
        }
    }
    
    private func add2(drawings2: Drawing2, toPath2 path2: inout Path) {
        let points2 = drawings2.points2
        if points2.count > 1 {
            for i in 0..<points2.count-1 {
                let current = points2[i]
                let next = points2[i+1]
                path2.move(to: current)
                path2.addLine(to: next)
                
            }
            }
        }
}

我尝试在

.onEnded
中实现这一点。这里一切正常,但我需要它实时工作。

ios swift swiftui onchange draggesture
1个回答
0
投票

我们可以使用

lineIntersection(_:eoFill:)

检测相交路径

例如,让我们创建两个路径:

var pth1 = Path()
var pth2 = Path()
        
pth1.move(to: .init(x: 200.0, y: 300.0))
pth1.addLine(to: .init(x: 150.0, y: 350.0))
pth1.addLine(to: .init(x: 120.0, y: 320.0))
pth1.addLine(to: .init(x:  80.0, y: 250.0))
        
pth2.move(to: .init(x: 180.0, y: 200.0))
pth2.addLine(to: .init(x: 180.0, y: 240.0))
pth2.addLine(to: .init(x: 140.0, y: 260.0))
pth2.addLine(to: .init(x: 160.0, y: 300.0))
        

如果我们画它们,它看起来像这样:

Non-Intersecting

所以,让我们检查一下:

let iPath = pth1.lineIntersection(pth2)
        
// we need to check the .cgPath
if iPath.cgPath.isEmpty {
    print("Paths do NOT Intersect")
} else {
    print("Paths Intersect!")
}
        

调试控制台输出将是:

Paths do NOT Intersect

如果我们向

pth2
添加另一条线段:

pth2.addLine(to: .init(x: 220.0, y: 340.0))

现在看起来像这样:

Intersecting

输出将是:

Paths Intersect!

因此,我们不要在拖动时跟踪所有点,而是更新一个

Path
:

struct Drawing {
    var thePath: Path = Path()
}

并且在

.onChanged
中,我们可以使用
.move(to:)
.addLine(to:)
,这样我们就不会在每个周期都重新构建路径。

我们还定义另外两个属性:

struct Drawing {
    var thePath: Path = Path()
    var theColor: Color = Color.black
    var canDraw: Bool = true
}

目前尚不清楚您要如何处理“绘图”数组,因此我们将稍微简化一下:

@State private var currentDrawing1: Drawing = Drawing(theColor: Color.red)
@State private var currentDrawing2: Drawing = Drawing(theColor: Color.blue)

这是您可以检查和尝试的完整视图结构(应该有足够的注释):

struct DrawIntersectView: View {
    @State private var currentDrawing1: Drawing = Drawing(theColor: Color.red)
    @State private var currentDrawing2: Drawing = Drawing(theColor: Color.blue)
    
    var body: some View {
        GeometryReader { geo in
            
            currentDrawing1.thePath
                .stroke(currentDrawing1.theColor, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
            
            currentDrawing2.thePath
                .stroke(currentDrawing2.theColor, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
            
            
            Image("example1")
                .resizable( resizingMode: .stretch)
                .scaledToFit()
                .clipped()
                .position(x:200, y:200)
                .frame(width: 100, height:100)
                .opacity(0.5)   // so we can see the line start
                .gesture(
                    DragGesture()
                        .onChanged({ value in
                            let currentPoint = value.location
                            // if the path has no points yet, move to
                            // else, addLine to
                            if self.currentDrawing1.thePath.isEmpty {
                                self.currentDrawing1.thePath.move(to: currentPoint)
                            } else {
                                self.currentDrawing1.thePath.addLine(to: currentPoint)
                            }
                            
                        })
                        .onEnded({ value in
                        })
                    
                )
            
            Image("example2")
                .resizable( resizingMode: .stretch)
                .scaledToFit()
                .clipped()
                .position(x:100, y:100)
                .frame(width: 100, height:100)
                .opacity(0.5)   // so we can see the line start
                .gesture(
                    DragGesture()
                        .onChanged({ value in
                            // if we haven't yet drawn from currentDrawing2
                            //  just return
                            if self.currentDrawing1.thePath.isEmpty {
                                return
                            }
                            
                            // if we've crossed the first line, we don't want to
                            //  draw until the user has lifted the touch (ended the current drag)
                            if !self.currentDrawing2.canDraw {
                                return
                            }
                                
                            let currentPoint = value.location
                            // if the path has no points yet, move to
                            // else, addLine to
                            if self.currentDrawing2.thePath.isEmpty {
                                self.currentDrawing2.thePath.move(to: currentPoint)
                            } else {
                                self.currentDrawing2.thePath.addLine(to: currentPoint)
                            }

                            // get lineIntersection of the paths from drawing 1 and drawing 2
                            let t = self.currentDrawing1.thePath.lineIntersection(self.currentDrawing2.thePath)
                            if !t.cgPath.isEmpty {
                                print("intersects")
                                self.currentDrawing2.canDraw = false
                                self.currentDrawing2.thePath = Path()
                            }
                            
                        })
                        .onEnded({ value in
                            // allow drawing again
                            self.currentDrawing2.canDraw = true
                        })
                )
        }
        
    }
    
}

值得注意:因为您使用的路径线宽为 10...如果我们像这样绘制线条/路径:

LineWidth

它们确实相交!那是因为实际的线路路径是:

ActualLines

© www.soinside.com 2019 - 2024. All rights reserved.