I have a UIImageView with a fixed width and height. I don't want to change the frame of the UIImageView. I want to have it hold an image where I keep the aspect ratio and I fit the width and let the image be either too tall, or too short for the UIImageView's frame. Like this:
The red is the UIImageView's frame. The gray is the actual image as it's displayed.
I think the best way to do it is to play with the mode of your imageView (Aspect Fill, Aspect Width, etc) and this is based on the ratio between the width and height of the image
if image.width > image.height {
imageView.contentMode = UIViewContentModeScaleAspectFit
//since the width > height we may fit it and we'll have bands on top/bottom
} else {
imageView.contentMode = UIViewContentModeScaleAspectFill
//width < height we fill it until width is taken up and clipped on top/bottom
}
UIViewContentModeScaleAspectFit
Scales the content to fit the size of the view by maintaining the
aspect ratio. Any remaining area of the view’s bounds is transparent.
UIViewContentModeScaleAspectFill
Scales the content to fill the size of the view. Some portion of the
content may be clipped to fill the view’s bounds.
I haven't tested it but off the top of my head this seems right
I think you need to compare the image aspect ratio to the aspect ratio of the UIImageView itself:
private func updateUI() {
guard let image = image else { return }
let viewAspectRatio = self.bounds.width / self.bounds.height
let imageAspectRatio = image.size.width / image.size.height
if viewAspectRatio > imageAspectRatio {
self.contentMode = .scaleAspectFill
} else {
self.contentMode = .scaleAspectFit
}
}
override var image: UIImage? { didSet { updateUI() }}
override func layoutSubviews() {
super.layoutSubviews()
updateUI()
}
Note: this is aspect fit width
Swift 5.1 iOS 13
Because mine was on the header cell on a collection view this is what worked for me:
if headerCell!.imageView.frame.width > headerCell!.imageView.frame.height {
headerCell!.imageView.contentMode = .scaleAspectFit
//since the width > height we may fit it and we'll have bands on top/bottom
} else {
headerCell!.imageView.contentMode = .scaleAspectFill
//width < height we fill it until width is taken up and clipped on top/bottom
}
For my case solution was to set UIImageView's contentMode based on if ratio of image's height and width is bigger than of imageView's.
func setupImageViewContentMode() {
if let image = imageView.image, image.size.height / image.size.width > imageView.frame.height / imageView.frame.width {
imageView.contentMode = .scaleAspectFit
} else {
imageView.contentMode = .scaleAspectFill
}
}
Also, note that you have to setup this according to current layout, so calling this method e.g. in layoutSubviews(), in viewDidLayoutSubviews(), after image is loaded from backend or wherever you need it does the job.
Related
I would like to change the height of imageview based on how much height the image occupies in imageview.
I am using content mode Aspect Fit
Any ideas on how to do that ?
If you want your image to fill your whole image view and have limit only for width of that image view, then the following code should help:
let imageView: UIImageView = ...
let image: UIImage = ...
let imageSize = image.size
let scaleFactor = imageSize.height / imageSize.width
let imageViewHeight = imageView.bounds.width * scaleFactor
If you use constraint based layout you don't even need the imageViewHeight - you can use calculated scaleFactor to set corresponding constraint for height of the imageView
for me :
let image: UIImage = UIImage(named: "")
print(image.size) // 400x400
let scaleFator = UIScreen.main.scale //3
let height = image.size.height / scaleFator ///here the actual height of image.
I really got frustrated with auto layout and UITableViewCell stuff. I have a UITableView with dynamic heighted cells. Cells can have an image inside them. Image should fit to UIImageView's width.
Images are larger than UIImageView's size. So, after downloading the image, I resize the image and then put it inside the UIImageView. However, resizing the image with respect to UIImageView's width leads to low resolution as UIScreen's scale is greater than 1. So I tried to increase the expected size of the image after the operation (size= (scale * imageViewWidth, scale * imageViewHeight)).
However, this time, cell's height becomes 2 times bigger. What I'm trying to do is basically, while keeping UIImageView's height the same, increasing the resolution.
class ProgressImageView : UIImageView{
func setImage(imagePath:String?, placeholder:String, showProgress:Bool, showDetail:Bool){
if imagePath == nil{
self.image = UIImage(named: placeholder)
}else{
if showProgress{
self.setShowActivityIndicatorView(true)
self.setIndicatorStyle(.Gray)
}
let placeholderImage = UIImage(named: placeholder)
SDWebImageManager.sharedManager().downloadImageWithURL(NSURL(string: imagePath!), options: SDWebImageOptions.RefreshCached, progress: nil, completed: { (image, error, _, _, _) in
//resize image to fit width
if error != nil{
self.image = placeholderImage
}else {
self.setImageFitWidth(image)
}
})
}
}
}
extension UIImageView{
func setImageFitWidth(image: UIImage){
let w = self.bounds.size.width //* UIScreen.mainScreen().scale
print ("ImageView size:\(self.bounds.size) scaled width:\(w)")
self.image = image.convertToWidth(w)
}
}
extension UIImage{
func convertToWidth(let width: CGFloat) -> UIImage{
print ("Old image size:\(self.size)")
let ratio = self.size.width / width
let height = self.size.height / ratio
let size = CGSizeMake(width, height)
print ("New image size:\(size)")
UIGraphicsBeginImageContext(size)
self.drawInRect(CGRectMake(0, 0, size.width, size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
As you can see in setImageFitWidth method, without UIScreen.mainScreen().scale it works just fine except the resolution and with the scale, UIImageView doubles in size. By the way, I tried all possible options of the content mode (aspect fit, aspect fill etc.) and I am using auto layout.
Result in low resolution:
Result in high resolution:
I want my screen to be just like in the first ss but with resolution of the image in second ss.
Try using UIGraphicsBeginImageContextWithOptions(size: size, opaque: YES, scale: 0)
to draw your image.
Scale 0 means the function will get the proper scale factor from the device.
The app will always be in portrait mode, and I want to set an image as the background using an Image View. The entire width of the image needs to fit the width of the screen, and the aspect ratio should stay the same. The top and bottom of the image can "bleed over" and not be visible. "Aspect Fit" doesn't help me because the top and bottom are fully shown, causing the image width not fit the screen width.
Have you tried Autolayout in your storyboard? Add an UIImageView and select Pin. Here you can choose your values of the positioning.
Or you can use code
let width = UIScreen.mainScreen().bounds.size.width
let height = UIScreen.mainScreen().bounds.size.height
let imageView = UIImageView(frame: CGRectMake(0, 0, width, height))
imageView.image = UIImage(named: "YOUR IMAGE NAME")
imageView.contentMode = UIViewContentMode.ScaleAspectFill
self.view.addSubview(imageView)
This view loads just one image and allows the user to zoom in and out to view parts of the image up close. However, it loads at a gigantic size, and I want it to load at screen size. Or if screen size is hard to get, then just loading at 50% size would work.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.scrollView.minimumZoomScale = 0
self.scrollView.maximumZoomScale = 1
self.scrollView.delegate = self
self.imageView.image = self.image!
}
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return self.imageView
}
}
How do I set the initial image size to a reasonable size, but allow for zooming to the full size image?
Thanks!
You just have to calculate the zoom dividing your view width by your image size width as follow:
scrollView.zoomScale = view.frame.width / imageView.image.size.width
Get the screen size: (objective-C)
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
Set the UIImageView frame to the screen size, and then set the aspect to fit:
imageView.contentMode = .ScaleAspectFit (UIViewContentModeScaleAspectFit in Obj-C)
Then, just set the zoomScale:
self.scrollView.zoomScale = .5;
I'm trying add UIImageViews to the ScrollView to get horizontaly pagging effect.
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(pageImages.count), pagesScrollViewSize.height)
for var index = 0; index < 3; ++index {
var frame = containerView.bounds
frame.origin.x = frame.size.width * CGFloat(index)
frame.origin.y = 0.0
let newPageView = UIImageView(image: pageImages[index])
newPageView.frame = frame
newPageView.contentMode = .ScaleAspectFit
scrollView.addSubview(newPageView)
}
Here is my layout:
But on phone/emulator image is scaled to ScrollView height. I want scale image to scrollview width, so on one page will be one image.
To scale image to scrollview width you need to resize (scale image to width) + crop image first. Setting content mode to ScaleAspectFit will only "...scale the content to fit the size of the view by maintaining the aspect ratio. Any remaining area of the view’s bounds is transparent".
The problem was that I was using autolayout in my Storyboard but I was adding new view's without adding constrains. So I've added this photos manualy in storyboard like was shown here: UIScrollView Paging Autolayout & Storyboard