0

I have a Canvas view as below

class Canvas: UIView {
    
    override func draw(_ rect: CGRect){
        super.draw(rect)
        
        guard let context = UIGraphicsGetCurrentContext() else { return }
        
        paths.forEach { path in
            switch(path.type) {
            case .move:
                context.move(to: path.point)
                break
            case .line:
                context.addLine(to: path.point)
                break
            }
        }
        
        context.setLineWidth(10)
        context.setLineCap(.round)
        context.strokePath()
    }
    
    var paths = [Path]()
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let point = touches.first?.location(in: nil) else { return }
        paths.append(Path(type: .move, point: point))
        setNeedsDisplay()
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let point = touches.first?.location(in: nil) else { return }
        paths.append(Path(type: .line, point: point))
        setNeedsDisplay()
    }
}

class Path {
    let type: PathType
    let point: CGPoint
    
    init(type: PathType, point: CGPoint) {
        self.type = type
        self.point = point
    }
    
    enum PathType {
        case move
        case line
    }
}

When I load it using a normal ViewController,

class ViewController: UIViewController {
    
    let canvas = Canvas()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(canvas)
        canvas.backgroundColor = .white
        canvas.frame = view.frame
    }
}

All works fine. I can draw as per where the touch happens, as shown below.

enter image description here

However, if I wrap it around SwiftUI framework, as below

struct ContentView: View {
    var body: some View {
        CanvasSwiftUI()
    }
}

struct CanvasSwiftUI : UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        Canvas()
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        uiView.backgroundColor = .white
    }
}

My touch and drawing doesn't match up. The drawing coordinate is lower than where the touch happens, as shown below

enter image description here

Did I miss out anything when wrapping it around the SwiftUI, that the touch and drawn coordinate is now mis-aligned?

Elye
  • 53,639
  • 54
  • 212
  • 474

1 Answers1

3

The problem you have is not related to SwiftUI, it's in your code

guard let point = touches.first?.location(in: nil) else { return }

you should use

guard let point = touches.first?.location(in: self) else { return }
JIE WANG
  • 1,875
  • 1
  • 17
  • 27
  • Thanks! Work like a charm. I was following the tutorial and got to that code. https://youtu.be/E2NTCmEsdSE?t=8m18s . Mind explaining why? – Elye Oct 31 '20 at 11:27
  • 1
    I guess, `nil` refers to the root view, and `self` refers to the `canvas` itself, right? – Elye Oct 31 '20 at 11:30
  • @Elye If you read this doc https://developer.apple.com/documentation/uikit/uitouch/1618116-location, it says Pass nil to get the touch location in the window’s coordinates. – JIE WANG Oct 31 '20 at 15:34