swiftuiviewuicontainerviewsetneedsdisplay

SetNeedsDisplay having no effect


I am creating a subview in a UIContainerView that is of the class described below. These values are printing correctly to the terminal window but are not actual being shown in the the subview that I am defining. I thought that setNeedsDisplay() should take care of this but nothing ever shows up.

I am creating this subview as let canvas = Canvas() and also tried

canvas.setNeedsDisplay() which has no effect.

class Canvas: UIView {

  override func draw(_ rect: CGRect) {

      super.draw(rect)
      guard let context = UIGraphicsGetCurrentContext() else { return }

      context.setStrokeColor(login_theme_color.cgColor)
      context.setLineWidth(10.0)
      context.setLineCap(.butt)

      lines.forEach { (line) in
      for(i, p) in line.enumerated(){
          //context.move(to: p)
          //context.addLine(to: p)
          if i == 0 {
              context.move(to: p)
              print("First point of new line is \(p)")
          }else{
              context.addLine(to: p)
              print("point is \(p)")
          }
      }
  }
       context.strokePath()
}

  var lines = [[CGPoint]]()

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
      lines.append([CGPoint]())
      //setNeedsDisplay()
  }
  override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
      guard let point = touches.first?.location(in: nil) else { return }
      guard var lastLine = lines.popLast() else { return }
      lastLine.append(point)
      lines.append(lastLine)
      setNeedsDisplay()
  }

}

Why would the values be printing to the terminal correctly, but the lines array not actually showing the values in the view? Thanks so much in advance for your help.

I define my canvas with the following constraints :

    let canvas = Canvas()

    @objc fileprivate func setupCanvas(){
        //canvas.setNeedsDisplay()
        containerView.addSubview(canvas)
        canvas.translatesAutoresizingMaskIntoConstraints = false
        canvas.topAnchor.constraint(equalTo: rectangle2.topAnchor, constant: 74).isActive = true
        canvas.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: (view.frame.width/2) + 8).isActive = true
        canvas.rightAnchor.constraint(equalTo: rectangle2.rightAnchor, constant: -4).isActive = true
        canvas.bottomAnchor.constraint(equalTo: howitWorksText.bottomAnchor, constant: 0).isActive = true
        canvas.layer.cornerRadius = 30
        canvas.layer.shadowRadius = 0.5
        canvas.layer.shadowColor = UIColor.white.cgColor
        //canvas.backgroundColor = UIColor.clear
        //canvas.layer.borderWidth = 2.0
        //canvas.layer.borderColor = UIColor.black.cgColor
        canvas.layer.masksToBounds = true
        canvas.backgroundColor = .white
        //canvas.setNeedsDisplay()
    }

and call setupCanvas() in my ViewDidLoad()


Solution

  • Your coordinates are off because you are using the wrong frame of reference. Specifying nil for the view in location(in view:) selects the coordinate space of the window. So even though your touches are inside the canvas, your drawings are not. You want to get the coordinates of the touch inside of the Canvas view by passing self instead of nil to location(in:).

    In touchesMoved() change:

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

    to:

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

    For more information, check out the documentation for location(in:).