how to get width and height of image from url without downloading? - ios

I'm using SDWebImage for showing images inside cells. But it is perfect mached to frame of UImageView that I'm doing in code below:
NSString * s =[NSString stringWithFormat:#"url of image to show"];
NSURL *url = [NSURL URLWithString:s];
[cell.shopImageView sd_setImageWithURL:url];
My UIImageView is size 50x50.
For example image from url is size 990x2100 and my image is not displaying well in the frame given.
In this case when hight is bigger, I want to resize my image by proper height ratio to match width 50.
Is there a way to check image's size from url without downloading it and allocating memory in awful way?

You can fetch this data from URL header, in Swift 3.0 use below code
if let imageSource = CGImageSourceCreateWithURL(url! as CFURL, nil) {
if let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary? {
let pixelWidth = imageProperties[kCGImagePropertyPixelWidth] as! Int
let pixelHeight = imageProperties[kCGImagePropertyPixelHeight] as! Int
print("the image width is: \(pixelWidth)")
print("the image height is: \(pixelHeight)")
}
}

Try messing with the different contentMode options to get the look you want. One example could be cell.shopImageView.contentMode = UIViewContentModeScaleAspectFit; That will just make the image fit nicely, but won't actually resize the image view.
Here are your contentMode options: UIViewContentModes
An alternative could be something along the lines of this:
NSData *data = [[NSData alloc]initWithContentsOfURL:URL];
UIImage *image = [[UIImage alloc]initWithData:data];
CGFloat height = image.size.height;
CGFloat width = image.size.width;
You can then set your imageView height/width depending on the ratio of the image's height/width.

I don't know how to get the size of your image from URL without downloading the image.
But I can provide you some code snippet to make your UIImageView frame proportionally after the image is downloaded.
NSData *data = [[NSData alloc]initWithContentsOfURL:URL]; // -- avoid this.
If you use the above method to download image it will block your UI. So please avoid it.
[cell.shopImageView ....]; // -- avoid this method.
As you're using SDWebImage, I guess it will have some dedicated methods to download the image first. So you can use that method to download the image instead of using UIImageView categories method as you used above.
After you downloaded the image. Try something like below.
Code Snippet
Assume the image is downloaded and the object is 'theImage', and 'imageView' as your cell imageview
float imageRatio = theImage.size.width/theImage.size.height;
float widthWithMaxHeight = imageView.frame.size.height * imageRatio;
float finalWidth, finalHeight;
if (widthWithMaxHeight > imageView.frame.size.width) {
finalWidth = imageView.frame.size.width;
finalHeight = imageView.frame.size.width/imageRatio;
} else {
finalHeight = imageView.frame.size.height;
finalWidth = imageView.frame.size.height * imageRatio;
}
[imageView setFrame:CGRectMake(xOffset, yOffset, finalWidth, finalHeight)];
[imageView setImage:theImage];

let ImageArray = ((arrFeedData[indexPath.row] as AnyObject).value(forKey: "social_media_images") as? NSArray)!
var ImageURL: String = ((ImageArray[0] as AnyObject) as? String)!
ImageURL = ImageURL.addingPercentEscapes(using: String.Encoding.ascii)!
let imageUrl = URL(string: ImageURL)
let imageData = try Data(contentsOf: imageUrl!)
let image = UIImage(data: imageData)
print("image height: \(image?.size.height)"
print("image Width: \(image?.size.width)")

Related

Change UIImage render mode at runtime

In an app I am working on I have some UIImageViews that may (or may not) need to be customised. How can I change the rendering mode of an image that was loaded as original to template at runtime?
You can init a UIImage from another cgImage, then you can render it as you need
ExampleCode
let originalImage = UIImage(named: "TimeClock2Filled")?.withRenderingMode(.alwaysOriginal)
if let original = originalImage?.cgImage {
let image2 = UIImage(cgImage: original).withRenderingMode(.alwaysTemplate)
}
Example code Objective-C
UIImage * image = [[UIImage imageNamed:#"TimeClock2Filled"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
if(image.CGImage != nil) {
UIImage * image2 = [[UIImage imageWithCGImage:image.CGImage]imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
this works just fine was tested

get the vertical length of Url Image in Swift 4 [duplicate]

I'm using SDWebImage for showing images inside cells. But it is perfect mached to frame of UImageView that I'm doing in code below:
NSString * s =[NSString stringWithFormat:#"url of image to show"];
NSURL *url = [NSURL URLWithString:s];
[cell.shopImageView sd_setImageWithURL:url];
My UIImageView is size 50x50.
For example image from url is size 990x2100 and my image is not displaying well in the frame given.
In this case when hight is bigger, I want to resize my image by proper height ratio to match width 50.
Is there a way to check image's size from url without downloading it and allocating memory in awful way?
You can fetch this data from URL header, in Swift 3.0 use below code
if let imageSource = CGImageSourceCreateWithURL(url! as CFURL, nil) {
if let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary? {
let pixelWidth = imageProperties[kCGImagePropertyPixelWidth] as! Int
let pixelHeight = imageProperties[kCGImagePropertyPixelHeight] as! Int
print("the image width is: \(pixelWidth)")
print("the image height is: \(pixelHeight)")
}
}
Try messing with the different contentMode options to get the look you want. One example could be cell.shopImageView.contentMode = UIViewContentModeScaleAspectFit; That will just make the image fit nicely, but won't actually resize the image view.
Here are your contentMode options: UIViewContentModes
An alternative could be something along the lines of this:
NSData *data = [[NSData alloc]initWithContentsOfURL:URL];
UIImage *image = [[UIImage alloc]initWithData:data];
CGFloat height = image.size.height;
CGFloat width = image.size.width;
You can then set your imageView height/width depending on the ratio of the image's height/width.
I don't know how to get the size of your image from URL without downloading the image.
But I can provide you some code snippet to make your UIImageView frame proportionally after the image is downloaded.
NSData *data = [[NSData alloc]initWithContentsOfURL:URL]; // -- avoid this.
If you use the above method to download image it will block your UI. So please avoid it.
[cell.shopImageView ....]; // -- avoid this method.
As you're using SDWebImage, I guess it will have some dedicated methods to download the image first. So you can use that method to download the image instead of using UIImageView categories method as you used above.
After you downloaded the image. Try something like below.
Code Snippet
Assume the image is downloaded and the object is 'theImage', and 'imageView' as your cell imageview
float imageRatio = theImage.size.width/theImage.size.height;
float widthWithMaxHeight = imageView.frame.size.height * imageRatio;
float finalWidth, finalHeight;
if (widthWithMaxHeight > imageView.frame.size.width) {
finalWidth = imageView.frame.size.width;
finalHeight = imageView.frame.size.width/imageRatio;
} else {
finalHeight = imageView.frame.size.height;
finalWidth = imageView.frame.size.height * imageRatio;
}
[imageView setFrame:CGRectMake(xOffset, yOffset, finalWidth, finalHeight)];
[imageView setImage:theImage];
let ImageArray = ((arrFeedData[indexPath.row] as AnyObject).value(forKey: "social_media_images") as? NSArray)!
var ImageURL: String = ((ImageArray[0] as AnyObject) as? String)!
ImageURL = ImageURL.addingPercentEscapes(using: String.Encoding.ascii)!
let imageUrl = URL(string: ImageURL)
let imageData = try Data(contentsOf: imageUrl!)
let image = UIImage(data: imageData)
print("image height: \(image?.size.height)"
print("image Width: \(image?.size.width)")

UIImageView ignores content mode form image data

I am trying load an image from a data :
NSError *error ;
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:#"png"];
NSData *encryptedData = [NSData dataWithContentsOfFile:imagePath];
NSData *decryptedData = [RNDecryptor decryptData:encryptedData
withPassword:PASSWORD
error:&error];
UIImage *image = [UIImage imageWithData:decryptedData];
//adding image
UIImageView *movingImageView = [[UIImageView alloc]initWithImage:image];
[movingImageView setContentMode:UIViewContentModeScaleAspectFit];
[self.scrollView addSubview:movingImageView];
now movingImageView ignores content mode !!! it will be fixed if I declare its frame ! but the problem is my datas are different images with different width size . Any solution ?
EDIT :
Found a solution :
movingImageView.frame = CGRectMake(0, 0, image.size.width/2, image.size.height/2);
According to the documentation:
initWithImage: adjusts the frame of the receiver to match the size of the specified image. It also disables user interactions for the image view by default.`
Obviously the contentMode will have no effect if the imageView and image have the same size.
I guess what you need is to specify the imageView's width (probably the same as scrollView?), and calculate the height according to the image's dimension.
use the following lines. it will fill the image to frame. if large image came it will show image with aspect ratio and some part may clips. try once.
movingImageView.contentMode = UIViewContentModeScaleAspectFill;
movingImageView.clipsToBounds = YES;[self.scrollView addSubview:movingImageView];

Screenshot for AVPlayer and Video

I am trying to take a screenshot of an AVPlayer inside a bigger view. I want to build a testing framework only, so private APIs or any method is good, because the framework will not be included when releasing to the AppStore.
I have tried with using
UIGetScreenImage() : works well on simulator but not on device
snapshotviewafterscreenupdates: it shows the view but I cannot create a UIImage from that.
drawViewInHierarchy and renderInContext will not work with AVPlayer
I don't want to use AVAssetGenerator for getting image from video, it is hard to get a good coordinate or the video player as the subview of other views
I know you don't want to use the AVAssetImageGenerator but I've also researched this extensively and I believe the only solution currently is using the AVAssetImageGenerator. It's not that difficult as you say to get the right coordinate because you should be able to get the current time of your player. In my App the following code works perfectly:
-(UIImage *)getAVPlayerScreenshot
{
AVURLAsset *asset = (AVURLAsset *)self.playerItem.asset;
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.requestedTimeToleranceAfter = kCMTimeZero;
imageGenerator.requestedTimeToleranceBefore = kCMTimeZero;
CGImageRef thumb = [imageGenerator copyCGImageAtTime:self.playerItem.currentTime
actualTime:NULL
error:NULL];
UIImage *videoImage = [UIImage imageWithCGImage:thumb];
CGImageRelease(thumb);
return videoImage;
}
AVPlayer rending videos using GPU, so you cannot capture it using core graphics methods.
However that’s possible to capture images with AVAssetImageGenerator, you need specify a CMTime.
Update:
Forget to take a screenshot of the entire screen. AVPlayerItemVideoOutput is my final choice, it supports video steam.
Here is my full implementation: https://github.com/BB9z/ZFPlayer/commit/a32c7244f630e69643336b65351463e00e712c7f#diff-2d23591c151edd3536066df7c18e59deR448
Swift version of Bob's answer below. I'm using AVQueuePlayer but should work for regular AVPlayer too.
public func getImageSnapshot() -> UIImage? {
guard let asset = player.currentItem?.asset else { return nil }
let imageGenerator = AVAssetImageGenerator(asset: asset);
imageGenerator.requestedTimeToleranceAfter = CMTime.zero;
imageGenerator.requestedTimeToleranceBefore = CMTime.zero;
do {
let thumb = try imageGenerator.copyCGImage(at: player.currentTime(), actualTime: nil);
let image = UIImage(cgImage: thumb);
return image;
} catch {
print("⛔️ Failed to get video snapshot: \(error)");
}
return nil;
}
Here is code for taking a screenshot of you entire screen, including the AVPlayer. You only need to add a UIImageView on top of your videoplayer, which stays hidden until we take the screenshot and then we hide it again.
func takeScreenshot() -> UIImage? {
//1 Hide all UI you do not want on the screenshot
self.hideButtonsForScreenshot()
//2 Create an screenshot from your AVPlayer
if let url = (self.overlayPlayer?.currentItem?.asset as? AVURLAsset)?.url {
let asset = AVAsset(url: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.requestedTimeToleranceAfter = CMTime.zero
imageGenerator.requestedTimeToleranceBefore = CMTime.zero
if let thumb: CGImage = try? imageGenerator.copyCGImage(at: self.overlayPlayer!.currentTime(), actualTime: nil) {
let videoImage = UIImage(cgImage: thumb)
//Note: create an image view on top of you videoPlayer in the exact dimensions, and display it before taking the screenshot
// mine is created in the storyboard
// 3 Put the image from the screenshot in your screenshotPhotoView and unhide it
self.screenshotPhotoView.image = videoImage
self.screenshotPhotoView.isHidden = false
}
}
//4 Take the screenshot
let bounds = UIScreen.main.bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, true, 0.0)
self.view.drawHierarchy(in: bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//5 show all UI again that you didn't want on your screenshot
self.showButtonsForScreenshot()
//6 Now hide the screenshotPhotoView again
self.screenshotPhotoView.isHidden = true
self.screenshotPhotoView.image = nil
return image
}
If you want to take screenshot of current screen just call following method on any action event which give you Image object.
-(UIImage *) screenshot
{
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *sourceImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//now we will position the image, X/Y away from top left corner to get the portion we want
UIGraphicsBeginImageContext(sourceImage.size);
[sourceImage drawAtPoint:CGPointMake(0, 0)];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//To write image on divice.
//UIImageWriteToSavedPhotosAlbum(croppedImage,nil, nil, nil);
return croppedImage;
}
Hope this will help you.
CGRect grabRect = CGRectMake(0,0,320,568);// size you want to take screenshot of.
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(grabRect.size, NO, [UIScreen mainScreen].scale);
} else {
UIGraphicsBeginImageContext(grabRect.size);
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, -grabRect.origin.x, -grabRect.origin.y);
[self.view.layer renderInContext:ctx];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

How to crop an image and set its cropping bounds from the original image

I've a UIImageView *userImage whose size is full screen and UIImageView *imageSquare whose size is 320x320. The user will be able to play with userImage to make it bigger, change position, etc. imageSquare is static and should be seen as the cropping view
The code below can crop userImage as the imageSquare.frame.size. My problem is that it crops it from the top of userImage and not from imageSquare.frame.origin, meaning I need to crop it from X and Y coordinates. It's my first time trying to do this and every things I've tried so far can't make it crop from imageSquare.frame.origin.
How could I crop the current view (the one the user is manipulating) of userImage from imageSquare.frame.origin?
CGSize pageSize = imageSquare.frame.size;
UIGraphicsBeginImageContext(pageSize);
CGContextRef resizedContext = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(resizedContext, userImage.frame.origin.x, userImage.frame.origin.y);
[userImage.layer renderInContext:resizedContext];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (image != nil) {
NSLog(#"is not nil");
NSData *imgData = UIImagePNGRepresentation(image);
imageSquare.image = [[UIImage alloc]initWithData:imgData];
}
You'll need to translate by negative x and y:
CGContextTranslateCTM(resizedContext,
-userImage.frame.origin.x,
-userImage.frame.origin.y);
[userImage.layer renderInContext:resizedContext];

Resources