Working with Images in WatchKit and watchOS 2
Previous | Table of Contents | Next |
A watchOS 2 WatchKit Context Menu Tutorial | A watchOS 2 WatchKit Animated Image Tutorial |
<google>BUY_WATCHOS2</google>
There are a number of factors that need to be taken into consideration when working with images within a WatchKit app such as whether the image can be included within the WatchKit app bundle or WatchKit extension, or needs to be transferred from the iPhone to the Apple Watch device at runtime. Unlike iPhone based apps, the size of the images used within a WatchKit app is also of key importance, particularly when working with large image files.
The goal of this chapter is to explore the various options for managing and displaying images in a WatchKit app including topics such as image caching, named images, animation, image compression, asset catalogs and image templates.
Displaying Images in WatchKit Apps
WatchKit provides two ways in which images can be displayed within a WatchKit app. The first involves the use of the WKInterfaceImage interface object class. When added to the scene of a WatchKit app storyboard, this object can be used to display images to the user. Another option involves setting an image as a background on the WKInterfaceGroup, WKInterfaceButton and WKInterfaceController classes. In both scenarios, the image can be specified within the storyboard file or set dynamically from within the code of the interface controller at runtime.
Wherever possible, images should be in PNG or JPEG format and sized to match the display size of the interface object on which they are to be displayed. Whilst WatchKit is able to handle other image formats, Apple warns that app performance is likely to be impacted adversely when those images are rendered.
Images can be generated and stored within the WatchKit extension or included as part of the WatchKit app or extension bundle.
Images Originating in the WatchKit Extension
Whenever a UIImage object is created or included within the extension of a WatchKit app, that image is local to the extension. The image contained within a UIImage object in a WatchKit extension can be displayed using the setImage, setImageData, setBackgroundImage and setBackgroundImageData methods of the interface object on which the image is to be displayed. The following code, for example, displays an image stored within a WatchKit extension onto a WKInterfaceImage object via an outlet connection named myImage:
let theImage = UIImage(named: "spaceship") myImage.setImage(theImage)
An alternative to including images as part of the WatchKit App extension is to include image files as part of the WatchKit app bundle. Such images are referred to as named images.
Understanding Named Images
Images that are bundled with the WatchKit app bundle are referred to as named images and are displayed using the setImageNamed and setBackgroundImageNamed methods of the interface objects on which the image is to be displayed. The following code, for example, displays a named image as the background for a WKInterfaceGroup object instance:
myGroup.setBackgroundImageNamed("spaceship")
Adding Images to a WatchKit App
Images can be stored as part of the WatchKit app bundle or WatchKit extension by placing them in the target’s Assets.xcassets asset catalog. Figure 17 1, for example, highlights the image assets catalogs for WatchKit app and WatchKit extension targets within the Xcode project navigator panel:
Figure 17-1
When files are stored into the image asset catalog the file names must end with “@2x”, for example [email protected]. This indicates to the asset catalog that the images are suitable for display on the retina screen of the Apple Watch device family. For the purposes of an example, assume that we have a file named [email protected] and need to add this as a named image to the WatchKit app bundle image asset catalog. The first step would be to select the Assets.xcassets catalog entry in the WatchKit app target as shown in Figure 17-2:
Figure 17-2
Making this selection will load the catalog into the main Xcode panel:
Figure 17-3
The left hand panel lists the image sets that are currently contained within the catalog. An image set collects together images in different sizes and resolutions. By default, the image asset catalog for a WatchKit app target contains a single image set named AppIcon. As the name suggests, this image set contains the icon used to represent the WatchKit app in a variety of locations such as the home screen and notification center.
To create a new image set for an image, simply locate the image in a Finder window and drag and drop it onto the image set panel as illustrated in Figure 17-4:
Figure 17-4
Multiple images may be imported by Ctrl-clicking within the image set panel, selecting the Import… menu option and navigating to the file system location containing the image files. If a folder is selected for import, all of the images in that folder will be imported into a single image set with a name reflecting that of the selected folder.
Once the image has been added, a new image set will be listed containing the 2x image. If this were an image set for an iOS app it would also be recommended to add 1x (for non-retina screens) and 3x (for iPhone 6 Plus and iPad devices) images to the image set. Since the Apple Watch has retina-only displays of similar sizes only the 2x image is required:
Figure 17-5
When referencing an image stored in an asset catalog, the “@2x” is dropped from the filename. With the image file named [email protected] contained in an image asset catalog for a WatchKit app target, the code to display this image on a WKInterfaceImage object would read as follows:
myImage.setImageNamed("spaceship")
Compressing Large Images
Depending on the image size, it can take a considerable amount of time to transfer an image from the iPhone to the Apple Watch. Consider a scenario in which the extension for a WatchKit app needs to display a photo that the user has taken using the iPhone camera. Transferring such an image could take over two minutes to transfer wirelessly from the iPhone to the watch. This is clearly an unacceptable amount of time to keep the user waiting.
In reality the image that is taken by the iPhone camera is orders of magnitude larger than is necessary to fit on the display of an Apple Watch. In fact, an image from the iPhone camera roll is measured in thousands of pixels while the display of even the largest Apple Watch model has a resolution of only 312 x 390 pixels. Clearly there is significant opportunity for reducing the size of any large images before they are transferred to the Apple Watch for display.
There are a number of options for reducing the size of any images both in terms of dimensions and storage space before the transfer to the watch takes place. The following code, for example, is useful for reducing the size of an image by a specified scale factor:
if let largeImage = UIImage(named: "myLargeImage") { let imageSize = largeImage.size let scale: CGFloat = 0.15 // Scale factor let reducedSize = CGSizeMake(imageSize.width * scale, imageSize.height * scale); UIGraphicsBeginImageContext(reducedSize) largeImage.drawInRect(CGRectMake(0, 0, reducedSize.width, reducedSize.height)) let compressedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() myImage.setImage(compressedImage) // Display the image }
In tests on an original PNG image file of 2448 x 3264 pixels with a size of 23MB the transfer time was reduced from approximately 3 minutes down to just 6 seconds after the image was compressed using the above code.
Specifying the WKInterfaceImage Object Dimensions in Code
When a WKInterfaceImage interface object is added to a storyboard scene using Interface Builder the dimensions of the object can be specified from within the Attributes Inspector panel (Figure 17 6). Options are available to allow the object to resize to accommodate the assigned image content, to size relative to the container in which the interface object is located, or to specify fixed height and width dimensions:
Figure 17-6
It is also possible to specify fixed height and width properties for a WKInterfaceImage object from within the code of the corresponding interface controller class. This involves establishing an outlet connection to the image object and making calls to the setHeight and setWidth methods of that instance, for example:
myImage.setHeight(100) myImage.setWidth(170)
Displaying Animated Images
The WKInterfaceImage interface object is able to display animated images. This animation takes the form of a sequence of images with each image representing a single frame of the animation. To create an animation sequence, the image files should be added to the project (preferably bundled with the WatchKit app as named images) using a file naming convention of name<sequence number>@2x.png (for example [email protected], [email protected], [email protected] and so on).
The way in which the sequence of animation images is converted into a single animation image and displayed to the user depends on whether the images are bundled with the WatchKit app as named images (the recommended approach for better performance), or reside within the extension.
If the animation images are bundled as named images within the WatchKit app, the animated image can be created and displayed using the setImageNamed method of the WKInterfaceImage instance, passing through the image file name prefix. Assuming a set of animation images named animation<image number>.png bundled with the WatchKit app, the animation would be displayed on a WKInterfaceImage instance with a single line of code as follows:
myImage.setImageNamed("animation")
When executed, the above method call collects together all of the appropriately named animation sequence images and uses them to create a single animated UIImage object which is then displayed on the corresponding WKInterfaceImage object.
In the case of extension-based animation images, an animated UIImage object must first be created using the animatedImageNamed method of the UIImage class which is then assigned to the WKInterfaceImage object via the object’s setImage method. For example:
let animatedImage = UIImage.animatedImageNamed("animation", duration: 20) myImage.setImage(animatedImage)
Once the animated image has been displayed, the startAnimating method of the WKInterfaceImage object can be used to begin the animation:
myImage.startAnimating()
The animation may be customized in terms of duration, repetition and the range of images used within the animation sequence using the startAnimatingWithImagesInRange method. The following code customizes an animation to use images from positions 1 through 4 of the image sequence with a 6 second duration and 2 repetitions:
myImage.startAnimatingWithImagesInRange(NSRange(location: 1, length: 4), duration: 6, repeatCount: 2)
An animation sequence may be stopped at any time via a call to the stopAnimating method:
myImage.stopAnimating()
The implementation of animation within a WatchKit app will be covered in greater detail in the next chapter entitled A WatchKit Animated Image Tutorial.
Template Images and Tinting
Images contained within an image asset catalog may be designated as template images. When an image is configured as a template image all color information in the image is ignored and the graphic displayed is defined by the stencil that is created by the transparent areas of the image in relation to the non-transparent areas. In Figure 17-7, for example, the rocket image is drawn against a transparent background:
Figure 17-7
When configured as a template image, the colored, non-transparent area of the image is filled with a solid color (light blue by default) creating the stencil image shown in Figure 17-8:
Figure 17-8
To specify that an image is to be rendered as a template, select the image in the asset catalog, display the Attributes Inspector panel and change the Render As menu to Template Image:
Figure 17-9
The default blue color used for the solid area of the image can be defined within Interface Builder by selecting the interface object on which the image is to be displayed and changing the Tint color setting. A tint color may also be assigned from within the interface controller code using the setTintColor method of the interface object, for example:
myImage.setTintColor(UIColor.redColor())
Summary
Images can be displayed within a WatchKit app scene either using an instance of the WKInterfaceImage class, or as a background image for WKInterfaceButton and WKInterfaceGroup classes. Images included in the WatchKit app bundle and WatchKit extension are pre-installed on the Apple Watch device along with the app. It is important to remember that those installed with the WatchKit app bundle are referred to as named images and are accessed differently in code compared to those located within the extension.
In addition to static images, WatchKit also provides support for animated images, an example of which will be demonstrated in the next chapter.
<google>BUY_WATCHOS2</google>
Previous | Table of Contents | Next |
A watchOS 2 WatchKit Context Menu Tutorial | A watchOS 2 WatchKit Animated Image Tutorial |