Merge two UIImages swift 4 - ios

I have a captured image from a device's camera and I want to add a UITextView on it and then save it into the gallery. I'm using the following code to get an image from the text view
extension UITextView {
var imageOfView: UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0)
self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
Then, by using the following code, I try to add the above image to the taken image:
extension UIImage {
func combine(with image: UIImage, at point: CGPoint, isLandscapeMode: Bool = false) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0)
self.draw(at: .zero)
image.draw(at: point)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}
}
finally, I call the method here to send the edited image to the previous page:
onImageEdited?(capturedImage.combine(with: textArea.imageOfView, at: textArea.frame.origin, isLandscapeMode: true))
Looking at the below screenshots, it's seen that the size changes and the position seem to be randomly chosen. Any idea why and how can I fix that?
During the editing:
After it's saved:
P.S: The captured image and the editing page are in Landscape mode

Try this code in Swift 4.2
func stitchImages(images: [UIImage], isVertical: Bool) -> UIImage {
var stitchedImages : UIImage!
if images.count > 0 {
var maxWidth = CGFloat(0), maxHeight = CGFloat(0)
for image in images {
if image.size.width > maxWidth {
maxWidth = image.size.width
}
if image.size.height > maxHeight {
maxHeight = image.size.height
}
}
var totalSize : CGSize
let maxSize = CGSize(width: maxWidth, height: maxHeight)
if isVertical {
totalSize = CGSize(width: maxSize.width, height: maxSize.height * (CGFloat)(images.count))
} else {
totalSize = CGSize(width: maxSize.width * (CGFloat)(images.count), height: maxSize.height)
}
UIGraphicsBeginImageContext(totalSize)
for image in images {
let offset = (CGFloat)(images.index(of: image)!)
let rect = AVMakeRect(aspectRatio: image.size, insideRect: isVertical ?
CGRect(x: 0, y: maxSize.height * offset, width: maxSize.width, height: maxSize.height) :
CGRect(x: maxSize.width * offset, y: 0, width: maxSize.width, height: maxSize.height))
image.draw(in: rect)
}
stitchedImages = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
return stitchedImages
}
}
and you can use this:
#IBOutlet weak var imageView: UIImageView!
let imageArray = [image1, image3, image4, image4]
override func viewDidLoad() {
super.viewDidLoad()
let image = stitchImages(images: imageArray, isVertical: false)
imageView.image = image
}

func combine(text: String, withImage image: UIImage, atPoint point: CGPoint) -> UIImage? {
// Variables
let textColor = UIColor.white
let font = UIFont.systemFont(ofSize: 12)
let attributes: [NSAttributedStringKey: Any] = [
.foregroundColor: textColor,
.font: font
]
// Set scale and begin context
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(image.size, false, scale)
// Draw
image.draw(in: CGRect(origin: .zero, size: image.size))
let rect = CGRect(origin: point, size: image.size)
text.draw(in: rect, withAttributes: attributes)
// Get the new image
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
You could also create an extension for UIImageView and render a new image from all of its subviews (e.x. the UITextView)
extension UIImageView {
func renderSubviewsToImage() -> UIImage? {
guard let image = image else { return nil }
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(image.size, false, scale)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
self.layer.render(in: context)
let renderedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return renderedImage
}
}

Try this one, I have commented the code which I have edited from the above code, please check it out and let me know it worked for you.
func stitchImages(images: [UIImage], isVertical: Bool) -> UIImage {
var stitchedImages : UIImage!
if images.count > 0 {
var maxWidth = CGFloat(0), maxHeight = CGFloat(0)
for image in images {
if image.size.width > maxWidth {
maxWidth = image.size.width
}
if image.size.height > maxHeight {
maxHeight = image.size.height
}
}
var totalSize : CGSize
let maxSize = CGSize(width: maxWidth, height: maxHeight)
if isVertical {
totalSize = CGSize(width: maxSize.width, height: maxSize.height * (CGFloat)(images.count))
} else {
totalSize = CGSize(width: maxSize.width, height: maxSize.height)
}
UIGraphicsBeginImageContext(totalSize)
let offset = (CGFloat)(0)
// Need to change or adjust the frame get get reach your reqirement
let rect1 = AVMakeRect(aspectRatio: images[1].size, insideRect: isVertical ? CGRect(x: 0, y: maxSize.height * offset, width: maxSize.width, height: maxSize.height) : CGRect(x: maxSize.width * offset, y: 0, width: maxSize.width, height: maxSize.height))
images[1].draw(in: rect1)
let rect2 = AVMakeRect(aspectRatio: images[0].size, insideRect: isVertical ? CGRect(x: 0, y: maxSize.height * offset, width: maxSize.width - 60, height: maxSize.height - 500) : CGRect(x: maxSize.width * offset, y: 250, width: maxSize.width - 60, height: maxSize.height - 500))
images[0].draw(in: rect2)
// for image in images {
// let offset = (CGFloat)(0)
// let rect = AVMakeRect(aspectRatio: image.size, insideRect: isVertical ?
// CGRect(x: 0, y: maxSize.height * offset, width: maxSize.width, height: maxSize.height) :
// CGRect(x: maxSize.width * offset, y: 0, width: maxSize.width, height: maxSize.height))
// image.draw(in: rect)
// }
stitchedImages = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
return stitchedImages
}

Related

How do change the size of an image (not the size of the file, not the size of an image view) in iOS? [duplicate]

I am making an app for iOS, using Swift and Parse.com
I am trying to let the user select a picture from an image picker and then resize the selected image to 200x200 pixels before uploading to my backend.
Parse.com have a tutorial for an Instagram copy app called "AnyPic" which gives this code for resizing images, but it is in Objective-C....
// Resize the image to be square (what is shown in the preview)
UIImage *resizedImage = [anImage resizedImageWithContentMode:UIViewContentModeScaleAspectFit
bounds:CGSizeMake(560.0f, 560.0f)
interpolationQuality:kCGInterpolationHigh];
// Create a thumbnail and add a corner radius for use in table views
UIImage *thumbnailImage = [anImage thumbnailImage:86.0f
transparentBorder:0.0f
cornerRadius:10.0f
interpolationQuality:kCGInterpolationDefault];
How would I create a 200x200px version of the selected picture (to then upload) in Swift?
And, what is the thumbnailImage function doing?
See my blog post, Resize image in swift and objective C, for further details.
Image resize function in swift as below.
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage? {
let size = image.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(origin: .zero, size: newSize)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Use the above function and resize image with 200*200 as below code
self.resizeImage(UIImage(named: "yourImageName")!, targetSize: CGSizeMake(200.0, 200.0))
swift3 updated
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
Details
Xcode 10.2.1 (10E1001), Swift 5
Links
https://gist.github.com/eugenebokhan/5e62a0155754ae6aa6c3c13cf1744930
Image Resizing Techniques
Solution
import UIKit
import CoreGraphics
import Accelerate
extension UIImage {
public enum ResizeFramework {
case uikit, coreImage, coreGraphics, imageIO, accelerate
}
/// Resize image with ScaleAspectFit mode and given size.
///
/// - Parameter dimension: width or length of the image output.
/// - Parameter resizeFramework: Technique for image resizing: UIKit / CoreImage / CoreGraphics / ImageIO / Accelerate.
/// - Returns: Resized image.
func resizeWithScaleAspectFitMode(to dimension: CGFloat, resizeFramework: ResizeFramework = .coreGraphics) -> UIImage? {
if max(size.width, size.height) <= dimension { return self }
var newSize: CGSize!
let aspectRatio = size.width/size.height
if aspectRatio > 1 {
// Landscape image
newSize = CGSize(width: dimension, height: dimension / aspectRatio)
} else {
// Portrait image
newSize = CGSize(width: dimension * aspectRatio, height: dimension)
}
return resize(to: newSize, with: resizeFramework)
}
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Parameter resizeFramework: Technique for image resizing: UIKit / CoreImage / CoreGraphics / ImageIO / Accelerate.
/// - Returns: Resized image.
public func resize(to newSize: CGSize, with resizeFramework: ResizeFramework = .coreGraphics) -> UIImage? {
switch resizeFramework {
case .uikit: return resizeWithUIKit(to: newSize)
case .coreGraphics: return resizeWithCoreGraphics(to: newSize)
case .coreImage: return resizeWithCoreImage(to: newSize)
case .imageIO: return resizeWithImageIO(to: newSize)
case .accelerate: return resizeWithAccelerate(to: newSize)
}
}
// MARK: - UIKit
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
private func resizeWithUIKit(to newSize: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(newSize, true, 1.0)
self.draw(in: CGRect(origin: .zero, size: newSize))
defer { UIGraphicsEndImageContext() }
return UIGraphicsGetImageFromCurrentImageContext()
}
// MARK: - CoreImage
/// Resize CI image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
// https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html
private func resizeWithCoreImage(to newSize: CGSize) -> UIImage? {
guard let cgImage = cgImage, let filter = CIFilter(name: "CILanczosScaleTransform") else { return nil }
let ciImage = CIImage(cgImage: cgImage)
let scale = (Double)(newSize.width) / (Double)(ciImage.extent.size.width)
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(NSNumber(value:scale), forKey: kCIInputScaleKey)
filter.setValue(1.0, forKey: kCIInputAspectRatioKey)
guard let outputImage = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return nil }
let context = CIContext(options: [.useSoftwareRenderer: false])
guard let resultCGImage = context.createCGImage(outputImage, from: outputImage.extent) else { return nil }
return UIImage(cgImage: resultCGImage)
}
// MARK: - CoreGraphics
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
private func resizeWithCoreGraphics(to newSize: CGSize) -> UIImage? {
guard let cgImage = cgImage, let colorSpace = cgImage.colorSpace else { return nil }
let width = Int(newSize.width)
let height = Int(newSize.height)
let bitsPerComponent = cgImage.bitsPerComponent
let bytesPerRow = cgImage.bytesPerRow
let bitmapInfo = cgImage.bitmapInfo
guard let context = CGContext(data: nil, width: width, height: height,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow, space: colorSpace,
bitmapInfo: bitmapInfo.rawValue) else { return nil }
context.interpolationQuality = .high
let rect = CGRect(origin: CGPoint.zero, size: newSize)
context.draw(cgImage, in: rect)
return context.makeImage().flatMap { UIImage(cgImage: $0) }
}
// MARK: - ImageIO
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
private func resizeWithImageIO(to newSize: CGSize) -> UIImage? {
var resultImage = self
guard let data = jpegData(compressionQuality: 1.0) else { return resultImage }
let imageCFData = NSData(data: data) as CFData
let options = [
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceThumbnailMaxPixelSize: max(newSize.width, newSize.height)
] as CFDictionary
guard let source = CGImageSourceCreateWithData(imageCFData, nil),
let imageReference = CGImageSourceCreateThumbnailAtIndex(source, 0, options) else { return resultImage }
resultImage = UIImage(cgImage: imageReference)
return resultImage
}
// MARK: - Accelerate
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
private func resizeWithAccelerate(to newSize: CGSize) -> UIImage? {
var resultImage = self
guard let cgImage = cgImage, let colorSpace = cgImage.colorSpace else { return nil }
// create a source buffer
var format = vImage_CGImageFormat(bitsPerComponent: numericCast(cgImage.bitsPerComponent),
bitsPerPixel: numericCast(cgImage.bitsPerPixel),
colorSpace: Unmanaged.passUnretained(colorSpace),
bitmapInfo: cgImage.bitmapInfo,
version: 0,
decode: nil,
renderingIntent: .absoluteColorimetric)
var sourceBuffer = vImage_Buffer()
defer {
sourceBuffer.data.deallocate()
}
var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags))
guard error == kvImageNoError else { return resultImage }
// create a destination buffer
let destWidth = Int(newSize.width)
let destHeight = Int(newSize.height)
let bytesPerPixel = cgImage.bitsPerPixel
let destBytesPerRow = destWidth * bytesPerPixel
let destData = UnsafeMutablePointer<UInt8>.allocate(capacity: destHeight * destBytesPerRow)
defer {
destData.deallocate()
}
var destBuffer = vImage_Buffer(data: destData, height: vImagePixelCount(destHeight), width: vImagePixelCount(destWidth), rowBytes: destBytesPerRow)
// scale the image
error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, numericCast(kvImageHighQualityResampling))
guard error == kvImageNoError else { return resultImage }
// create a CGImage from vImage_Buffer
let destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue()
guard error == kvImageNoError else { return resultImage }
// create a UIImage
if let scaledImage = destCGImage.flatMap({ UIImage(cgImage: $0) }) {
resultImage = scaledImage
}
return resultImage
}
}
Usage
Get image size
import UIKit
// https://stackoverflow.com/a/55765409/4488252
extension UIImage {
func getFileSizeInfo(allowedUnits: ByteCountFormatter.Units = .useMB,
countStyle: ByteCountFormatter.CountStyle = .memory,
compressionQuality: CGFloat = 1.0) -> String? {
// https://developer.apple.com/documentation/foundation/bytecountformatter
let formatter = ByteCountFormatter()
formatter.allowedUnits = allowedUnits
formatter.countStyle = countStyle
return getSizeInfo(formatter: formatter, compressionQuality: compressionQuality)
}
func getSizeInfo(formatter: ByteCountFormatter, compressionQuality: CGFloat = 1.0) -> String? {
guard let imageData = jpegData(compressionQuality: compressionQuality) else { return nil }
return formatter.string(fromByteCount: Int64(imageData.count))
}
}
Test function
private func test() {
guard let img = UIImage(named: "img") else { return }
printInfo(of: img, title: "original image |")
let dimension: CGFloat = 2000
var framework: UIImage.ResizeFramework = .accelerate
var startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
framework = .coreGraphics
startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
framework = .coreImage
startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
framework = .imageIO
startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
framework = .uikit
startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
}
private func printInfo(of image: UIImage, title: String, with resizeFramework: UIImage.ResizeFramework? = nil, startedTime: Date? = nil) {
var description = "\(title) \(image.size)"
if let startedTime = startedTime { description += ", execution time: \(Date().timeIntervalSince(startedTime))" }
if let fileSize = image.getFileSizeInfo(compressionQuality: 0.9) { description += ", size: \(fileSize)" }
if let resizeFramework = resizeFramework { description += ", framework: \(resizeFramework)" }
print(description)
}
Output
original image | (5790.0, 8687.0), size: 17.1 MB
resized image | (1333.0, 2000.0), execution time: 0.8192930221557617, size: 1.1 MB, framework: accelerate
resized image | (1333.0, 2000.0), execution time: 0.44696998596191406, size: 1 MB, framework: coreGraphics
resized image | (1334.0, 2000.0), execution time: 54.172922015190125, size: 1.1 MB, framework: coreImage
resized image | (1333.0, 2000.0), execution time: 1.8765920400619507, size: 1.1 MB, framework: imageIO
resized image | (1334.0, 2000.0), execution time: 0.4638739824295044, size: 1 MB, framework: uikit
For Swift 4.0 and iOS 10
extension UIImage {
func resizeImage(_ dimension: CGFloat, opaque: Bool, contentMode: UIViewContentMode = .scaleAspectFit) -> UIImage {
var width: CGFloat
var height: CGFloat
var newImage: UIImage
let size = self.size
let aspectRatio = size.width/size.height
switch contentMode {
case .scaleAspectFit:
if aspectRatio > 1 { // Landscape image
width = dimension
height = dimension / aspectRatio
} else { // Portrait image
height = dimension
width = dimension * aspectRatio
}
default:
fatalError("UIIMage.resizeToFit(): FATAL: Unimplemented ContentMode")
}
if #available(iOS 10.0, *) {
let renderFormat = UIGraphicsImageRendererFormat.default()
renderFormat.opaque = opaque
let renderer = UIGraphicsImageRenderer(size: CGSize(width: width, height: height), format: renderFormat)
newImage = renderer.image {
(context) in
self.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
}
} else {
UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), opaque, 0)
self.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
}
return newImage
}
}
Since #KiritModi 's answer is from 2015, this is the Swift 3.0's version:
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / image.size.width
let heightRatio = targetSize.height / image.size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
For Swift 5.0 and iOS 12
extension UIImage {
func imageResized(to size: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { _ in
draw(in: CGRect(origin: .zero, size: size))
}
}
}
use:
let image = #imageLiteral(resourceName: "ic_search")
cell!.search.image = image.imageResized(to: cell!.search.frame.size)
For Swift 4 I would just make an extension on UIImage with referencing to self.
import UIKit
extension UIImage {
func resizeImage(targetSize: CGSize) -> UIImage {
let size = self.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
let newSize = widthRatio > heightRatio ? CGSize(width: size.width * heightRatio, height: size.height * heightRatio) : CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
It's also possible to use AlamofireImage (https://github.com/Alamofire/AlamofireImage)
let size = CGSize(width: 30.0, height: 30.0)
let aspectScaledToFitImage = image.af_imageAspectScaled(toFit: size)
The function in the previous post gave me a blurry result.
Swift 3 Version and Extension style
This answer come from #Kirit Modi.
extension UIImage {
func resizeImage(targetSize: CGSize) -> UIImage {
let size = self.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
Updated Swift 5 version that uses the latest UIGraphicsImageRenderer API.
extension UIImage {
public func resized(to target: CGSize) -> UIImage {
let ratio = min(
target.height / size.height, target.width / size.width
)
let new = CGSize(
width: size.width * ratio, height: size.height * ratio
)
let renderer = UIGraphicsImageRenderer(size: new)
return renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: new))
}
}
}
Swift 4, extension version, NO WHITE LINE ON EDGES.
Nobody seems to be mentioning that if image.draw() is called with non-integer values, resulting image could show a white line artifact at the right or bottom edge.
extension UIImage {
func scaled(with scale: CGFloat) -> UIImage? {
// size has to be integer, otherwise it could get white lines
let size = CGSize(width: floor(self.size.width * scale), height: floor(self.size.height * scale))
UIGraphicsBeginImageContext(size)
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Swift 5 version respecting ratio (scaleToFill) and centering image:
extension UIImage {
func resized(to newSize: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size: newSize).image { _ in
let hScale = newSize.height / size.height
let vScale = newSize.width / size.width
let scale = max(hScale, vScale) // scaleToFill
let resizeSize = CGSize(width: size.width*scale, height: size.height*scale)
var middle = CGPoint.zero
if resizeSize.width > newSize.width {
middle.x -= (resizeSize.width-newSize.width)/2.0
}
if resizeSize.height > newSize.height {
middle.y -= (resizeSize.height-newSize.height)/2.0
}
draw(in: CGRect(origin: middle, size: resizeSize))
}
}
}
Swift 4 Version
extension UIImage {
func resizeImage(_ newSize: CGSize) -> UIImage? {
func isSameSize(_ newSize: CGSize) -> Bool {
return size == newSize
}
func scaleImage(_ newSize: CGSize) -> UIImage? {
func getScaledRect(_ newSize: CGSize) -> CGRect {
let ratio = max(newSize.width / size.width, newSize.height / size.height)
let width = size.width * ratio
let height = size.height * ratio
return CGRect(x: 0, y: 0, width: width, height: height)
}
func _scaleImage(_ scaledRect: CGRect) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(scaledRect.size, false, 0.0);
draw(in: scaledRect)
let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
UIGraphicsEndImageContext()
return image
}
return _scaleImage(getScaledRect(newSize))
}
return isSameSize(newSize) ? self : scaleImage(newSize)!
}
}
UIImage Extension Swift 5
extension UIImage {
func resize(_ width: CGFloat, _ height:CGFloat) -> UIImage? {
let widthRatio = width / size.width
let heightRatio = height / size.height
let ratio = widthRatio > heightRatio ? heightRatio : widthRatio
let newSize = CGSize(width: size.width * ratio, height: size.height * ratio)
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
Use :
UIImage().resize(200, 300)
All of the listed answers so far seem to result in an image of a reduced size, however the size isn't measured in pixels. Here's a Swift 5, pixel-based resize.
extension UIImage {
func resize(_ max_size: CGFloat) -> UIImage {
// adjust for device pixel density
let max_size_pixels = max_size / UIScreen.main.scale
// work out aspect ratio
let aspectRatio = size.width/size.height
// variables for storing calculated data
var width: CGFloat
var height: CGFloat
var newImage: UIImage
if aspectRatio > 1 {
// landscape
width = max_size_pixels
height = max_size_pixels / aspectRatio
} else {
// portrait
height = max_size_pixels
width = max_size_pixels * aspectRatio
}
// create an image renderer of the correct size
let renderer = UIGraphicsImageRenderer(size: CGSize(width: width, height: height), format: UIGraphicsImageRendererFormat.default())
// render the image
newImage = renderer.image {
(context) in
self.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
}
// return the image
return newImage
}
}
Usage:
image.resize(500)
Here's a general method (in Swift 5) for downscaling an image to fit a size. The resulting image can have the same aspect ratio as the original, or it can be the target size with the original image centered in it. If the image is smaller than the target size, it is not resized.
extension UIImage {
func scaledDown(into size:CGSize, centered:Bool = false) -> UIImage {
var (targetWidth, targetHeight) = (self.size.width, self.size.height)
var (scaleW, scaleH) = (1 as CGFloat, 1 as CGFloat)
if targetWidth > size.width {
scaleW = size.width/targetWidth
}
if targetHeight > size.height {
scaleH = size.height/targetHeight
}
let scale = min(scaleW,scaleH)
targetWidth *= scale; targetHeight *= scale
let sz = CGSize(width:targetWidth, height:targetHeight)
if !centered {
return UIGraphicsImageRenderer(size:sz).image { _ in
self.draw(in:CGRect(origin:.zero, size:sz))
}
}
let x = (size.width - targetWidth)/2
let y = (size.height - targetHeight)/2
let origin = CGPoint(x:x,y:y)
return UIGraphicsImageRenderer(size:size).image { _ in
self.draw(in:CGRect(origin:origin, size:sz))
}
}
}
Swift 4 Solution-
Use this function
func image(with image: UIImage, scaledTo newSize: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
drawingImageView.image = newImage
return newImage ?? UIImage()
}
Calling a function:-
image(with: predictionImage, scaledTo: CGSize(width: 28.0, height: 28.0)
here 28.0 is the pixel size that you want to set
Swift 4.2 version of #KiritModi answer
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
calling of resizeimage method
let image1 = resizeimage(image: myimage.image!, withSize: CGSize(width:200, height: 200))
method for resizeing image
func resizeimage(image:UIImage,withSize:CGSize) -> UIImage {
var actualHeight:CGFloat = image.size.height
var actualWidth:CGFloat = image.size.width
let maxHeight:CGFloat = withSize.height
let maxWidth:CGFloat = withSize.width
var imgRatio:CGFloat = actualWidth/actualHeight
let maxRatio:CGFloat = maxWidth/maxHeight
let compressionQuality = 0.5
if (actualHeight>maxHeight||actualWidth>maxWidth) {
if (imgRatio<maxRatio){
//adjust width according to maxHeight
imgRatio = maxHeight/actualHeight
actualWidth = imgRatio * actualWidth
actualHeight = maxHeight
}else if(imgRatio>maxRatio){
// adjust height according to maxWidth
imgRatio = maxWidth/actualWidth
actualHeight = imgRatio * actualHeight
actualWidth = maxWidth
}else{
actualHeight = maxHeight
actualWidth = maxWidth
}
}
let rec:CGRect = CGRect(x:0.0,y:0.0,width:actualWidth,height:actualHeight)
UIGraphicsBeginImageContext(rec.size)
image.draw(in: rec)
let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
let imageData = UIImageJPEGRepresentation(image, CGFloat(compressionQuality))
UIGraphicsEndImageContext()
let resizedimage = UIImage(data: imageData!)
return resizedimage!
}
Here you have two simple functions of UIImage extension:
func scaledWithMaxWidthOrHeightValue(value: CGFloat) -> UIImage? {
let width = self.size.width
let height = self.size.height
let ratio = width/height
var newWidth = value
var newHeight = value
if ratio > 1 {
newWidth = width * (newHeight/height)
} else {
newHeight = height * (newWidth/width)
}
UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, 0)
draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func scaled(withScale scale: CGFloat) -> UIImage? {
let size = CGSize(width: self.size.width * scale, height: self.size.height * scale)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
SWIFT 5 - XCODE 12 -- RESIZE IMAGE & No White line
I used a wonderful solution above for Swift 5. And I changed one bit to include the term "floor" as I was getting a white line around my resized images. This rounds it to the nearest pixel or something so it looks great! I also had to change the syntax around the image name when the function is called (last line).
//method for resizing image
func resizeimage(image:UIImage,withSize:CGSize) -> UIImage {
var actualHeight:CGFloat = image.size.height
var actualWidth:CGFloat = image.size.width
let maxHeight:CGFloat = withSize.height
let maxWidth:CGFloat = withSize.width
var imgRatio:CGFloat = actualWidth/actualHeight
let maxRatio:CGFloat = maxWidth/maxHeight
let compressionQuality = 0.5
if (actualHeight>maxHeight||actualWidth>maxWidth) {
if (imgRatio<maxRatio){
//adjust width according to maxHeight
imgRatio = maxHeight/actualHeight
actualWidth = floor(imgRatio * actualWidth)
actualHeight = maxHeight
}else if(imgRatio>maxRatio){
// adjust height according to maxWidth
imgRatio = maxWidth/actualWidth
actualHeight = imgRatio * actualHeight
actualWidth = maxWidth
}else{
actualHeight = maxHeight
actualWidth = maxWidth
}
}
let rec:CGRect = CGRect(x:0.0,y:0.0,width:actualWidth,height:actualHeight)
UIGraphicsBeginImageContext(rec.size)
image.draw(in: rec)
let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
let imageData = UIImageJPEGRepresentation(image, CGFloat(compressionQuality))
UIGraphicsEndImageContext()
let resizedimage = UIImage(data: imageData!)
return resizedimage!
}
//calling of resizeimage method:
let myimage = UIImage(named: "imagename")
let image1 = resizeimage(image: myimage!, withSize: CGSize(width:50, height: 50)).withRenderingMode(.alwaysOriginal)
Example is for image minimize to 1024 and less
func resizeImage(image: UIImage) -> UIImage {
if image.size.height >= 1024 && image.size.width >= 1024 {
UIGraphicsBeginImageContext(CGSize(width:1024, height:1024))
image.draw(in: CGRect(x:0, y:0, width:1024, height:1024))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
else if image.size.height >= 1024 && image.size.width < 1024
{
UIGraphicsBeginImageContext(CGSize(width:image.size.width, height:1024))
image.draw(in: CGRect(x:0, y:0, width:image.size.width, height:1024))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
else if image.size.width >= 1024 && image.size.height < 1024
{
UIGraphicsBeginImageContext(CGSize(width:1024, height:image.size.height))
image.draw(in: CGRect(x:0, y:0, width:1024, height:image.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
else
{
return image
}
}
You can use this for fit image at Swift 3;
extension UIImage {
func resizedImage(newSize: CGSize) -> UIImage {
// Guard newSize is different
guard self.size != newSize else { return self }
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
func resizedImageWithinRect(rectSize: CGSize) -> UIImage {
let widthFactor = size.width / rectSize.width
let heightFactor = size.height / rectSize.height
var resizeFactor = widthFactor
if size.height > size.width {
resizeFactor = heightFactor
}
let newSize = CGSize(width: size.width/resizeFactor, height: size.height/resizeFactor)
let resized = resizedImage(newSize: newSize)
return resized
}
}
Usage;
let resizedImage = image.resizedImageWithinRect(rectSize: CGSize(width: 1900, height: 1900))

Stitch multiple images together horizontally (Swift 3)

I want to stitch multiple images together horizontally and save it as one image. Under this question I found this suggested solution:
import UIKit
import AVFoundation
func stitchImages(images: [UIImage], isVertical: Bool) -> UIImage {
var stitchedImages : UIImage!
if images.count > 0 {
var maxWidth = CGFloat(0), maxHeight = CGFloat(0)
for image in images {
if image.size.width > maxWidth {
maxWidth = image.size.width
}
if image.size.height > maxHeight {
maxHeight = image.size.height
}
}
var totalSize : CGSize
let maxSize = CGSize(width: maxWidth, height: maxHeight)
if isVertical {
totalSize = CGSize(width: maxSize.width, height: maxSize.height * (CGFloat)(images.count))
} else {
totalSize = CGSize(width: maxSize.width * (CGFloat)(images.count), height: maxSize.height)
}
UIGraphicsBeginImageContext(totalSize)
for image in images {
let offset = (CGFloat)(images.index(of: image)!)
let rect = AVMakeRect(aspectRatio: image.size, insideRect: isVertical ?
CGRect(x: 0, y: maxSize.height * offset, width: maxSize.width, height: maxSize.height) :
CGRect(x: maxSize.width * offset, y: 0, width: maxSize.width, height: maxSize.height))
image.draw(in: rect)
}
stitchedImages = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
return stitchedImages
}
However, I don't know how to use it. Can someone make an example on how to use this with an array of images?
Thank you!
a little update with
Swift 4
extension Array where Element: UIImage {
func stitchImages(isVertical: Bool) -> UIImage {
let maxWidth = self.compactMap { $0.size.width }.max()
let maxHeight = self.compactMap { $0.size.height }.max()
let maxSize = CGSize(width: maxWidth ?? 0, height: maxHeight ?? 0)
let totalSize = isVertical ?
CGSize(width: maxSize.width, height: maxSize.height * (CGFloat)(self.count))
: CGSize(width: maxSize.width * (CGFloat)(self.count), height: maxSize.height)
let renderer = UIGraphicsImageRenderer(size: totalSize)
return renderer.image { (context) in
for (index, image) in self.enumerated() {
let rect = AVMakeRect(aspectRatio: image.size, insideRect: isVertical ?
CGRect(x: 0, y: maxSize.height * CGFloat(index), width: maxSize.width, height: maxSize.height) :
CGRect(x: maxSize.width * CGFloat(index), y: 0, width: maxSize.width, height: maxSize.height))
image.draw(in: rect)
}
}
}
}
Seriously?
The code you found is EXACTLY what you're looking for, but you don't know how to call it?
If you can't figure out how to call a function it's a bit hard to help you in a way that will be useful. Here's some general code that would load an array of images and stitch them together:
//Create an array of image names
let imageNames = ["Fish", "Dog", "Eggplant", "Wombat"]
//create an array of images with those names (images must exist in app bundle)
let images = imageNames.flatMap{UIImage(named:$0)}
//Stitch the images together horizontally
let stichedImage = stitchImages(images: images, isVertical: false)
You should probably stop and do some reading on Swift. I suggest downloading the Apple Swift book and reading that. It's quite good, and very easy to follow. That's how I learned the language.

How to merge two UIImages?

I am trying to merge two different images and create a new one. This is the way I would like to do:
I have this image (A):
It's a PNG image and I would like to merge this one with another image (B) which I took from the phone to create something like this:
I need a function who merge A with B creating C. The size must remain from the A image and the image B should auto adapt the size to fit into the polaroid (A). Is it possible to do that? Thank for your help!
UPDATE
Just one thing, the image (A) is a square and the image i took is a 16:9, how can i fix that?? If i use your function the image (B) that i took become stretched!
Hope this may help you,
var bottomImage = UIImage(named: "bottom.png")
var topImage = UIImage(named: "top.png")
var size = CGSize(width: 300, height: 300)
UIGraphicsBeginImageContext(size)
let areaSize = CGRect(x: 0, y: 0, width: size.width, height: size.height)
bottomImage!.draw(in: areaSize)
topImage!.draw(in: areaSize, blendMode: .normal, alpha: 0.8)
var newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
All the Best :)
Swift 5: Extension for UIImage
extension UIImage {
func mergeWith(topImage: UIImage) -> UIImage {
let bottomImage = self
UIGraphicsBeginImageContext(size)
let areaSize = CGRect(x: 0, y: 0, width: bottomImage.size.width, height: bottomImage.size.height)
bottomImage.draw(in: areaSize)
topImage.draw(in: areaSize, blendMode: .normal, alpha: 1.0)
let mergedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return mergedImage
}
}
Swift 4 UIImage extension that enables easy image merging / overlaying.
extension UIImage {
func overlayWith(image: UIImage, posX: CGFloat, posY: CGFloat) -> UIImage {
let newWidth = size.width < posX + image.size.width ? posX + image.size.width : size.width
let newHeight = size.height < posY + image.size.height ? posY + image.size.height : size.height
let newSize = CGSize(width: newWidth, height: newHeight)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
draw(in: CGRect(origin: CGPoint.zero, size: size))
image.draw(in: CGRect(origin: CGPoint(x: posX, y: posY), size: image.size))
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
This way the overlay picture will be much cleaner:
class func mergeImages(imageView: UIImageView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(imageView.frame.size, false, 0.0)
imageView.superview!.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Objective C version of this solution with top image re-centered logic :
-(UIImage *)getImageInclosedWithinAnotherImage
{
float innerImageSize = 20;
UIImage *finalImage;
UIImage *outerImage = [UIImage imageNamed:#"OuterImage.png"];
UIImage *innerImage = [UIImage imageNamed:#"InnerImage.png"];
CGSize outerImageSize = CGSizeMake(40, 40); // Provide custom size or size of your actual image
UIGraphicsBeginImageContext(outerImageSize);
//calculate areaSize for re-centered inner image
CGRect areSize = CGRectMake(((outerImageSize.width/2) - (innerImageSize/2)), ((outerImageSize.width/2) - (innerImageSize/2)), innerImageSize, innerImageSize);
[outerImage drawInRect:CGRectMake(0, 0, outerImageSize.width, outerImageSize.height)];
[innerImage drawInRect:areSize blendMode:kCGBlendModeNormal alpha:1.0];
finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
The upvoted answer stretches the background image changing its ratio. The solution below fixes that by rendering the image from a UIView that contains the two image views as subviews.
ANSWER YOU ARE LOOKING FOR (Swift 4):
func blendImages(_ img: UIImage,_ imgTwo: UIImage) -> Data? {
let bottomImage = img
let topImage = imgTwo
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 306, height: 306))
let imgView2 = UIImageView(frame: CGRect(x: 0, y: 0, width: 306, height: 306))
// - Set Content mode to what you desire
imgView.contentMode = .scaleAspectFill
imgView2.contentMode = .scaleAspectFit
// - Set Images
imgView.image = bottomImage
imgView2.image = topImage
// - Create UIView
let contentView = UIView(frame: CGRect(x: 0, y: 0, width: 306, height: 306))
contentView.addSubview(imgView)
contentView.addSubview(imgView2)
// - Set Size
let size = CGSize(width: 306, height: 306)
// - Where the magic happens
UIGraphicsBeginImageContextWithOptions(size, true, 0)
contentView.drawHierarchy(in: contentView.bounds, afterScreenUpdates: true)
guard let i = UIGraphicsGetImageFromCurrentImageContext(),
let data = UIImageJPEGRepresentation(i, 1.0)
else {return nil}
UIGraphicsEndImageContext()
return data
}
The returned image data doubles the size of the image, so set the size of the views at half the desired size.
EXAMPLE: I wanted the width and height of the image to be 612, so I set the view frames width and height to 306)
// Enjoy :)
Slightly modified version of answer by budidino. This implementation also handles negative posX and posY correctly.
extension UIImage {
func overlayWith(image: UIImage, posX: CGFloat, posY: CGFloat) -> UIImage {
let newWidth = posX < 0 ? abs(posX) + max(self.size.width, image.size.width) :
size.width < posX + image.size.width ? posX + image.size.width : size.width
let newHeight = posY < 0 ? abs(posY) + max(size.height, image.size.height) :
size.height < posY + image.size.height ? posY + image.size.height : size.height
let newSize = CGSize(width: newWidth, height: newHeight)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
let originalPoint = CGPoint(x: posX < 0 ? abs(posX) : 0, y: posY < 0 ? abs(posY) : 0)
self.draw(in: CGRect(origin: originalPoint, size: self.size))
let overLayPoint = CGPoint(x: posX < 0 ? 0 : posX, y: posY < 0 ? 0 : posY)
image.draw(in: CGRect(origin: overLayPoint, size: image.size))
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}

How to Resize image in Swift?

I am making an app for iOS, using Swift and Parse.com
I am trying to let the user select a picture from an image picker and then resize the selected image to 200x200 pixels before uploading to my backend.
Parse.com have a tutorial for an Instagram copy app called "AnyPic" which gives this code for resizing images, but it is in Objective-C....
// Resize the image to be square (what is shown in the preview)
UIImage *resizedImage = [anImage resizedImageWithContentMode:UIViewContentModeScaleAspectFit
bounds:CGSizeMake(560.0f, 560.0f)
interpolationQuality:kCGInterpolationHigh];
// Create a thumbnail and add a corner radius for use in table views
UIImage *thumbnailImage = [anImage thumbnailImage:86.0f
transparentBorder:0.0f
cornerRadius:10.0f
interpolationQuality:kCGInterpolationDefault];
How would I create a 200x200px version of the selected picture (to then upload) in Swift?
And, what is the thumbnailImage function doing?
See my blog post, Resize image in swift and objective C, for further details.
Image resize function in swift as below.
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage? {
let size = image.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(origin: .zero, size: newSize)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Use the above function and resize image with 200*200 as below code
self.resizeImage(UIImage(named: "yourImageName")!, targetSize: CGSizeMake(200.0, 200.0))
swift3 updated
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
Details
Xcode 10.2.1 (10E1001), Swift 5
Links
https://gist.github.com/eugenebokhan/5e62a0155754ae6aa6c3c13cf1744930
Image Resizing Techniques
Solution
import UIKit
import CoreGraphics
import Accelerate
extension UIImage {
public enum ResizeFramework {
case uikit, coreImage, coreGraphics, imageIO, accelerate
}
/// Resize image with ScaleAspectFit mode and given size.
///
/// - Parameter dimension: width or length of the image output.
/// - Parameter resizeFramework: Technique for image resizing: UIKit / CoreImage / CoreGraphics / ImageIO / Accelerate.
/// - Returns: Resized image.
func resizeWithScaleAspectFitMode(to dimension: CGFloat, resizeFramework: ResizeFramework = .coreGraphics) -> UIImage? {
if max(size.width, size.height) <= dimension { return self }
var newSize: CGSize!
let aspectRatio = size.width/size.height
if aspectRatio > 1 {
// Landscape image
newSize = CGSize(width: dimension, height: dimension / aspectRatio)
} else {
// Portrait image
newSize = CGSize(width: dimension * aspectRatio, height: dimension)
}
return resize(to: newSize, with: resizeFramework)
}
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Parameter resizeFramework: Technique for image resizing: UIKit / CoreImage / CoreGraphics / ImageIO / Accelerate.
/// - Returns: Resized image.
public func resize(to newSize: CGSize, with resizeFramework: ResizeFramework = .coreGraphics) -> UIImage? {
switch resizeFramework {
case .uikit: return resizeWithUIKit(to: newSize)
case .coreGraphics: return resizeWithCoreGraphics(to: newSize)
case .coreImage: return resizeWithCoreImage(to: newSize)
case .imageIO: return resizeWithImageIO(to: newSize)
case .accelerate: return resizeWithAccelerate(to: newSize)
}
}
// MARK: - UIKit
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
private func resizeWithUIKit(to newSize: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(newSize, true, 1.0)
self.draw(in: CGRect(origin: .zero, size: newSize))
defer { UIGraphicsEndImageContext() }
return UIGraphicsGetImageFromCurrentImageContext()
}
// MARK: - CoreImage
/// Resize CI image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
// https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html
private func resizeWithCoreImage(to newSize: CGSize) -> UIImage? {
guard let cgImage = cgImage, let filter = CIFilter(name: "CILanczosScaleTransform") else { return nil }
let ciImage = CIImage(cgImage: cgImage)
let scale = (Double)(newSize.width) / (Double)(ciImage.extent.size.width)
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(NSNumber(value:scale), forKey: kCIInputScaleKey)
filter.setValue(1.0, forKey: kCIInputAspectRatioKey)
guard let outputImage = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return nil }
let context = CIContext(options: [.useSoftwareRenderer: false])
guard let resultCGImage = context.createCGImage(outputImage, from: outputImage.extent) else { return nil }
return UIImage(cgImage: resultCGImage)
}
// MARK: - CoreGraphics
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
private func resizeWithCoreGraphics(to newSize: CGSize) -> UIImage? {
guard let cgImage = cgImage, let colorSpace = cgImage.colorSpace else { return nil }
let width = Int(newSize.width)
let height = Int(newSize.height)
let bitsPerComponent = cgImage.bitsPerComponent
let bytesPerRow = cgImage.bytesPerRow
let bitmapInfo = cgImage.bitmapInfo
guard let context = CGContext(data: nil, width: width, height: height,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow, space: colorSpace,
bitmapInfo: bitmapInfo.rawValue) else { return nil }
context.interpolationQuality = .high
let rect = CGRect(origin: CGPoint.zero, size: newSize)
context.draw(cgImage, in: rect)
return context.makeImage().flatMap { UIImage(cgImage: $0) }
}
// MARK: - ImageIO
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
private func resizeWithImageIO(to newSize: CGSize) -> UIImage? {
var resultImage = self
guard let data = jpegData(compressionQuality: 1.0) else { return resultImage }
let imageCFData = NSData(data: data) as CFData
let options = [
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceThumbnailMaxPixelSize: max(newSize.width, newSize.height)
] as CFDictionary
guard let source = CGImageSourceCreateWithData(imageCFData, nil),
let imageReference = CGImageSourceCreateThumbnailAtIndex(source, 0, options) else { return resultImage }
resultImage = UIImage(cgImage: imageReference)
return resultImage
}
// MARK: - Accelerate
/// Resize image from given size.
///
/// - Parameter newSize: Size of the image output.
/// - Returns: Resized image.
private func resizeWithAccelerate(to newSize: CGSize) -> UIImage? {
var resultImage = self
guard let cgImage = cgImage, let colorSpace = cgImage.colorSpace else { return nil }
// create a source buffer
var format = vImage_CGImageFormat(bitsPerComponent: numericCast(cgImage.bitsPerComponent),
bitsPerPixel: numericCast(cgImage.bitsPerPixel),
colorSpace: Unmanaged.passUnretained(colorSpace),
bitmapInfo: cgImage.bitmapInfo,
version: 0,
decode: nil,
renderingIntent: .absoluteColorimetric)
var sourceBuffer = vImage_Buffer()
defer {
sourceBuffer.data.deallocate()
}
var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags))
guard error == kvImageNoError else { return resultImage }
// create a destination buffer
let destWidth = Int(newSize.width)
let destHeight = Int(newSize.height)
let bytesPerPixel = cgImage.bitsPerPixel
let destBytesPerRow = destWidth * bytesPerPixel
let destData = UnsafeMutablePointer<UInt8>.allocate(capacity: destHeight * destBytesPerRow)
defer {
destData.deallocate()
}
var destBuffer = vImage_Buffer(data: destData, height: vImagePixelCount(destHeight), width: vImagePixelCount(destWidth), rowBytes: destBytesPerRow)
// scale the image
error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, numericCast(kvImageHighQualityResampling))
guard error == kvImageNoError else { return resultImage }
// create a CGImage from vImage_Buffer
let destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue()
guard error == kvImageNoError else { return resultImage }
// create a UIImage
if let scaledImage = destCGImage.flatMap({ UIImage(cgImage: $0) }) {
resultImage = scaledImage
}
return resultImage
}
}
Usage
Get image size
import UIKit
// https://stackoverflow.com/a/55765409/4488252
extension UIImage {
func getFileSizeInfo(allowedUnits: ByteCountFormatter.Units = .useMB,
countStyle: ByteCountFormatter.CountStyle = .memory,
compressionQuality: CGFloat = 1.0) -> String? {
// https://developer.apple.com/documentation/foundation/bytecountformatter
let formatter = ByteCountFormatter()
formatter.allowedUnits = allowedUnits
formatter.countStyle = countStyle
return getSizeInfo(formatter: formatter, compressionQuality: compressionQuality)
}
func getSizeInfo(formatter: ByteCountFormatter, compressionQuality: CGFloat = 1.0) -> String? {
guard let imageData = jpegData(compressionQuality: compressionQuality) else { return nil }
return formatter.string(fromByteCount: Int64(imageData.count))
}
}
Test function
private func test() {
guard let img = UIImage(named: "img") else { return }
printInfo(of: img, title: "original image |")
let dimension: CGFloat = 2000
var framework: UIImage.ResizeFramework = .accelerate
var startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
framework = .coreGraphics
startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
framework = .coreImage
startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
framework = .imageIO
startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
framework = .uikit
startTime = Date()
if let img = img.resizeWithScaleAspectFitMode(to: dimension, resizeFramework: framework) {
printInfo(of: img, title: "resized image |", with: framework, startedTime: startTime)
}
}
private func printInfo(of image: UIImage, title: String, with resizeFramework: UIImage.ResizeFramework? = nil, startedTime: Date? = nil) {
var description = "\(title) \(image.size)"
if let startedTime = startedTime { description += ", execution time: \(Date().timeIntervalSince(startedTime))" }
if let fileSize = image.getFileSizeInfo(compressionQuality: 0.9) { description += ", size: \(fileSize)" }
if let resizeFramework = resizeFramework { description += ", framework: \(resizeFramework)" }
print(description)
}
Output
original image | (5790.0, 8687.0), size: 17.1 MB
resized image | (1333.0, 2000.0), execution time: 0.8192930221557617, size: 1.1 MB, framework: accelerate
resized image | (1333.0, 2000.0), execution time: 0.44696998596191406, size: 1 MB, framework: coreGraphics
resized image | (1334.0, 2000.0), execution time: 54.172922015190125, size: 1.1 MB, framework: coreImage
resized image | (1333.0, 2000.0), execution time: 1.8765920400619507, size: 1.1 MB, framework: imageIO
resized image | (1334.0, 2000.0), execution time: 0.4638739824295044, size: 1 MB, framework: uikit
For Swift 4.0 and iOS 10
extension UIImage {
func resizeImage(_ dimension: CGFloat, opaque: Bool, contentMode: UIViewContentMode = .scaleAspectFit) -> UIImage {
var width: CGFloat
var height: CGFloat
var newImage: UIImage
let size = self.size
let aspectRatio = size.width/size.height
switch contentMode {
case .scaleAspectFit:
if aspectRatio > 1 { // Landscape image
width = dimension
height = dimension / aspectRatio
} else { // Portrait image
height = dimension
width = dimension * aspectRatio
}
default:
fatalError("UIIMage.resizeToFit(): FATAL: Unimplemented ContentMode")
}
if #available(iOS 10.0, *) {
let renderFormat = UIGraphicsImageRendererFormat.default()
renderFormat.opaque = opaque
let renderer = UIGraphicsImageRenderer(size: CGSize(width: width, height: height), format: renderFormat)
newImage = renderer.image {
(context) in
self.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
}
} else {
UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), opaque, 0)
self.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
}
return newImage
}
}
Since #KiritModi 's answer is from 2015, this is the Swift 3.0's version:
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / image.size.width
let heightRatio = targetSize.height / image.size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
For Swift 5.0 and iOS 12
extension UIImage {
func imageResized(to size: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { _ in
draw(in: CGRect(origin: .zero, size: size))
}
}
}
use:
let image = #imageLiteral(resourceName: "ic_search")
cell!.search.image = image.imageResized(to: cell!.search.frame.size)
For Swift 4 I would just make an extension on UIImage with referencing to self.
import UIKit
extension UIImage {
func resizeImage(targetSize: CGSize) -> UIImage {
let size = self.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
let newSize = widthRatio > heightRatio ? CGSize(width: size.width * heightRatio, height: size.height * heightRatio) : CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
It's also possible to use AlamofireImage (https://github.com/Alamofire/AlamofireImage)
let size = CGSize(width: 30.0, height: 30.0)
let aspectScaledToFitImage = image.af_imageAspectScaled(toFit: size)
The function in the previous post gave me a blurry result.
Swift 3 Version and Extension style
This answer come from #Kirit Modi.
extension UIImage {
func resizeImage(targetSize: CGSize) -> UIImage {
let size = self.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
Updated Swift 5 version that uses the latest UIGraphicsImageRenderer API.
extension UIImage {
public func resized(to target: CGSize) -> UIImage {
let ratio = min(
target.height / size.height, target.width / size.width
)
let new = CGSize(
width: size.width * ratio, height: size.height * ratio
)
let renderer = UIGraphicsImageRenderer(size: new)
return renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: new))
}
}
}
Swift 4, extension version, NO WHITE LINE ON EDGES.
Nobody seems to be mentioning that if image.draw() is called with non-integer values, resulting image could show a white line artifact at the right or bottom edge.
extension UIImage {
func scaled(with scale: CGFloat) -> UIImage? {
// size has to be integer, otherwise it could get white lines
let size = CGSize(width: floor(self.size.width * scale), height: floor(self.size.height * scale))
UIGraphicsBeginImageContext(size)
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
Swift 5 version respecting ratio (scaleToFill) and centering image:
extension UIImage {
func resized(to newSize: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size: newSize).image { _ in
let hScale = newSize.height / size.height
let vScale = newSize.width / size.width
let scale = max(hScale, vScale) // scaleToFill
let resizeSize = CGSize(width: size.width*scale, height: size.height*scale)
var middle = CGPoint.zero
if resizeSize.width > newSize.width {
middle.x -= (resizeSize.width-newSize.width)/2.0
}
if resizeSize.height > newSize.height {
middle.y -= (resizeSize.height-newSize.height)/2.0
}
draw(in: CGRect(origin: middle, size: resizeSize))
}
}
}
Swift 4 Version
extension UIImage {
func resizeImage(_ newSize: CGSize) -> UIImage? {
func isSameSize(_ newSize: CGSize) -> Bool {
return size == newSize
}
func scaleImage(_ newSize: CGSize) -> UIImage? {
func getScaledRect(_ newSize: CGSize) -> CGRect {
let ratio = max(newSize.width / size.width, newSize.height / size.height)
let width = size.width * ratio
let height = size.height * ratio
return CGRect(x: 0, y: 0, width: width, height: height)
}
func _scaleImage(_ scaledRect: CGRect) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(scaledRect.size, false, 0.0);
draw(in: scaledRect)
let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
UIGraphicsEndImageContext()
return image
}
return _scaleImage(getScaledRect(newSize))
}
return isSameSize(newSize) ? self : scaleImage(newSize)!
}
}
UIImage Extension Swift 5
extension UIImage {
func resize(_ width: CGFloat, _ height:CGFloat) -> UIImage? {
let widthRatio = width / size.width
let heightRatio = height / size.height
let ratio = widthRatio > heightRatio ? heightRatio : widthRatio
let newSize = CGSize(width: size.width * ratio, height: size.height * ratio)
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
Use :
UIImage().resize(200, 300)
All of the listed answers so far seem to result in an image of a reduced size, however the size isn't measured in pixels. Here's a Swift 5, pixel-based resize.
extension UIImage {
func resize(_ max_size: CGFloat) -> UIImage {
// adjust for device pixel density
let max_size_pixels = max_size / UIScreen.main.scale
// work out aspect ratio
let aspectRatio = size.width/size.height
// variables for storing calculated data
var width: CGFloat
var height: CGFloat
var newImage: UIImage
if aspectRatio > 1 {
// landscape
width = max_size_pixels
height = max_size_pixels / aspectRatio
} else {
// portrait
height = max_size_pixels
width = max_size_pixels * aspectRatio
}
// create an image renderer of the correct size
let renderer = UIGraphicsImageRenderer(size: CGSize(width: width, height: height), format: UIGraphicsImageRendererFormat.default())
// render the image
newImage = renderer.image {
(context) in
self.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
}
// return the image
return newImage
}
}
Usage:
image.resize(500)
Here's a general method (in Swift 5) for downscaling an image to fit a size. The resulting image can have the same aspect ratio as the original, or it can be the target size with the original image centered in it. If the image is smaller than the target size, it is not resized.
extension UIImage {
func scaledDown(into size:CGSize, centered:Bool = false) -> UIImage {
var (targetWidth, targetHeight) = (self.size.width, self.size.height)
var (scaleW, scaleH) = (1 as CGFloat, 1 as CGFloat)
if targetWidth > size.width {
scaleW = size.width/targetWidth
}
if targetHeight > size.height {
scaleH = size.height/targetHeight
}
let scale = min(scaleW,scaleH)
targetWidth *= scale; targetHeight *= scale
let sz = CGSize(width:targetWidth, height:targetHeight)
if !centered {
return UIGraphicsImageRenderer(size:sz).image { _ in
self.draw(in:CGRect(origin:.zero, size:sz))
}
}
let x = (size.width - targetWidth)/2
let y = (size.height - targetHeight)/2
let origin = CGPoint(x:x,y:y)
return UIGraphicsImageRenderer(size:size).image { _ in
self.draw(in:CGRect(origin:origin, size:sz))
}
}
}
Swift 4 Solution-
Use this function
func image(with image: UIImage, scaledTo newSize: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
drawingImageView.image = newImage
return newImage ?? UIImage()
}
Calling a function:-
image(with: predictionImage, scaledTo: CGSize(width: 28.0, height: 28.0)
here 28.0 is the pixel size that you want to set
Swift 4.2 version of #KiritModi answer
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
} else {
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
}
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
calling of resizeimage method
let image1 = resizeimage(image: myimage.image!, withSize: CGSize(width:200, height: 200))
method for resizeing image
func resizeimage(image:UIImage,withSize:CGSize) -> UIImage {
var actualHeight:CGFloat = image.size.height
var actualWidth:CGFloat = image.size.width
let maxHeight:CGFloat = withSize.height
let maxWidth:CGFloat = withSize.width
var imgRatio:CGFloat = actualWidth/actualHeight
let maxRatio:CGFloat = maxWidth/maxHeight
let compressionQuality = 0.5
if (actualHeight>maxHeight||actualWidth>maxWidth) {
if (imgRatio<maxRatio){
//adjust width according to maxHeight
imgRatio = maxHeight/actualHeight
actualWidth = imgRatio * actualWidth
actualHeight = maxHeight
}else if(imgRatio>maxRatio){
// adjust height according to maxWidth
imgRatio = maxWidth/actualWidth
actualHeight = imgRatio * actualHeight
actualWidth = maxWidth
}else{
actualHeight = maxHeight
actualWidth = maxWidth
}
}
let rec:CGRect = CGRect(x:0.0,y:0.0,width:actualWidth,height:actualHeight)
UIGraphicsBeginImageContext(rec.size)
image.draw(in: rec)
let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
let imageData = UIImageJPEGRepresentation(image, CGFloat(compressionQuality))
UIGraphicsEndImageContext()
let resizedimage = UIImage(data: imageData!)
return resizedimage!
}
Here you have two simple functions of UIImage extension:
func scaledWithMaxWidthOrHeightValue(value: CGFloat) -> UIImage? {
let width = self.size.width
let height = self.size.height
let ratio = width/height
var newWidth = value
var newHeight = value
if ratio > 1 {
newWidth = width * (newHeight/height)
} else {
newHeight = height * (newWidth/width)
}
UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, 0)
draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func scaled(withScale scale: CGFloat) -> UIImage? {
let size = CGSize(width: self.size.width * scale, height: self.size.height * scale)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
SWIFT 5 - XCODE 12 -- RESIZE IMAGE & No White line
I used a wonderful solution above for Swift 5. And I changed one bit to include the term "floor" as I was getting a white line around my resized images. This rounds it to the nearest pixel or something so it looks great! I also had to change the syntax around the image name when the function is called (last line).
//method for resizing image
func resizeimage(image:UIImage,withSize:CGSize) -> UIImage {
var actualHeight:CGFloat = image.size.height
var actualWidth:CGFloat = image.size.width
let maxHeight:CGFloat = withSize.height
let maxWidth:CGFloat = withSize.width
var imgRatio:CGFloat = actualWidth/actualHeight
let maxRatio:CGFloat = maxWidth/maxHeight
let compressionQuality = 0.5
if (actualHeight>maxHeight||actualWidth>maxWidth) {
if (imgRatio<maxRatio){
//adjust width according to maxHeight
imgRatio = maxHeight/actualHeight
actualWidth = floor(imgRatio * actualWidth)
actualHeight = maxHeight
}else if(imgRatio>maxRatio){
// adjust height according to maxWidth
imgRatio = maxWidth/actualWidth
actualHeight = imgRatio * actualHeight
actualWidth = maxWidth
}else{
actualHeight = maxHeight
actualWidth = maxWidth
}
}
let rec:CGRect = CGRect(x:0.0,y:0.0,width:actualWidth,height:actualHeight)
UIGraphicsBeginImageContext(rec.size)
image.draw(in: rec)
let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
let imageData = UIImageJPEGRepresentation(image, CGFloat(compressionQuality))
UIGraphicsEndImageContext()
let resizedimage = UIImage(data: imageData!)
return resizedimage!
}
//calling of resizeimage method:
let myimage = UIImage(named: "imagename")
let image1 = resizeimage(image: myimage!, withSize: CGSize(width:50, height: 50)).withRenderingMode(.alwaysOriginal)
Example is for image minimize to 1024 and less
func resizeImage(image: UIImage) -> UIImage {
if image.size.height >= 1024 && image.size.width >= 1024 {
UIGraphicsBeginImageContext(CGSize(width:1024, height:1024))
image.draw(in: CGRect(x:0, y:0, width:1024, height:1024))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
else if image.size.height >= 1024 && image.size.width < 1024
{
UIGraphicsBeginImageContext(CGSize(width:image.size.width, height:1024))
image.draw(in: CGRect(x:0, y:0, width:image.size.width, height:1024))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
else if image.size.width >= 1024 && image.size.height < 1024
{
UIGraphicsBeginImageContext(CGSize(width:1024, height:image.size.height))
image.draw(in: CGRect(x:0, y:0, width:1024, height:image.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
else
{
return image
}
}
You can use this for fit image at Swift 3;
extension UIImage {
func resizedImage(newSize: CGSize) -> UIImage {
// Guard newSize is different
guard self.size != newSize else { return self }
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
func resizedImageWithinRect(rectSize: CGSize) -> UIImage {
let widthFactor = size.width / rectSize.width
let heightFactor = size.height / rectSize.height
var resizeFactor = widthFactor
if size.height > size.width {
resizeFactor = heightFactor
}
let newSize = CGSize(width: size.width/resizeFactor, height: size.height/resizeFactor)
let resized = resizedImage(newSize: newSize)
return resized
}
}
Usage;
let resizedImage = image.resizedImageWithinRect(rectSize: CGSize(width: 1900, height: 1900))

Cut a UIImage into a circle

I want to cut a UIImage into a circle so that I can then use it as an annotation. Every answer on this site that I've found describes creating an UIImageView, then modifying that and displaying it, but you cant set the image of an annotation to an UIImageView, only a UIImage.
How should I go about this?
Xcode 11 • Swift 5.1 or later
edit/update: For iOS10+ We can use UIGraphicsImageRenderer. For older Swift syntax check edit history.
extension UIImage {
var isPortrait: Bool { size.height > size.width }
var isLandscape: Bool { size.width > size.height }
var breadth: CGFloat { min(size.width, size.height) }
var breadthSize: CGSize { .init(width: breadth, height: breadth) }
var breadthRect: CGRect { .init(origin: .zero, size: breadthSize) }
var circleMasked: UIImage? {
guard let cgImage = cgImage?
.cropping(to: .init(origin: .init(x: isLandscape ? ((size.width-size.height)/2).rounded(.down) : 0,
y: isPortrait ? ((size.height-size.width)/2).rounded(.down) : 0),
size: breadthSize)) else { return nil }
let format = imageRendererFormat
format.opaque = false
return UIGraphicsImageRenderer(size: breadthSize, format: format).image { _ in
UIBezierPath(ovalIn: breadthRect).addClip()
UIImage(cgImage: cgImage, scale: format.scale, orientation: imageOrientation)
.draw(in: .init(origin: .zero, size: breadthSize))
}
}
}
Playground Testing
let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"http://i.stack.imgur.com/Xs4RX.jpg")!))!
profilePicture.circleMasked
Make sure to import QuarzCore if needed.
func maskRoundedImage(image: UIImage, radius: CGFloat) -> UIImage {
let imageView: UIImageView = UIImageView(image: image)
let layer = imageView.layer
layer.masksToBounds = true
layer.cornerRadius = radius
UIGraphicsBeginImageContext(imageView.bounds.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage!
}
UIImage extension:
extension UIImage {
func circularImage(size size: CGSize?) -> UIImage {
let newSize = size ?? self.size
let minEdge = min(newSize.height, newSize.width)
let size = CGSize(width: minEdge, height: minEdge)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
self.drawInRect(CGRect(origin: CGPoint.zero, size: size), blendMode: .Copy, alpha: 1.0)
CGContextSetBlendMode(context, .Copy)
CGContextSetFillColorWithColor(context, UIColor.clearColor().CGColor)
let rectPath = UIBezierPath(rect: CGRect(origin: CGPoint.zero, size: size))
let circlePath = UIBezierPath(ovalInRect: CGRect(origin: CGPoint.zero, size: size))
rectPath.appendPath(circlePath)
rectPath.usesEvenOddFillRule = true
rectPath.fill()
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
}
Usage:
UIImageView:
#IBDesignable class CircularImageView: UIImageView {
override var image: UIImage? {
didSet {
super.image = image?.circularImage(size: nil)
}
}
}
UIButton:
#IBDesignable class CircularImageButton: UIButton {
override func setImage(image: UIImage?, forState state: UIControlState) {
let circularImage = image?.circularImage(size: nil)
super.setImage(circularImage, forState: state)
}
}
Based on Nikos answer:
public extension UIImage {
func roundedImage() -> UIImage {
let imageView: UIImageView = UIImageView(image: self)
let layer = imageView.layer
layer.masksToBounds = true
layer.cornerRadius = imageView.frame.width / 2
UIGraphicsBeginImageContext(imageView.bounds.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage!
}
}
//Usage
let roundedImage = image.roundedImage()
You can use this code to circle Image
extension UIImage {
func circleImage(_ cornerRadius: CGFloat, size: CGSize) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
if let context = UIGraphicsGetCurrentContext() {
var path: UIBezierPath
if size.height == size.width {
if cornerRadius == size.width/2 {
path = UIBezierPath(arcCenter: CGPoint(x: size.width/2, y: size.height/2), radius: cornerRadius, startAngle: 0, endAngle: 2.0*CGFloat(Double.pi), clockwise: true)
}else {
path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
}
}else {
path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
}
context.addPath(path.cgPath)
context.clip()
self.draw(in: rect)
// 从上下文上获取剪裁后的照片
guard let uncompressedImage = UIGraphicsGetImageFromCurrentImageContext() else {
UIGraphicsEndImageContext()
return nil
}
// 关闭上下文
UIGraphicsEndImageContext()
return uncompressedImage
}else {
return nil
}
}}
All these answers were really complex for a straight forward solution. I just replicated my Objective-C code and adjusted for Swift.
self.myImageView?.layer.cornerRadius = (self.myImageView?.frame.size.width)! / 2;
self.myImageView?.clipsToBounds = true
Xcode 8.1, Swift 3.0.1
My code will look like this:
let image = yourImage.resize(CGSize(width: 20, height: 20))?.circled(forRadius: 20)
Add UIImage Extension, then:
func resize(_ size: CGSize) -> UIImage? {
let rect = CGRect(origin: .zero, size: size)
return redraw(in: rect)
}
func redraw(in rect: CGRect) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale)
guard let context = UIGraphicsGetCurrentContext(), let cgImage = cgImage else { return nil }
let rect = CGRect(origin: .zero, size: size)
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: rect.size.height)
context.concatenate(flipVertical)
context.draw(cgImage, in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func circled(forRadius radius: CGFloat) -> UIImage? {
let rediusSize = CGSize(width: radius, height: radius)
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
guard let context = UIGraphicsGetCurrentContext(), let cgImage = cgImage else { return nil }
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: rect.size.height)
context.concatenate(flipVertical)
let bezierPath = UIBezierPath(roundedRect: rect, byRoundingCorners: [.allCorners], cornerRadii: rediusSize)
context.addPath(bezierPath.cgPath)
context.clip()
context.drawPath(using: .fillStroke)
context.draw(cgImage, in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
I managed to answer my own question by finding a use of BezierPath!
if let xyz = UIImage(contentsOfFile: readPath) {
var Rect: CGRect = CGRectMake(0, 0, xyz.size.width, xyz.size.height)
var x = UIBezierPath(roundedRect: Rect, cornerRadius: 200).addClip()
UIGraphicsBeginImageContextWithOptions(xyz.size, false, xyz.scale)
xyz.drawInRect(Rect)
var ImageNew = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
annotation.image = ImageNew
}
Swift 5.3, Xcode 12.2, Handles all imageOrientations
Based on answer Leo Dabus
Thanks, works perfectly! BUT only for images with imageOrientation .up or .down. For images with .right or .left orientation there are distortions in result. And from iPhone/iPad camera for original photos initially we get .right orientation.
Code below takes into account imageOrientation property:
extension UIImage {
func cropToCircle() -> UIImage? {
let isLandscape = size.width > size.height
let isUpOrDownImageOrientation = [0,1,4,5].contains(imageOrientation.rawValue)
let breadth: CGFloat = min(size.width, size.height)
let breadthSize = CGSize(width: breadth, height: breadth)
let breadthRect = CGRect(origin: .zero, size: breadthSize)
let xOriginPoint = CGFloat(isLandscape ?
(isUpOrDownImageOrientation ? ((size.width-size.height)/2).rounded(.down) : 0) :
(isUpOrDownImageOrientation ? 0 : ((size.height-size.width)/2).rounded(.down)))
let yOriginPoint = CGFloat(isLandscape ?
(isUpOrDownImageOrientation ? 0 : ((size.width-size.height)/2).rounded(.down)) :
(isUpOrDownImageOrientation ? ((size.height-size.width)/2).rounded(.down) : 0))
guard let cgImage = cgImage?.cropping(to: CGRect(origin: CGPoint(x: xOriginPoint, y: yOriginPoint),
size: breadthSize)) else { return nil }
let format = imageRendererFormat
format.opaque = false
return UIGraphicsImageRenderer(size: breadthSize, format: format).image {_ in
UIBezierPath(ovalIn: breadthRect).addClip()
UIImage(cgImage: cgImage, scale: format.scale, orientation: imageOrientation).draw(in: CGRect(origin: .zero, size: breadthSize))
}
}
}
swift 3 conform to MVC pattern
create an external file
#IBDesignable
class RoundImage: UIImageView{
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet{
self.layer.cornerRadius = cornerRadius
}
}
// set border width
#IBInspectable var borderWidth: CGFloat = 0 {
didSet{
self.layer.borderWidth = borderWidth
}
}
// set border color
#IBInspectable var borderColor: UIColor = UIColor.clear {
didSet{
self.layer.borderColor = borderColor.cgColor
}
}
override func awakeFromNib() {
self.clipsToBounds = true
}
}// class
call class in the IB on storyboard
set cornerradius as you please (1/2 of width if desire circle)
Done!
I am using RoundedImageView class, the problem facing is that when browse image from gallery the image not show in round or circle.. I simply change the properties of UIImageView/RoundedImageView -> view -> Content Mode -> Aspect Fillsee screenshot
The accepted answer by #Leo Dabus is good but here's a better approach ✅
import UIKit
public extension UIImage {
/// Returns a circle image with diameter, color and optional padding
class func circle(_ color: UIColor, diameter: CGFloat, padding: CGFloat = .zero) -> UIImage {
let rectangle = CGSize(width: diameter + padding * 2, height: diameter + padding * 2)
return UIGraphicsImageRenderer(size: rectangle).image { context in
let rect = CGRect(x: padding, y: padding, width: diameter + padding, height: diameter + padding)
color.setFill()
UIBezierPath(ovalIn: rect).fill()
}
}
}
How to use
let image = UIImage.circle(.black, diameter: 8.0)
Fewer code lines
Ability to add padding
Non-optional result

Resources