HEIC Image Compression for iOS
In this HEIC image compression tutorial, you’ll learn how to transform images into HEIC and JPEG formats, comparing their efficiency for optimum performance. By Ryan Ackermann.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
HEIC Image Compression for iOS
20 mins
Measuring Time
Now, you might be thinking this whole HEIC thing isn’t very impressive. The only thing clear at the moment is that compressing an image using HEIC is slow. Well, next you’ll see how much smaller a HEIC file is.
Included in the project is a helper file called Data+Additions.swift, which contains a computed property that pretty-prints the size of a Data
object. This property uses the Foundation framework’s handy ByteCountFormatter
to format the byte size.
In MainViewController.swift, replace TODO: Add image size here… inside compressJPGImage(with:)
with:
self.jpgSizeLabel.text = data.prettySize
Like the JPEG method, replace the TODO: Add image size here… inside compressHEICImage(with:)
with:
self.heicSizeLabel.text = data.prettySize
This will update the size label to reflect each image’s size.
Build and run. You should see right away how much more space you’ll save using HEIC, which is much more useful.
The last element to consider when choosing between HEIC and JPEG is time. The time it takes to compress is a key piece of data to consider. If your app needs speed over space, then HEIC might not be the best option for you.
At the top of MainViewController.swift add the following:
private let numberFormatter = NumberFormatter()
Formatters help make numbers more readable. This formatter will make it easier to read precise time intervals.
At the bottom of viewDidLoad()
, right before updateImages()
, add the following code:
numberFormatter.maximumSignificantDigits = 1
numberFormatter.maximumFractionDigits = 3
This configures the formatter to limit its output, as the difference between the two compression methods is noticeable. If the two were closer, then a higher level of precision would be beneficial.
Add the following method after resetLabels()
:
private func elapsedTime(from startDate: Date) -> String? {
let endDate = Date()
let interval = endDate.timeIntervalSince(startDate)
let intervalNumber = NSNumber(value: interval)
return numberFormatter.string(from: intervalNumber)
}
This method uses the formatter you declared earlier. It takes in a start date, then calculates the duration based on that and returns an optional string using the number formatter.
Inside compressJPGImage(with:)
add this to the top of the method:
let startDate = Date()
Next, replace the TODO: Add compression time here… inside compressJPGImage(with:)
:
if let time = self.elapsedTime(from: startDate) {
self.jpgTimeLabel.text = "\(time) s"
}
The start date gets recorded right away, ensuring that all parts of the method contribute to the calculated duration. The time label is then set as soon as the decoding finishes on the main queue.
Like before, you’ll need to add the accompanying logic for the HEIC image compression method. Add this to the top of compressHEICImage(with:)
:
let startDate = Date()
And replace the TODO: Add compression time here… with the following:
if let time = self.elapsedTime(from: startDate) {
self.heicTimeLabel.text = "\(time) s"
}
When typing or copying similar code like this, make sure you’re setting the correct label. Notice this heicTimeLabel
is set in the HEIC method and the jpgTimeLabel
for the JPG method.
Build and run.
Now you can make a fully informed decision. JPG compression is very fast at the cost of a larger image. Conversely, the HEIC image is smaller but its compression is slower.
Being aware of your user’s device is a good thing. Since HEIC takes longer you might defer saving space when the battery is low. You could also check the device’s free storage space. If the disk is over 75 percent full, always opt for the HEIC image compression.
Sharing HEIC
One final thing to consider is sharing an HEIC image. The JPEG compression algorithm has been the standard on the web for many years, but the flexibility and space saving that HEIC has to offer is impressive.
Many well designed sites already compress their content with JPEG. If a site’s files are already small, then saving 50 percent isn’t as enticing. But saving half of the space of high quality iPhone photos is much more meaningful.
While it might not be the right time to go all in on HEIC on the web, some sites offer support to upload HEIC photos. Within the Apple ecosystem, other apps shouldn’t have a problem handling HEIC content. When sharing content, it’d be beneficial to offer the choice of what format to share.
Add the following method below updateImages()
:
private func shareImage(_ image: UIImage) {
let avc = UIActivityViewController(
activityItems: [image],
applicationActivities: nil
)
present(avc, animated: true)
}
This method shares an image compressed in either format. The UIImage class takes care of handling its underlying format so you don’t have to.
To use this, add to following to shareButtonPressed()
in MainViewController.swift:
let avc = UIAlertController(
title: "Share",
message: "How would you like to share?",
preferredStyle: .alert
)
if let jpgImage = jpgImageView.image {
avc.addAction(UIAlertAction(title: "JPG", style: .default) { _ in
self.shareImage(jpgImage)
})
}
if let heicImage = heicImageView.image {
avc.addAction(UIAlertAction(title: "HEIC", style: .default) { _ in
self.shareImage(heicImage)
})
}
avc.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(avc, animated: true)
This sets up the simple sharing feature for this app, as there are already the two compressed images in the image views. For a real world app that stores all images as HEIC you’d need to convert the image to JPEG before sharing.
One last quality of life feature to add to the example app is to prevent sharing when no images are available. The HEIC image compression might fail or take longer for a larger file. During this time it’d be beneficial to disable the share button to avoid confusion and safeguard a good user experience.
Inside updateImages()
add:
navigationItem.leftBarButtonItem?.isEnabled = false
This line disables the share button before the compression gets kicked off.
Next, replace each occurrence of TODO: Disable share button here… with:
self.navigationItem.leftBarButtonItem?.isEnabled = true
As each compression finishes, the share button is re-enabled. You’ll see only the JPG option in the alert if the HEIC image compression is still processing. For your app, it might make more sense to wait until both options are available.
Build and run.
Congratulations, the example app is complete!
Where to Go From Here
You should now have a working knowledge of HEIC, its benefits and its weaknesses. There are many uses cases for HEIC, and it’ll only gain in popularity over time.
To recap the benefits of HEIC:
- 50 percent smaller files sizes compared to JPEG.
- Contain many image items.
- Image derivations, non destructive edits.
- Image sequences, like Live Photos.
- Auxiliary image items for storing depth or HDR data.
- Image metadata like location or camera information.
You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial.
For more information about HEIC from Apple take a look at the WWDC session on HEIF and HEVC from 2017.
I hope you enjoyed this tutorial! If you have any questions or comments, please join the discussion below.