Kai
Kai

Reputation: 13

iOS CGContext Poor Performance

I've written a render function that is called every 1/60 of a second. It's shown below. Essentially, it renders 40 lines in radial pattern that uses sine. It looks like this https://i.sstatic.net/HPwOd.jpg. When I run it on my iPhone 6s with the debugger on, it shows that it is using around 60% of the CPU and it just makes the "high" battery usage level. This doesn't seem right to me, it's only 40 simple lines right?

Could I be doing this more efficiently? Should I try to use something else like CALayer or Metal for better performance? I'm a noob at this by the way.

func render() { tick += 0.01
    let renderer = UIGraphicsImageRenderer(size: CGSize(width: 375, height: 667))
    let img = renderer.image { ctx in
        let c = ctx.cgContext

        // Set background
        let background = CGRect(x: 0, y: 0, width: 375, height: 667)
        c.setFillColor(UIColor.black.cgColor)
        c.addRect(background)
        c.drawPath(using: .fillStroke)

        // Function to draw a line
        func line(p1:CGPoint, p2:CGPoint) {
            let line = CGRect(x: 0, y: 0, width: 200, height: 200)
            c.addLines(between: [p1,p2])
            c.drawPath(using: .fillStroke)
        }

        // Draw lines
        c.setStrokeColor(UIColor.white.cgColor)
        c.setLineWidth(1)
        c.setLineCap(CGLineCap.round)
        for i in 0...39 {
            let n:CGFloat = CGFloat(i) * CGFloat.pi/40
            let d:CGFloat = sin(tick + CGFloat(i)*CGFloat.pi/5)*20 + 100
            line(p1: CGPoint(x: d*cos(tick+n)+187.5,            y: d*sin(tick+n)+333.5),
                 p2: CGPoint(x: d*cos(tick+n+CGFloat.pi)+187.5, y: d*sin(tick+n+CGFloat.pi)+333.5))
        }   
    }
    imageView.image = img
}

Upvotes: 1

Views: 631

Answers (1)

clemens
clemens

Reputation: 17722

I think apptoach isn't good. When you want to display animated content you shouldn't redraw views. You should use CoreAnimation. The good news is its easy to implement. The bad news is it needs a little bit to learn. ;)

Some suggestions to your code:

  1. Some parts of your drawing seems to be constant. Thus souldn't redraw this again and again. Instead you can put it in a separate layer.
  2. As @rmaddy suggests you need many sinand cos computations. But these values should always be the same. So you can compute them once and cache them.
  3. You can implement the drawing inside in draw(in:), and use dynamic properties for all variable parts the code. You can use animations from CoreAnimation then to animate your drawing. Creating dynamic CA-properties in Swift is a litte bit tricky. (As far as I know you must annotate them with @NSManaged(!), and you must overwrite needsDisplay(forKey:) and return truefor them.).

Upvotes: 1

Related Questions