Search code examples
iosuiimageuibezierpathswift4.2

UIBezierpaths Are Not Drawing Where They Were Drawn


Looking for help on this "weird" problem.

I am using a PanGesture to allow the user to draw a line on CAShapeLayer.

I keep track of the path until they end and then store the path in an array of paths, clearing their original path from the CAShapeLayer.path

When I redraw those paths onto a UIImage, the paths are shifted in the Y-axis towards the top of the screen.

I am attaching two images:

1) The drawing of the path. Drawing The Path

2) The UIImage that is put out when I redraw the path. As you can see, I draw along a grid line, however, when the UIImage is created, the line is above the grid line. The Path Redrawn On UIImage

Any suggestions appreciated.

The code for both the drawing and then the rendering are below.

    @objc private func drawLine(_ gestureRecognizer: UIPanGestureRecognizer) {

    let point = gestureRecognizer.location(in: self)
    if point.x < 0 || point.x > self.bounds.width || point.y < 0 || point.y > self.bounds.height {
      return
    }

    switch gestureRecognizer.state {
    case .began:
      currentLine = UIBezierPath()
      currentLine.lineWidth = settings.defaultSpotRadius
      currentLine.lineCapStyle = .round
      currentLine.lineJoinStyle = .round
      currentLine.move(to: point)
      break

    case .changed:
      currentLine.addLine(to: point)
      currentLineDrawLayer.path = currentLine.cgPath
      break

    case .ended:
      self.storage.addLine(clear: fowClearMode, path: currentLine, ofWidth: currentLine.lineWidth)
      currentLineDrawLayer.path = nil
      storedImage = drawPaths()
      break

    default:
      break
    }

  }

  public func drawPaths() -> UIImage? {
    if pathsHidden { return nil }

    let rect = CGRect(origin: .zero, size: self.imageSize)
    UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)

    storedImage.draw(in: rect)

    for (isClear, line, width) in thePaths {
      let blendMode = isClear ? settings.colorClear : settings.colorDark
      line.lineWidth = width
      line.stroke(with: blendMode, alpha: 1.0)
    }

    let image = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return image
  }

Tue 25 Sep, 2018

I have created a mini-project that contains only the import bits and pieces. It is available here ... Mini-Project


Solution

  • I think you have a ways to go to get everything working as desired, but, to solve your initial "offset" issue...

    In ViewController.swift you are setting the frame of imgDisplay: ImageDisplay to self.view.bounds --- which, in this case, will be 1024 x 768.

    Then in ImageDisplay.swift you create aPathImage at a size of 1024 x 791 and setting that image as a sublayer.

    Then...

    You are tracking pan-gestures based on a view with bounds of 1024 x 768, then re-drawing those lines on an image that is 1024 x 791, resulting in a "shift".

    Note that, if you change your image size to 800 x 600 the shift will be very obvious, and it will be a little easier to see how it is related to the size difference.

    What you probably want to do is set the size of the frame of imgDisplay to the size of the image.

    Try this to see what happens - in ImageDisplay.swift:

      override init(frame: CGRect) {
        super.init(frame: frame)
    
        // add this line        
        self.frame = CGRect(origin: .zero, size: aPathImage.sizeCurrent)
    
        self.contentMode = .center
        ...
    

    Now, your pan coordinates will match the coordinates of the contained image.

    As I said, you've still got a ways to go, but hopefully this helps you along the way.