I have started working with swift recently. I have a function to resize the image in one swift file and I wanted to call it in another swift file. Here is the function which was added in class UIImage:
public extension UIImage{
class func scalePreservingAspectRatio(targetSize: CGSize) -> UIImage {
let uimageview = UIImage()
let widthRatio = targetSize.width / uimageview.size.width
let heightRatio = targetSize.height / uimageview.size.height
let scaleFactor = min(widthRatio, heightRatio)
let scaledImageSize = CGSize(
width: uimageview.size.width * scaleFactor,
height: uimageview.size.height * scaleFactor
)
let renderer = UIGraphicsImageRenderer(
size: scaledImageSize
)
let scaledImage = renderer.image { _ in
uimageview.draw(in: CGRect(
origin: .zero,
size: scaledImageSize
))
}
return scaledImage
}
}
so when I want to call this function in another file to resize "myimage", I think I should write like this:
let image = UIImage(named: "myimage")
let targetSize = CGSize(width: 100, height: 100)
let scaledImage = image.UIImage.scalePreservingAspectRatio(targetSize: targetSize)
but by adding 'image' before UIImage.scalePreservingAspectRatio(), it does not recognize the function and it shows an error: Value of type 'UIImage?' has no member 'UIImage'. So I don't know how I can use my image for this function in another file.
If you want to call it on UIImage, it needs to be put into an extension:
extension UIImage {
func scalePreservingAspectRatio(targetSize: CGSize) -> UIImage {
...
}
}
You also probably want it to be able to access the image that you call it on, so remove class from the function declaration. Then you can call it like:
image.scalePreservingAspectRatio(targetSize: size)
Then update the code to use self instead of creating a new source image:
func scalePreservingAspectRatio(targetSize: CGSize) -> UIImage {
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
let scaleFactor = min(widthRatio, heightRatio)
let scaledImageSize = CGSize(
width: size.width * scaleFactor,
height: size.height * scaleFactor
)
let renderer = UIGraphicsImageRenderer(
size: scaledImageSize
)
let scaledImage = renderer.image { _ in
self.draw(in: CGRect(
origin: .zero,
size: scaledImageSize
))
}
return scaledImage
}
Related
I tried to look up the answer on the Internet and did not find it, maybe I typed my query wrong on the Internet.
Can you tell me how to crop a 3:4 photo? I can't find the function to crop it. Please share?
You can use this function to crop your image in any AspectRatio (e.g 3:4). You have to pass your Image and desired AspectRatio as a parameter to the function and it will return you the Cropped Image.
func crop(image: UIImage, to aspectRatio: CGFloat) -> UIImage {
let originalAspectRatio = image.size.height/image.size.width
var newImagesize = image.size
if originalAspectRatio > aspectRatio {
newImagesize.height = image.size.width * aspectRatio
} else if originalAspectRatio < aspectRatio {
newImagesize.width = image.size.height / aspectRatio
} else {
return image
}
let center = CGPoint(x: image.size.width/2, y: image.size.height/2)
let origin = CGPoint(x: center.x - newImagesize.width/2, y: center.y - newImagesize.height/2)
let cgCroppedImage = image.cgImage!.cropping(to: CGRect(origin: origin, size: CGSize(width: newImagesize.width, height: newImagesize.height)))!
let croppedImage = UIImage(cgImage: cgCroppedImage, scale: image.scale, orientation: image.imageOrientation)
return croppedImage
}
Usage:
let croppedImage = crop(image: "ImageName", to: 3/4)
Use the following function. For more details read this.
func cropImage(_ inputImage: UIImage, toRect cropRect: CGRect, viewWidth: CGFloat, viewHeight: CGFloat) -> UIImage?
{
let imageViewScale = max(inputImage.size.width / viewWidth,
inputImage.size.height / viewHeight)
// Scale cropRect to handle images larger than shown-on-screen size
let cropZone = CGRect(x:cropRect.origin.x * imageViewScale,
y:cropRect.origin.y * imageViewScale,
width:cropRect.size.width * imageViewScale,
height:cropRect.size.height * imageViewScale)
// Perform cropping in Core Graphics
guard let cutImageRef: CGImage = inputImage.cgImage?.cropping(to:cropZone)
else {
return nil
}
// Return image to UIImage
let croppedImage: UIImage = UIImage(cgImage: cutImageRef)
return croppedImage
}
I need to add some transparent padding around an NSImage.I referred this SO post Placing an NSImage on a transparent canvas in Swift
It was pointing to the the Github code https://gist.github.com/MaciejGad/11d8469b218817290ee77012edb46608 where it mentions of tweaking the resizeWhileMaintainingAspectRatioToSize method. I tried what the post has suggested but swift seems to miss the NSImage.copy method with the NSrect as parameter.So wrote the below extension method with my limited knowledge
func addPadding() -> NSImage? {
let newSize: NSSize
let widthRatio = size.width / self.size.width
let heightRatio = size.height / self.size.height
if widthRatio > heightRatio {
newSize = NSSize(width: floor(self.size.width * widthRatio), height: floor(self.size.height * widthRatio))
} else {
newSize = NSSize(width: floor(self.size.width * heightRatio), height: floor(self.size.height * heightRatio))
}
let img = NSImage(size: newSize)
let frame = NSRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Set the drawing context and make sure to remove the focus before returning.
img.lockFocus()
defer { img.unlockFocus() }
let representation = self.bestRepresentation(for: frame, context: nil, hints: nil)
// Draw the new image
if representation!.draw(in: frame) {
return img
}
// Return nil in case something went wrong.
return nil
}
But this does nothing to add padding to the image.
You can do it with this code:
extension NSImage {
var pixelSize: NSSize {
if let rep = self.representations.first {
let size = NSSize(
width: rep.pixelsWide,
height: rep.pixelsHigh
)
return size
}
return NSSize(width: 0, height: 0)
}
func withPadding(percent: Int) -> NSImage {
let originalSize = self.pixelSize
let newHeight = originalSize.height - originalSize.height * CGFloat(percent) / 100
let newWidth = originalSize.width - originalSize.width * CGFloat(percent) / 100
let sizeWithPadding = NSSize(
width: newWidth,
height: newHeight
)
guard let representation = NSBitmapImageRep(
bitmapDataPlanes: nil,
pixelsWide: Int(originalSize.width),
pixelsHigh: Int(originalSize.height),
bitsPerSample: 8,
samplesPerPixel: 4,
hasAlpha: true,
isPlanar: false,
colorSpaceName: .calibratedRGB,
bytesPerRow: 0,
bitsPerPixel: 0
)
else {
return self
}
representation.size = originalSize
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = NSGraphicsContext.init(bitmapImageRep: representation)
let xPoint = (originalSize.width - newWidth) / 2
let yPoint = (originalSize.height - newHeight) / 2
self.draw(
in: NSRect(
x: xPoint,
y: yPoint,
width: sizeWithPadding.width,
height: sizeWithPadding.height
),
from: .zero,
operation: .copy,
fraction: 1.0
)
NSGraphicsContext.restoreGraphicsState()
let newImage = NSImage(size: originalSize)
newImage.addRepresentation(representation)
return newImage
}
}
I have large images uploaded by users in Swift and I need to resize them all to 100x100px to create thumbnails to store in my server. So far I have found that this resizes an image given a CGSize:
func resizedImage(image: UIImage, size: CGSize) -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { (context) in
image.draw(in: CGRect(origin: .zero, size: size))
}
}
Is there any way to create a CGSize knowing that my target size is strictly 100x100px?
Got this to work:
extension UIImage {
func resizedImage(pixelSize: (width: Int, height: Int)) -> UIImage? {
var size = CGSize(width: CGFloat(pixelSize.width) / UIScreen.main.scale, height: CGFloat(pixelSize.height) / UIScreen.main.scale)
let rect = AVMakeRect(aspectRatio: self.size, insideRect: CGRect(x:0, y:0, width: size.width, height: size.height))
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { (context) in
self.draw(in: rect)
}
}
}
You should initialize your render based on the user device scale and multiply its width and height instead of dividing it:
extension UIImage {
func aspectFitScaled(to size: CGSize) -> UIImage {
let format = imageRendererFormat
format.opaque = false
format.scale = UIScreen.main.scale
let isLandscape = self.size.width > self.size.height
let ratio = isLandscape ? size.width / self.size.width : size.height / self.size.height
let drawSize = self.size.scaled(by: ratio)
let x = (size.width - drawSize.width) / 2
let y = (size.height - drawSize.height) / 2
let origin = CGPoint(x: x, y: y)
return UIGraphicsImageRenderer(size: size, format: format).image { _ in
draw(in: CGRect(origin: origin, size: drawSize))
}
}
}
usage:
class ViewController: UIViewController {
// imageView frame is 200 x 200
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// original image size is (719.0, 808.0)
let image = UIImage(data: try! Data(contentsOf: URL(string: "https://i.stack.imgur.com/Xs4RX.jpg")!))!
imageView.backgroundColor = .gray
let ivImage = image.aspectFitScaled(to: imageView.frame.size)
imageView.image = ivImage
print("ivImage.size", ivImage.size) // (200.0, 200.0)
print("ivImage.scale", ivImage.scale) // screen scale 3.0 iPhone 8 Plus
// lets check the real image dimension
let data = ivImage.jpegData(compressionQuality: 1)!
let savedSize = UIImage(data: data)!.size
print("savedSize", savedSize) // savedSize (600.0, 600.0)
}
}
I'm using the following code to resize an image.But i keep getting a white border at the bottom
func resize(withSize targetSize: NSSize) -> NSImage? {
let size=NSSize(width: floor((targetSize.width/(NSScreen.main?.backingScaleFactor)!)), height:floor( (targetSize.height/(NSScreen.main?.backingScaleFactor)!)))
let frame = NSRect(x: 0, y: 0, width: floor(size.width), height: floor(size.height))
guard let representation = self.bestRepresentation(for: frame, context: nil,hints: nil) else {
return nil
}
let image = NSImage(size: size, flipped: false, drawingHandler: { (_) -> Bool in
return representation.draw(in: frame)
})
return image
}
I have tried using floor function to round off the values.But still the same issue.
extension NSImage {
func resize(withSize targetSize: NSSize) -> NSImage? {
let ratioX = targetSize.width / size.width / (NSScreen.main?.backingScaleFactor ?? 1)
let ratioY = targetSize.height / size.height / (NSScreen.main?.backingScaleFactor ?? 1)
let ratio = ratioX < ratioY ? ratioX : ratioY
let canvasSize = NSSize(width: size.width * ratio, height: size.height * ratio)
let frame = NSRect(origin: .zero, size: canvasSize)
guard let representation = bestRepresentation(for: frame, context: nil, hints: nil)
else { return nil }
return NSImage(size: canvasSize, flipped: false) { _ in representation.draw(in: frame) }
}
}
let image = NSImage(contentsOf: URL(string: "http://i.stack.imgur.com/Xs4RX.jpg")!)!
let resized = image.resize(withSize: .init(width: 400, height: 300)) // w 267 h 300
If you would like to take into account the screen scale just remove the division by (NSScreen.main?.backingScaleFactor ?? 1) from the method when calculating ratioX and ratioY
I have created an image from UIView using UIGraphicsGetCurrentContext. It's work fine but when I resized that image to larger size its be blurred with bad quality. Can it possibility to keep quality when resize? I have tried many ways but not work.
- code image:
func image(with view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
defer { UIGraphicsEndImageContext() }
if let context = UIGraphicsGetCurrentContext() {
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
context.setAllowsAntialiasing(true)
context.setShouldAntialias(true)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
return nil
}
code I have used to resize, this extension of UIImage
func resizedImage(newSize: CGSize) -> UIImage {
// Guard newSize is different
guard self.size != newSize else { return self }
let aspect_ratio = self.size.width / self.size.height
var image_w = newSize.height * aspect_ratio
let finalSize = CGSize(width: image_w, height: newSize.height)
UIGraphicsBeginImageContextWithOptions(finalSize, false, 0.0)
self.draw(in: CGRect(x: 0, y: 0, width: (CGFloat)(finalSize.width), height: (CGFloat)(newSize.height)))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
and the example image:image
Thanks
Try this:
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!
}
}