Search code examples
iosswiftuibezierpath

Can't get Swift UIBezierPath plot to remove and then update


In my app, viewdidLoad() uses UIBezierPath to display green UIBezierPath plotted line.
But later in the program, an event callback uses same UIBezierPath to removeAllPoints and display red line next to green one.
But, all it does is turn the green line to red, at same position.

Ultimately, I want to use UIBezierPath to each second show an ever-changing, multi-line plot -- that is: remove previous multi-line plot and show updated multi-line plot, every second.

CODE THAT SHOWS GREEN LINE...................

    var sensor_plot_UIBezierPath = UIBezierPath()
var sensor_plot_CAShapeLayer = CAShapeLayer()

override func viewDidLoad() 
{
                sensor_plot_UIBezierPath.move(to: CGPoint(  x: 100, 
                                                            y: 100))
                sensor_plot_UIBezierPath.addLine( to: CGPoint(  x:  200,
                                                                y:  200 ) )
        init_app_display()

        ...start background events...
}

func init_app_display()
{
    sensor_plot_CAShapeLayer.path = sensor_plot_UIBezierPath.cgPath
    sensor_plot_CAShapeLayer.fillColor = UIColor.clear.cgColor       // doesn't matter
    sensor_plot_CAShapeLayer.strokeColor = UIColor.green.cgColor
    sensor_plot_CAShapeLayer.lineWidth = 3.0

    view.layer.addSublayer( sensor_plot_CAShapeLayer )
}

CODE THAT IS SUPPOSED TO SHOW RED LINE NEXT TO IT.................
(but actually turns initial green line to red, at same position -- no 2nd line next to first)

...display_plot_update() is called by event from background thread (specifically, BLE event didUpdateValueFor)

    func display_plot_update()
    {
        DispatchQueue.main.async
        {
            self.sensor_plot_UIBezierPath.removeAllPoints()
            self.sensor_plot_UIBezierPath.move(to: CGPoint(     x: 110, 
                                                                y: 100))
            self.sensor_plot_UIBezierPath.addLine( to: CGPoint(  x:  210,
                                                                 y:  200 ) )
            self.sensor_plot_CAShapeLayer.strokeColor = UIColor.red.cgColor
        }
    }

Solution

  • You don't show the code that installs your path in your shape layer, or the code that installs your shape layer as a sublayer of your view controller's view. You should show that code so that we can see what you're doing. You should also show the code for your init_app_display() function.

    I assume that somewhere you have code that says:

    sensor_plot_CAShapeLayer.path = self.sensor_plot_UIBezierPath.cgPath
    

    and code that does something like

    view.layer.addSublayer(sensor_plot_CAShapeLayer)
    

    You seem to be under the impression that setting a CAShapeLayer's path to a specific bezier path links the shape layer to that path, and updating the path updates the shape layer. It doesn't work that way. After changing your path, you need to install the new path into your shape layer.

    You could change your display_plot_update() function like this:

    func display_plot_update()
    {
        DispatchQueue.main.async
        {
            // Create a new bezier path.
            let newPath = UIBezierPath()
            newPath.move(to: CGPoint(     x: 110, 
                                          y: 100))
            newPath.addLine( to: CGPoint( x: 210,
                                          y: 200 ) )
            self.sensor_plot_CAShapeLayer.strokeColor = UIColor.red.cgColor
            self.sensor_plot_CAShapeLayer.path = newPath.cgPath // The important part
        }
    }
    

    That would move the line to your new coordinates and make it red.

    If you want to add a second line next to the first, then write your `display_plot_update()~ function like this:

    func display_plot_update()
    {
        DispatchQueue.main.async
        {
            // Add the new line to the existing path (without clearing it)
            self.sensor_plot_UIBezierPath.move(to: CGPoint(     x: 110, 
                                          y: 100))
            self.sensor_plot_UIBezierPath.addLine( to: CGPoint( x: 210,
                                          y: 200 ) )
            self.sensor_plot_CAShapeLayer.strokeColor = UIColor.red.cgColor
            sensor_plot_CAShapeLayer.path = self.sensor_plot_UIBezierPath.cgPath // The important part
        }
    }
    

    Note that you can't have lines of different colors in a single shape layer. A shape layer can only contain a single path, and it draws the entire path using a single stroke color and a single fill color. (The stroke color and the fill color can be different, but the entire path will use those same colors.)