Suppose you’re writing a simple game for Apple Watch – for example, you might have a treasure map image and you want to render a cross on it in a random position to locate the treasure.
This is tricky, because WatchKit severely limits your options for laying out UI primitives on the screen. For example, if you put a label and an image onto a StoryBoard, it will tile them (rather than letting you put one on top of the other).
The approach I’ve adopted is:
- Create a Group in the story board and set its background image
- Add an image view within the Group
- Create a context and use CoreGraphics to write into it
- Apply the context to the image view
Set up a new iOS WatchKit App, then drag a Group and Image from the Object Library onto the storyboard:
In the WatchKit App assets, create a new image set and drag your background image onto the x2 outline:
Set the background image on the group:
Then create an outlet in the InterfaceController for the image – one way is to control-drag from the outline view of the storyboard into the interface controller’s swift file. I called mine OverlayImage to convey the purpose.
Finally, add the code that will leverage the CoreGraphics library to draw into the overlay – the work is done in drawCross() which is called from awakeWithContext(). I’ve split out line and circle drawing methods for clarity.
class InterfaceController: WKInterfaceController { // Create by control-dragging to the StoryBoard @IBOutlet var OverlayImage: WKInterfaceImage! let imageWidth : CGFloat = 312.0 let imageHeight : CGFloat = 348.0 // 390 - 42 for status bar on 42mm watch override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) drawCross() } override func willActivate() { ... } // standard override func didDeactivate() { ... } // standard func drawCross() { // Begin image context and grab context let context = createContext() // Draw our primitives drawLine( context, startX: 75, startY: 150, endX: 125, endY: 200 ) drawLine( context, startX: 75, startY: 200, endX: 125, endY: 150 ) drawCircle( context, radius : 10, centreX : 100, centreY : 175 ) // End by applying our graphics to the Overlay image applyContextToImage( context ) } func createContext() -> CGContext? { // The 'opaque' parameter is false, so that we overlay // rather than the static image underneath UIGraphicsBeginImageContextWithOptions( CGSizeMake( imageWidth, imageHeight ), false, 0 ) let context = UIGraphicsGetCurrentContext() CGContextBeginPath( context ) return context } func drawLine( context : CGContext?, startX : CGFloat, startY : CGFloat, endX : CGFloat, endY : CGFloat ) { CGContextSetStrokeColorWithColor( context, UIColor.blackColor().CGColor ) CGContextSetLineWidth(context, 3.0) CGContextMoveToPoint( context, startX, startY ) CGContextAddLineToPoint( context, endX, endY ) CGContextStrokePath( context ) } func drawCircle( context : CGContext?, radius : CGFloat, centreX : CGFloat, centreY : CGFloat ) { let diameter = radius * 2.0 let rect = CGRect( x: centreX - radius, y : centreY - radius, width : diameter, height : diameter ) CGContextSetLineWidth( context, 3.0 ) CGContextSetStrokeColorWithColor( context, UIColor.blackColor().CGColor ) CGContextStrokeEllipseInRect( context, rect ) } func applyContextToImage( context : CGContext? ) { let img = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() OverlayImage.setImage( img ) }
All being well, you can now run your WatchKit App and check that the black cross and circle have been drawn on top of the background image!
Pingback: How to write a Watch Face App for Apple Watch | musingstudio
Pingback: How to draw text onto an image in Apple Watch App | musingstudio
Pingback: How to switch watch faces using swipe gestures | musingstudio