Display a line graph on Apple Watch using watchOS 2

I am trying to display a graph / step on an Apple Watch using watchOS 2. Unlike iOS 9, watchOS 2 does not support Quartz. It only supports Core Graphics. I tried to write some code to draw a line graph, but I get the error "CGContextRestoreGState: invalid context 0x0. This is a serious error. This application or the library it uses uses an invalid context and thereby contributes to a general deterioration in system stability and reliability. This notification is a courtesy: fix this problem. It will be a fatal mistake in the upcoming update. "

Below is the code snippet that I used:

import WatchKit import Foundation import UIKit class InterfaceController: WKInterfaceController{ override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) let path = UIBezierPath() let startPoint = CGPointMake(0.0, 0.0) path.moveToPoint(startPoint) let nextPoint = CGPointMake(20.0, 20.0) path.addLineToPoint(nextPoint) path.lineWidth = 1.0 UIColor.whiteColor().setStroke() path.stroke() } override func willActivate() { super.willActivate() } override func didDeactivate() { super.didDeactivate() } } 

My end result should be something like the Stocks app present on the Apple Watch. When the user clicks on a specific stock, he will be able to view / visualize statistics of this stock. Can anyone help me on this.

+7
ios swift watchkit watch-os-2
source share
5 answers

I managed to display the lines with the following steps:

  • Create a graphics context based on a bitmap and make it the current context using UIGraphicsBeginImageContext .
  • Draw a context.
  • Extract the CGImageRef from the context and convert it to a UIImage object.
  • Show image on WKInterfaceGroup or WKInterfaceImage.

the code:

 // Create a graphics context let size = CGSizeMake(100, 100) UIGraphicsBeginImageContext(size) let context = UIGraphicsGetCurrentContext() // Setup for the path appearance CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor) CGContextSetLineWidth(context, 4.0) // Draw lines CGContextBeginPath (context); CGContextMoveToPoint(context, 0, 0); CGContextAddLineToPoint(context, 100, 100); CGContextMoveToPoint(context, 0, 100); CGContextAddLineToPoint(context, 100, 0); CGContextStrokePath(context); // Convert to UIImage let cgimage = CGBitmapContextCreateImage(context); let uiimage = UIImage(CGImage: cgimage!) // End the graphics context UIGraphicsEndImageContext() // Show on WKInterfaceImage image.setImage(uiimage) 

image - property WKInterfaceImage. This works for me.

I can also draw using UIBezierPath on watchOS as follows:

 // Create a graphics context let size = CGSizeMake(100, 100) UIGraphicsBeginImageContext(size) let context = UIGraphicsGetCurrentContext() UIGraphicsPushContext(context!) // Setup for the path appearance UIColor.greenColor().setStroke() UIColor.whiteColor().setFill() // Draw an oval let rect = CGRectMake(2, 2, 96, 96) let path = UIBezierPath(ovalInRect: rect) path.lineWidth = 4.0 path.fill() path.stroke() // Convert to UIImage let cgimage = CGBitmapContextCreateImage(context); let uiimage = UIImage(CGImage: cgimage!) // End the graphics context UIGraphicsPopContext() UIGraphicsEndImageContext() image.setImage(uiimage) 

You can check the code samples here .

+11
source share

Here is the code in watchOS 3

  // Create a graphics context let size = CGSize(width:self.contentFrame.size.width, height:100) UIGraphicsBeginImageContext(size) let context = UIGraphicsGetCurrentContext() // Setup for the path appearance context!.setStrokeColor(UIColor.white.cgColor) context!.setLineWidth(4.0) // Draw lines context!.beginPath (); context?.move(to: CGPoint()) context?.addLine(to: CGPoint(x: 100, y: 100)) context?.addLine(to: CGPoint(x: 0, y: 100)) context?.addLine(to: CGPoint(x: 100, y: 0)) context!.strokePath(); // Convert to UIImage let cgimage = context!.makeImage(); let uiimage = UIImage(cgImage: cgimage!) // End the graphics context UIGraphicsEndImageContext() self.graphGroup.setBackgroundImage(uiimage) 
+2
source share

The context here is not a CGContext, so you have a problem. I don’t think you can use Core Graphics directly on the Apple Watch.

0
source share

Here is an example project that shows how to make dynamic graphs on WatchKit 2.0

https://github.com/vlm/ExampleWatchGraph

Its essence is in the GraphPainter.swift file, where CoreGraphics is used to draw off-screen buffers (like @ shu223), then display this buffer in WKInterfaceImage.

Animated preview of the GIF interface .

0
source share

Graphic rendering is done using bezierPath and which is converted to an image attached to the Apple Watch

// Swift-3 Xcode-8.1

import UIKit class InterfaceController: WKInterfaceController {

 @IBOutlet var graphImage: WKInterfaceImage! override func awake(withContext context: Any?) { super.awake(withContext: context) // Configure interface objects here. } override func willActivate() { super.willActivate() let path = createBeizePath() //Change graph to image let image:UIImage = UIImage.shapeImageWithBezierPath(bezierPath: path, fillColor: .red, strokeColor: .black) graphImage.setImage(uiimage) } //Draw graph here func createBeizePath() -> UIBezierPath { let path = UIBezierPath() //Rectangle path Trace path.move(to: CGPoint(x: 20, y: 100) ) path.addLine(to: CGPoint(x: 50 , y: 100)) path.addLine(to: CGPoint(x: 50, y: 150)) path.addLine(to: CGPoint(x: 20, y: 150)) return path } } extension UIImage { class func shapeImageWithBezierPath(bezierPath: UIBezierPath, fillColor: UIColor?, strokeColor: UIColor?, strokeWidth: CGFloat = 0.0) -> UIImage! { bezierPath.apply(CGAffineTransform(translationX: -bezierPath.bounds.origin.x, y: -bezierPath.bounds.origin.y ) ) let size = CGSize(width: 100 , height: 100) UIGraphicsBeginImageContext(size) let context = UIGraphicsGetCurrentContext() var image = UIImage() if let context = context { context.saveGState() context.addPath(bezierPath.cgPath) if strokeColor != nil { strokeColor!.setStroke() context.setLineWidth(strokeWidth) } else { UIColor.clear.setStroke() } fillColor?.setFill() context.drawPath(using: .fillStroke) image = UIGraphicsGetImageFromCurrentImageContext()! context.restoreGState() UIGraphicsEndImageContext() } return image } } 
0
source share

All Articles