Multi-platform image resizing
In this post, let’s take a look at how to resize images in UIKit & AppKit. The result will work on iOS, macOS, tvOS & watchOS and lets us use the the same APIs on all platforms.
Image resizing is pretty straightforward in Swift, so let’s look at how to implement it in UIKit and AppKit, then add more functionality that applies to all platforms.
In UIKit, we can add a UIImage
extension that resizes images using the image context:
#if canImport(UIKit)
import UIKit
public extension UIImage {
func resized(to size: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(in: CGRect(origin: CGPoint.zero, size: size))
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
}
#endif
In AppKit, we can add an NSImage
extension that resizes images in a different way:
#if canImport(AppKit)
import AppKit
public extension NSImage {
func resized(to newSize: CGSize) -> NSImage? {
let newImage = NSImage(size: newSize)
newImage.lockFocus()
let sourceRect = NSMakeRect(0, 0, size.width, size.height)
let destRect = NSMakeRect(0, 0, newSize.width, newSize.height)
draw(in: destRect, from: sourceRect, operation: .sourceOver, fraction: CGFloat(1))
newImage.unlockFocus()
return newImage
}
}
#endif
Since UIImage
& NSImage
now have the same API for resizing images to a certain size, we can extend both types with more resizing functionality that use this underlying functionality.
Let’s first define a platform-agnostic image typealias, to have a single name for the image:
#if canImport(AppKit)
import class AppKit.NSImage
public typealias ImageRepresentable = NSImage
#endif
#if canImport(UIKit)
import class UIKit.UIImage
public typealias ImageRepresentable = UIImage
#endif
This lets us use ImageRepresentable
to handle both UIImage
& NSImage
. Let’s use it to add ways to resize images in more ways, using the resizing function we defined earlier:
public extension ImageRepresentable {
func resized(toHeight points: CGFloat) -> ImageRepresentable? {
let ratio = points / size.height
let width = size.width * ratio
let newSize = CGSize(width: width, height: points)
return resized(to: newSize)
}
func resized(toWidth points: CGFloat) -> ImageRepresentable? {
let ratio = points / size.width
let height = size.height * ratio
let newSize = CGSize(width: points, height: height)
return resized(to: newSize)
}
}
That’s it - you can now use resized(to:)
, resized(toWidth:)
& resized(toHeight:)
with UIImage
& NSImage
, to resize images on all platforms.
I have added these extension to SwiftUIKit. Feel free to try them out and let me know what you think.
Discussions & More
If you found this interesting and would like to share your thoughts, please comment in the Disqus section below or reply to this tweet.
Follow on Twitter and Mastodon to be notified when new content & articles are published.