Swift - Adding text to a resizable image - ios

I've been fiddling around with different techniques on how to implement a resizable image in the Asset Catalog, but there are no examples out there on how to add text to these resizable images in Swift (even in Apple's own guides) and allow them to resize dynamically.
If any knows how to do that, knows links to blogs which explain that, that will be helpful.

UIImage class scales an image to the dimensions you set it at via constraints or frame depending on how you're laying your stuff out. You can load an image from the assets catalog via the name, as you know.
So just upload the images to the asset catalog in all resolutions for the device classes (e.g. 1x, 2x, 3x...) and then create a UIImage() and set the frame of the UIImage instance after it is a subview, and there you go. iOS will select the right image size based on the device/screen you have and that's about as well as you can do to get a good resolution for a scaled image.
By default, you'll be using constraints to size the UIImage in Interface Builder. If you create the image programmatically you'll have more flexibility but more work as to how you size the UIImage after you place it in a superview.
The following is some code to scale a UIImage. Once you have a bitmap context of the image as shown in the function below, you can use other drawing and font functions with the derived context handle to add text. As you search core image and core text classes you'll notice conversion options and ways manipulate and add text and do all kinds of image manipulation.
func scaleImage(image: UIImage, newSize: CGSize) -> UIImage {
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue)
var colorSpace = CGColorSpaceCreateDeviceRGB()
var bytesPerRow = CGImageGetBytesPerRow(image.CGImage)
var ctx = CGBitmapContextCreate(nil,
UInt(newSize.width),
UInt(newSize.height),
CGImageGetBitsPerComponent(image.CGImage),
UInt(newSize.width * 4),
colorSpace,
bitmapInfo)!
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh)
CGContextDrawImage(ctx, CGRect(origin: CGPointZero, size: CGSizeMake(newSize.width, newSize.height)), image.CGImage)
return UIImage(CGImage: CGBitmapContextCreateImage(ctx))!
}
You could also convert the label to an image by programmatically taking a snapshot as shown here:
func takeSnapshot(view: UIView) -> UIImageView {
var image : UIImage
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0.0)
view.layer.renderInContext(UIGraphicsGetCurrentContext())
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
var imageView = UIImageView(image: image)
imageView.opaque = false
return imageView
}
And overlay one image on the other like this:
func overlayImages(images: [UIImage]) -> UIImage? {
var compositeImage : 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 size = CGSizeMake(maxWidth, maxHeight)
UIGraphicsBeginImageContext(size);
for image in images {
var x = maxWidth / 2 - image.size.width / 2
var y = maxHeight / 2 - image.size.height / 2
image.drawInRect(CGRectMake(x, y, image.size.width, image.size.height))
}
compositeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return compositeImage;
}
You could also subclass your UIImageView() and override drawRect() method rect to add some text as shown, but don't forget to call super() to make sure the image is drawn as well.
var stringAttrs = [UITextAttributeFont : font,
UITextAttributeTextColor : textColor ]
var attrStr = NSAttributedString(string:"hello", attributes:stringAttrs)
attrStr.drawAtPoint:CGPointMake(10.f, 10.f)

Related

Quality get reduced when convert imageview to image

In photo editor screen , I have imageview and it has background image and on top of imageview I add elements like text (label), stickers(images) etc. , Now for the final image containing all elements added on imageview , I am getting image from below code
clipRect is rect for background image inside imageview, image is aspectfit in imageview
Below is code inside UIView extension which has function to generate image out of view.
self == uiview
let op_format = UIGraphicsImageRendererFormat()
op_format.scale = 1.0
let renderer = UIGraphicsImageRenderer(bounds: CGRect(origin: clipRect!.origin, size: outputSize), format: op_format)
let originalBound = self.bounds
self.bounds = CGRect(origin: clipRect!.origin, size: clipRect!.size)
var finalImage = renderer.image { ctx in
self.drawHierarchy(in: CGRect(origin: self.bounds.origin, size: outputSize), afterScreenUpdates: true)
}
self.bounds = CGRect(origin: originalBound.origin, size: originalBound.size)
Issue here is quality of final image quality is very poor as compared to original background image.
Don't set the scale of your UIGraphicsImageRendererFormat to 1. That forces the output image to #1x (non-retina). For most (all?) iOS devices, that will cause a 2X or 3X loss of resolution. Leave the scale value of the UIGraphicsImageRendererFormat at the default value.
you can take screenshot as well
// Convert a uiview to uiimage
func captureView() -> UIImage {
// Gradually increase the number for high resolution.
let scale = 1.0
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, scale)
layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}

crop a specific area of a image in swift

I am creating a app where you can crop multiple images to one specific size.
I have a array with multiple images. The images of the array were displayed on a view, where I can drag them inside the view. I am using the same image twice. It look like this:
I also have a crop view (displays red only for demonstration). The images should be crop to this size of the crop view:
The end result look like this:
There were a few problems.
I don't understand why the image is rotated. It also seems the image is not cropped to the crop view that I created (the red view). Also the images should have a slight delay, because I drag each of them to a other place in the view.
The method that I am using is from apples documentation:
let cropRect = CGRect(x: cropView.frame.origin.x, y: cropView.frame.origin.y, width: cropView.frame.width, height: cropView.frame.height)
let croppedImage = ImageCrophandler.sharedInstance.cropImage(imageContentView[i].image!, toRect: cropRect, viewWidth: cropView.frame.width, viewHeight: cropView.frame.height)
print(croppedImage)
arrayOfCropedImages.append(croppedImage!)
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
}

Resize Image without distorting or cropping it

Users upload an image of any size and we need to resize it so it becomes a square without distorting or cropping the image. Basically, it should do something similar to the "Aspect Fit" content mode in an image view. So if we have a 200x100px png image, I want to make it 200x200px and have the extra 100px in the height be transparent space. It should not crop the image to 200x200.
I tried to use this image processor but it does not do what I want. https://github.com/gavinbunney/Toucan. It only crops the image.
How would I do this in swift and is there a framework that is better than the one I mentioned above to make doing this easier. Basically, I am looking for the simplest way to do this.
Posting this as an answer, along with example usage...
The scaling code is not mine, it's from: https://gist.github.com/tomasbasham/10533743#gistcomment-1988471
Here is code you can run in a playground to test:
import UIKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 800, height: 800))
container.backgroundColor = UIColor.blue
PlaygroundPage.current.liveView = container
// MARK: - Image Scaling.
extension UIImage {
/// Represents a scaling mode
enum ScalingMode {
case aspectFill
case aspectFit
/// Calculates the aspect ratio between two sizes
///
/// - parameters:
/// - size: the first size used to calculate the ratio
/// - otherSize: the second size used to calculate the ratio
///
/// - return: the aspect ratio between the two sizes
func aspectRatio(between size: CGSize, and otherSize: CGSize) -> CGFloat {
let aspectWidth = size.width/otherSize.width
let aspectHeight = size.height/otherSize.height
switch self {
case .aspectFill:
return max(aspectWidth, aspectHeight)
case .aspectFit:
return min(aspectWidth, aspectHeight)
}
}
}
/// Scales an image to fit within a bounds with a size governed by the passed size. Also keeps the aspect ratio.
///
/// - parameter:
/// - newSize: the size of the bounds the image must fit within.
/// - scalingMode: the desired scaling mode
///
/// - returns: a new scaled image.
func scaled(to newSize: CGSize, scalingMode: UIImage.ScalingMode = .aspectFill) -> UIImage {
let aspectRatio = scalingMode.aspectRatio(between: newSize, and: size)
/* Build the rectangle representing the area to be drawn */
var scaledImageRect = CGRect.zero
scaledImageRect.size.width = size.width * aspectRatio
scaledImageRect.size.height = size.height * aspectRatio
scaledImageRect.origin.x = (newSize.width - size.width * aspectRatio) / 2.0
scaledImageRect.origin.y = (newSize.height - size.height * aspectRatio) / 2.0
/* Draw and retrieve the scaled image */
UIGraphicsBeginImageContext(newSize)
draw(in: scaledImageRect)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage!
}
}
if let srcimg = UIImage(named: "flags") {
let w = srcimg.size.width
let h = srcimg.size.height
// determine whether width or height is greater
let longer = max(w, h)
// create a Square size
let sz = CGSize(width: longer, height: longer)
// call scaling function to scale the image to the Square dimensions,
// using "aspect fit"
let newImage = srcimg.scaled(to: sz, scalingMode: .aspectFit)
// create a UIImageView with the resulting image
let v = UIImageView(image: newImage)
v.backgroundColor = UIColor.white
// add it to the container view
container.addSubview(v)
}

Resizing UIImage to fit table cell ImageView

I have images of the size 176 points by 176 points. I am trying to resize them so that they fit into a tableViewCell's ImageView. There are a few problems that I am coming across.
Problems
I don't know what size the image view in the tableViewCell actually is.
If I simply add the image without resizing it, it is so sharp that it looses detail:
If I use this extension on UIImage (below), then the transparent parts of the UIImage turn to black, not what I want:
extension UIImage {
func resizeImageWithBounds(bounds: CGSize) -> UIImage {
let horizontalRatio = bounds.width/size.width
let verticalRatio = bounds.height/size.height
let ratio = max(horizontalRatio, verticalRatio)
let newSize = CGSize(width: size.width * ratio, height: size.height * ratio)
UIGraphicsBeginImageContextWithOptions(newSize, true, 0)
draw(in: CGRect(origin: CGPoint.zero, size: newSize))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
I am looking for information on how big the UIImageView is and how to best resize an image into it. I really don't want to create another set of assets (I have a lot of images), and I don't think I should have to.
Try changing the contentMode of the cell.
cell.imageView?.contentMode = .scaleAspectFit
Or, if that doesn't work, here's how to fix the issues of your resized images turning black:
UIGraphicsBeginImageContextWithOptions(newSize, false, 0) // <-- changed opaque setting from true to false

Pasteboard UIImage not using scale

I am building a custom keyboard and am having trouble adding an image to the pasteboard and maintaining the appropriate scale and resolution with in the pasted image. Let me start with a screenshot of the keyboard to illustrate my trouble:
So the picture of the face in the top left of the keyboard is just a UIButton with the original photo set to the background. When the button is pressed the image is resized with the following function:
func imageResize(image:UIImage, size:CGSize)-> UIImage {
let scale = UIScreen.mainScreen().scale
UIGraphicsBeginImageContextWithOptions(size, false, scale)
var context = UIGraphicsGetCurrentContext()
CGContextSetInterpolationQuality(context, kCGInterpolationHigh)
image.drawInRect(CGRect(origin: CGPointZero, size: size))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage
}
This function creates a UIImage the same size as the UIButton with the appropriate scale to reflect the device's screen resolution. To verify that the function is correct, I added an UIImageView filled with the scaled image. The scaled image is the image that looks misplaced near the center of the keyboard. I added the UIImageView with this function:
func addImageToBottomRight(image: UIImage) {
var tempImgView = UIImageView(image: image)
self.view.addSubview(tempImgView)
tempImgView.frame.offset(dx: 100.0, dy: 50.0)
}
I have tried a few different methods for adding the image to the pasteboard, but all seem to ignore the scale of the image and display it twice as large as opposed to displaying it at a higher resolution:
let pb = UIPasteboard.generalPasteboard()!
var pngType = UIPasteboardTypeListImage[0] as! String
var jpegType = UIPasteboardTypeListImage[2] as! String
pb.image = image
pb.setData(UIImagePNGRepresentation(image), forPasteboardType: pngType)
pb.setData(UIImageJPEGRepresentation(image, 1.0), forPasteboardType: jpegType)
All three of these methods do not work correctly and produce the same result as illustrated in the screenshot. Does anyone have any suggestions of other methods? To further clarify my goal, I would like the image in the message text box to look identical to both UIImages in the keyboard in terms of size and resolution.
Here are a few properties of the UIImage before and resize in case anyone is curious:
Starting Image Size is (750.0, 750.0)
Size to scale to is: (78.0, 78.0))
Initial Scale: 1.0
Resized Image Size is (78.0, 78.0)
Resized Image Scale: 2.0
I know this is an old post, but thought I share the work around I found for this specific case of copying images and pasting to messaging apps.The thing is, when you send a picture with such apps like iMessages, whatsapp, messenger, etc, the way they display the image is so that it aspect fits to some certain horizontal width (lets say around 260 pts for this demo).
As you can see from the diagram below, if you send 150x150 image #1x resolution in imessage, it will be stretched and displayed in the required 260 width box, making the image grainy.
But if you add an empty margin of width 185 to both the left and right sides of the image, you will end up with an image of size 520x150. Now if you send that sized image in imessage, it will have to fit it in a 260 width box, ending up cramming a 520x150 image in a 260x75 box, in a way giving you a 75x75 image at #2x resolution.
You can add a clear color margin to a UIImage with a code like this
extension UIImage {
func addMargin(withInsets inset: UIEdgeInsets) -> UIImage? {
let finalSize = CGSize(width: size.width + inset.left + inset.right, height: size.height + inset.top + inset.bottom)
let finalRect = CGRect(origin: CGPoint(x: 0, y: 0), size: finalSize)
UIGraphicsBeginImageContextWithOptions(finalSize, false, scale)
UIColor.clear.setFill()
UIGraphicsGetCurrentContext()?.fill(finalRect)
let pictureOrigin = CGPoint(x: inset.left, y: inset.top)
let pictureRect = CGRect(origin: pictureOrigin, size: size)
draw(in: pictureRect)
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
defer { UIGraphicsEndImageContext() }
return finalImage
}
}

Resources