IOS Swift Kingfisher Resize processor result blurry image - ios

I'm developing an app using Kingfisher library to load image from ulr and display in CollectionViewCell. I'm trying to resize the image to fit the Contentview of CollectionViewCell.
I have tried ResizeProcessor and ScaleFactor from the library but the result image seem blur. My implementation is below (function is call in CellForRowAtIndexPath)
let url = photo.flickrImageURL("m")
let size = contentView.frame.size
print("size is \(size.debugDescription)")
let resizeProcessor = ResizingImageProcessor(referenceSize: size, mode: .aspectFit)
self.flickrPhoto.kf.setImage(with: url, options: [.backgroundDecode,.processor(resizeProcessor), .scaleFactor(UIScreen.main.scale),.cacheOriginalImage])
Is there anything that I did wrong? The quality of images seem very blur.

You should set referenceSize to size of imageView instead of contentView. As size of your contentView will be bigger than your imageView
Change your code with below code:
let size = self.flickrPhoto.frame.size
UPDATED
Just found that ResizingImageProcessor works on pixel instead of points so you need to multiply scale into image size like below:
let resizingProcessor = ResizingImageProcessor(referenceSize: CGSize(width: self.flickrPhoto.frame.size.width * UIScreen.main.scale, height: self.flickrPhoto.frame.size.height * UIScreen.main.scale))

Related

How to create an image of specific size from UIView

I'm working on iOS app which should enable users to create Instagram stories photos and export them to Instagram. Basically app like Unfold, Stellar, Chroma Stories... I've prepared UI where user can select from prepared templates and add to them own photos with filters, labels etc.
My question is - what is the best way to export created UIView to bigger Image?
I mean how to get the best quality, sharp pixels of labels etc?
Because the template view with subviews (added photos, labels...) is taking +- half of device's screen. But I need bigger size for exported image.
Currently I use:
func makeImageFromView() -> UIImage {
let format = UIGraphicsImageRendererFormat()
let size = CGSize(width: 1080 / format.scale, height: 1920 / format.scale)
let renderer = UIGraphicsImageRenderer(size: size, format: format)
let image = renderer.image { (ctx) in
templateView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
}
return image
}
The resulted image has size of 1080 x 1920, but labels aren't sharp.
Do I need to scale somehow the photo and font size before making it to an image?
Thanks!
So actually yes, before capturing image I need to scale whole view and it's subviews. Here are my findings (maybe obvious things but it took me a while to realize that – I'll be glad for any improvements)
Rendering image of the same size
When you want to capture UIView as an image, you can simply use this function. Resulted image will have a same size as a view (scaled 2x / 3x depending on actual device)
func makeImageFrom(_ desiredView: MyView) -> UIImage {
let size = CGSize(width: desiredView.bounds.width, height: desiredView.bounds.height)
let renderer = UIGraphicsImageRenderer(size: size)
let image = renderer.image { (ctx) in
desiredView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
}
return image
}
Rendering image of the different size
But what to do, when you want a specific size for your exported image?
So from my use-case I wanted to render image of final size (1080 x 1920), but a view I wanted to capture had a smaller size (in my case 275 x 487). If you do such a rendering without anything, there must be a loss in quality.
If you want to avoid that and preserve sharp labels and other subviews, you need to try to scale the view ideally to the desired size. In my case, make it from 275 x 487 to 1080 x 1920.
func makeImageFrom(_ desiredView: MyView) -> UIImage {
let format = UIGraphicsImageRendererFormat()
// We need to divide desired size with renderer scale, otherwise you get output size larger #2x or #3x
let size = CGSize(width: 1080 / format.scale, height: 1920 / format.scale)
let renderer = UIGraphicsImageRenderer(size: size, format: format)
let image = renderer.image { (ctx) in
// remake constraints or change size of desiredView to 1080 x 1920
// handle it's subviews (update font size etc.)
// ...
desiredView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
// undo the size changes
// ...
}
return image
}
My approach
But because I didn't want to mess with a size of a view displayed to the user, I took a different way and used second view which isn't shown to the user. That means that just before I want to capture image, I prepare "duplicated" view with the same content but bigger size. I don't add it to the view controller's view hierarchy, so it's not visible.
Important note!
You really need to take care of subviews. That means, that you have to increase the font size, update position of moved subviews (for example their center) etc.!
Here is just a few lines to illustrate that:
// 1. Create bigger view
let hdView = MyView()
hdView.frame = CGRect(x: 0, y: 0, width: 1080, height: 1920)
// 2. Load content according to the original view (desiredView)
// set text, images...
// 3. Scale subviews
// Find out what scale we need
let scaleMultiplier: CGFloat = 1080 / desiredView.bounds.width // 1080 / 275 = 3.927 ...
// Scale everything, for examples label's font size
[label1, label2].forEach { $0.font = UIFont.systemFont(ofSize: $0.font.pointSize * scaleMultiplier, weight: .bold) }
// or subview's center
subview.center = subview.center.applying(.init(scaleX: scaleMultiplier, y: scaleMultiplier))
// 4. Render image from hdView
let hdImage = makeImageFrom(hdView)
Difference in quality from real usage – zoomed to the label:

Image size is resized when convert it from data in swift 3

I want to save an image in database. Therefore I convert it to Data. However during these steps the width and height of the image will change. It is increased in size.
// Original Image Size
print("Original Image Size : \(capturedImage.size)") // Displays (320.0, 427.0)
// Convert to Data
var imageData: Data?
imageData = UIImagePNGRepresentation(capturedImage)
// Store imageData into Db.
// Convert it back
m_CarImgVw.image = UIImage(data: damageImage!.imageData!, scale: 1.0)
print("m_CarImgVw Image Size : \(m_CarImgVw.image.size)") // Displays (640.0, 854.0)
I do not want the imagesize to increase!
If it’s originally an image from your assets, it’s probably #2x, which means the size in pixels (real size) is double the size in pts (displayed size). So the image size isn’t actually increasing, it was 640x854 before and after the transform. It’s just that before the OS automatically scaled it because it was named #2x.
To use the original image scale you can replace 1.0 with capturedImage.scale.
Your problem is in this line:
m_CarImgVw.image = UIImage(data: damageImage!.imageData!, scale: 1.0)
Can you see it?
Hint: It's in scale: 1.0.
It looks like your original image was Retina (or #2x), so it had scale 2.0.
So you should either put your original image scale (damageImage.scale) there, or if you're presenting image on the screen you should use UIScreen's scale.

iOS: I get many `UIImage` from network, how can I know their `aspect ratio`?

How can I get the aspect ratio for the image which comes from the server?
EDIT:If I get many image in the network, how can I get them by using async function ? and in the block to get their aspect ratio?
Once you download the image, create an UIImage instance by using the downloaded data then through size property, you will be able to determine the height and width of the actual image downloaded :
let image = UIImage(data: <imageDownloadeddata>)
let imgWidth = image.size.width
let imgHeight = image.size.height

Make a large image fit into a imageview of small size

I am trying to make a large image fit into an UIImageView of small size. How can I reduce the size of the image or scale it appropriately so that the full image fits into the UIImageView?
try to use different modes like scale to fill , aspect fit , aspect fill` etc.
i think accorging to your requirement scale to fill is good.
aspect fit will keep the original shape your image and fit accorging to aspect ratio so it will possible that some portion of your imageview remains blank or not cover by image.
Scale to fit try to scale image according to imageview. this is by default mode.
aspect fill will fill whole imageview with aspect ratio.
so choose whatever is best for you. you can easily change mode from storyboard from attribute inspector.
hope this will help :)
set the mode of the imageView to Aspect Fit
Set the this property for your image view
imageView.contentMode = UIViewContentModeScaleAspectFit;
Set the desired frame of your imageView and set the content mode of the image View. For example :
imgView.contentMode = UIViewContentMode.ScaleAspectFit
//OR
imgView.contentMode = UIViewContentMode.ScaleAspectFill
//OR
imgView.contentMode = UIViewContentMode.ScaleToFill
Use whichever works for you.
By looking at the JSON I'm updating the answer:
As I had suspected, its a URL which you are getting in JSON. So do the following:
Parse the JSON and get the URL from the JSON.(videothumbnail key value)
The URL contains back slashes as escape charecters(http://content.m-tutor.com/images/20150916113347_5-cont.png) remove them to get the URL as this http://content.m-tutor.com/images/20150916113347_5-cont.png
Download the image Using below code :
func startDownloadImageWithURL(urlInString: String?){
let session = NSURLSession.sharedSession();
if let urlInString = urlInString{
let urlToDownload = NSURL(string: urlInString)
session.dataTaskWithURL(urlToDownload!, completionHandler: {(data : NSData?,response : NSURLResponse?, error : NSError?) -> Void in
dispatch_async(dispatch_get_main_queue(), {
if let error = error {
print("Error while downloading \(error.localizedDescription)")
}
else{
//Assuming the yourImageView is already initialized with position
//yourImageView.image = UIImage(data: data)
}
})
}).resume()
}
}
I have given just an example on how to download image(get the data), create the image using data downloaded and the set it to imageView, you can update it according to your need.
Hope it helps :]

Getting size of an image in an UIImageView

I am having trouble getting the size of an image after it has been assigned to an UIImageView programmatically.
The code below runs in a loop and a function is used (getNoteImage) to download an image from an URL (photoURL) and assign it to the created UIImageView. I need to get the height of the image so that I can calculate the spacing for the following images and labels.
var myImage :UIImageView
myImage = UIImageView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height))
myImage.center = CGPointMake(UIScreen.mainScreen().bounds.size.width/2, imageY)
myImage.getNoteImage(photoUrl)
self.detailsView.addSubview(myImage)
myImage.contentMode = UIViewContentMode.ScaleAspectFit
imageHeight = myImage.bounds.size.height
I have tried using
imageHeight = myImage.bounds.size.height
which I read as a solution on another post but his just returns the screen size for the device (667 on the iPhone6 simulator).
Can anyone guide me on how I can get the correct image size ?
Thanks
As your imageView is taking up the whole screen and the image is sized using 'aspect fit' to get the height the image is displayed at you will need to get the original image size using myImage.image!.size then scale this based on myImage.bounds.size something like;
let imageViewHeight = myImage.bounds.height
let imageViewWidth = myImage.bounds.width
let imageSize = myImage.image!.size
let scaledImageHeight = min(imageSize.height * (imageViewWidth / imageSize.width), imageViewHeight)
That should give the actual height of the image, note that image.size gives the "logical dimensions of the image" i.e. its natural size and not the size it is drawn at.
As UIImage official doc:
if let image = myImage.image {
let size = image.size
print("image size is \(size)")
} else {
print("There is no image here..")
}
I suppose your code working with synchronously image as I understand in your question, but if not I recommended to use AlamofireImage.
With AlamofireImage you can do:
self.myImage.af_setImageWithURL(
NSURL(string: photoUrl)!,
placeholderImage: nil,
filter: nil,
imageTransition: .CrossDissolve(0.5),
completion: { response in
print(response.result.value) # UIImage
if let image = response.result.value {
let size = image.size
print("image size is \(size)")
}
print(response.result.error) # NSError
}
)
change your code like this and try again. only you did note force set the frame of the UIImageView and then give it a image ,you can use "imageHeight = myImage.bounds.size.height" get the real size of the image.
var myImage :UIImageView
//remove your initial frame parameters
myImage = UIImageView()
myImage.center = CGPointMake(UIScreen.mainScreen().bounds.size.width/2, imageY)
try using myImage.image.size.height and also make sure the image is downloaded from the url you should write it in a completion block and only check if the image is downloaded.
See the documentation for more details.

Resources