I have a few UIImageViews and UILabels that you can drag around the screen in an editor.
The images are png's with a very high resolution (>2400px ) but on screen they might appear smaller.
I would like to produce a png file out of that screen ( which includes labels and images at certain positions), which will hold the actual sizes of these images (>2400px) .
If I just capture the screen programmatically (render), he will produce an image in the size of the screen, and it will not be transparent.
How can I construct the png file by my self by placing images and texts in whatever sizes and positions I want?
This for example :
if let image = UIImage(named: "example.png") {
if let data = UIImagePNGRepresentation(image) {
let filename = getDocumentsDirectory().appendingPathComponent("copy.png")
try? data.write(to: filename)
}
}
will not let you specify locations or add multiple images and texts to the file
Found a pretty simple and sweet way to do so, first you save in a dictionary all your screen elements, then you can simply add them to a file with :
let font = UIFont.boldSystemFont(ofSize: 160)
let showText:NSString = "hello world"
// setting attr: font name, color...etc.
let attr = [NSFontAttributeName: font, NSForegroundColorAttributeName:UIColor.blue]
// getting size
let sizeOfText = showText.size(attributes: attr)
let image = UIImage(named: "image.png")
let rect = CGRect(x: 0, y: 0, width: image!.size.width,height: image!.size.height)
UIGraphicsBeginImageContextWithOptions(CGSize(width: rect.size.width, height: rect.size.height), false, 0)
// drawing our image to the graphics context
image?.draw(in: rect)
// drawing text
showText.draw(in: CGRect(x: rect.size.width/2.0, y: rect.size.height/2.0, width: rect.size.width, height: rect.size.height), withAttributes: attr)
// getting an image from it
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
Related
I'm building an app that contains iOS 14 widgets. User can change widget content by pressing on Edit Widget, and selecting it from provided intent collection. For INObjectCollection I'm using an array of INObject and for each object I'm setting an image this way:
let image = INImage(url: imageRemoteURL, width: 80, height: 80)
let myObject = MyCustomObject(identifier: "an id", display: "some text", subtitle: nil, image: image)
// MyCustomObject is a subclass of INObject
In the list images are displayed properly, but after selecting an item and opening again the view by pressing on Edit Widget - image is shown all system blue, also big sized. See attached screenshot:
I could find only this topic on the issue but with no solution yet.
If I would use an image from app bundle, then a solution would be to set Original for Render As, but no idea how to fix this when using a remote image URL.
Another problem is image size, which is too large.
Any help would be appreciated.
Thanks to Edward's hints I managed to fix my issues this way:
func getIntentImage(imageLink: String) -> INImage?
{
guard let url = URL(string: imageLink),
let imageData = try? Data(contentsOf: url), // may use image local caching
var image = UIImage(data: imageData) else
{
return nil
}
let maxSize: CGFloat = 80
let size = CGSize(width: maxSize, height: maxSize)
if let resizedImage = image.resizeImage(targetSize: size)
{
image = resizedImage
}
return INImage(uiImage: image.withRenderingMode(.alwaysOriginal))
}
// Image resizer:
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)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
1. Image Size
I had the same issue with INImage's size in intent and found out this https://developer.apple.com/documentation/sirikit/inimage/1648800-imagesize but couldn't find anything about how to use it properly so eventually decided on removing the intent image at all. Maybe it helps you!
2. Blue Tint
You can change the image's default rendering type in your asset catalog or programmatically, and this should remove tint color.
- Asset Catalog
Click on the image you want to change in your asset catalog, then go to the Attributes Inspector, and change the "Render As" attribute to "Original Image".
– Programmatically
INImage itself actually has a way to set rendering mode, but it is set as private and using this API could get your application rejected by Apple. So, if you're planning to release it on App Store keep this in mind.
The "legal" solution is to use INImage(uiImage:) and set the rendering mode on the UIImage.
var renderedImage: UIImage? = UIImage(named:"myImage")?.withRenderingMode(.alwaysOriginal)
let image = INImage(uiImage: renderedImage, width: 40, height: 40)
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
}
I try to create live hair color mask IOS app. I used UIGraphicsGetImageFromCurrentImageContext() function to merge the mask and the background from video. It works but the image quality is not good enough. I wanted to change my code to use blendKernel.apply(foreground: maskCI!, background: backgroundCI!) function. In that case the size and position of the mask and the background image do not match. Mask image is shown on the right bottom corner of the background image. My background image size is 1080x1080 and mask image is 224x224. Could you please advice me how to overlap the mask and the background? Please see my code below:
class IntegrateViewAndMask {
var blendKernel: CIBlendKernel { return .softLight }
func mergeMaskAndBackground(mask: UIImage, background: CVPixelBuffer, size: Int) -> UIImage? {
// Merge two images
// let sizeImage = CGSize(width: size, height: size)
// UIGraphicsBeginImageContext(sizeImage)
// let areaSize = CGRect(x: 0, y: 0, width: sizeImage.width, height: sizeImage.height)
// background
var background = UIImage(pixelBuffer: background)
// crop image to square 1080 x 1080
background = cropImageToSquare(image:background!)
// background?.draw(in: areaSize)
// mask
// mask.draw(in: areaSize)
let backgroundCI = CIImage(image: background!)
let maskCI = CIImage(image: mask)
let trialImage = blendKernel.apply(foreground: maskCI!, background: backgroundCI!)
let trialImageUI = UIImage(ciImage:trialImage!)
// let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
// UIGraphicsEndImageContext()
return trialImageUI
I have just found an answer and tried it. Please see below link. As suggested, I resized the UIimage to bounding square and then converted to CIImage. So it works perfectly now.
CIImage extent in pixels or points?
I have a UIImageView in created in Inspector that I resize in my code based on a selected image which i get from the web. However on first load of the image, it's being displayed in the images normal resolution instead of the UIImageViews newly created bounds.
Resizing the UIImageView:
fullScreenImage.bounds.size = CGSize(width: scaledWidth, height: scaledHeight)
Setting the UIImageView's image
let imageStringURL = images[indexPath.row].urls!["regular"]
let imageURL = URL(string: imageStringURL!)!
let imageData = try! Data(contentsOf: imageURL)
let image = UIImage(data: imageData)
fullScreenImage.image = image
This is how it looks when the image is first clicked on to enter "fullscreen mode"
This is how it looks the second time i click it
Not really sure why the Image isn't bounding itself within the specified UIImageView bounds
Instead of resizing the bound, you can set the UIViewContentMode property of UIImageView. This will resize the imageView image to fit inside the bounds.
fullScreenImage.contentMode = .scaleAspectFit
I found a workaround solution. I tried setting the contentMode to aspect fit, and i also tried enabling clip to bounds in the inspector, however none of them worked. So I simply looked into just resizing the UIImage itself and placing it into the UIImageView.
I found an extension in another post for a UIImage that resizes it
extension UIImage{
func resizeImage(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
}
}
If anyone knows of a better way please let me know :)
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
}
}