I'm trying to place an UIImageView in the parent view's center. Image must keep its original aspect ratio and also it shouldn't exceed parent's bounds. So landscape images should be limited by parent's width and portrait images should occupy as much vertical space as needed keeping original ratio.
Sounds like quite a simple task for AutoLayout, right? Here's my setup for UIImageView:
center vertically in parent
center horizontally in parent
imageView.width <= superview.width
imageView.height <= superview.height
I also set contentMode to Aspect Fit. Everything works really great for small images which are smaller than device screen but for some reason my UIImageView takes more space than its underlying UIImage for large images (notice the green background - imageView.backgroundColor = [UIColor greenColor]).
Why is this happening? I am not an AutoLayout expert by my constraints look reasonable to me. If I use smaller images like 200x400 then UIImageView takes exactly 200x400 points on the screen with no extra green areas.
I'd like to add borders and rounded corners which obviously won't work properly in this case.
This is happenning because you've set width and height constraints as <=.
For small images imageView's frame is calculated without any issues and all constraints are satisfied. But when a big image is set, imageView's size is going to be set so that the image fits, but at the same it is limited by the size of the superview (screen size).
If you know aspect ratio for sure, you can set it as a constraint and
you won't see any green background.
Otherwise just leave your
constraints as they are and set background color to clear color. This
way any unoccupied zones will be transparent and any image you set
will take maximum space in at least one dimension.
Edit:
Probably not the best solution, kind of oldschool. You can add strict width and height constraints and calculate them manually using image's aspectRatio:
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
#IBOutlet weak var widthConstraint: NSLayoutConstraint!
func setImage(image: UIImage) {
imageView.image = image
let screenSize = UIScreen.main.bounds.size
let imageAspectRatio = image.size.width / image.size.height
let screenAspectRatio = screenSize.width / screenSize.height
if imageAspectRatio > screenAspectRatio {
widthConstraint.constant = min(image.size.width, screenSize.width)
heightConstraint.constant = widthConstraint.constant / imageAspectRatio
}
else {
heightConstraint.constant = min(image.size.height, screenSize.height)
widthConstraint.constant = heightConstraint.constant * imageAspectRatio
}
view.layoutIfNeeded()
}
Try to check "Clip to Bounds" for imageView in Interface Builder
Clip to Bounds Image View
and you must have a well-defined height and width, not "<="
Related
I have UIImageView in the Scroll View, I set content mode of image view is aspect fit. When I select an image from gallery then it shows blank space in the above and bottom of the image but I want the removed empty space of image in the scroll view.
Basically you need to keep same aspect ratio for image size and image view.
If your image is in app bundle then check its aspect ration and give
same aspect ratio constraint to image view from storyboard.
if your image is dynamic i.e. coming from api then give height
constraint to it (imageViewHeight). and change it as per image aspect
ratio.
let height = (imageView.frame.width * image.size.height) / image.size.width
imageViewHeight.constant = height
imageViewHeight is height constraint.
Hook the height constraint of the imageView
let h = (imageView.frame.width * img.size.height) / img.size.width
imageViewHCon.constant = h
self.view.layoutIfNeeded()
Above you apply the same aspect ratio to the imageView , so there will be no blank around it
On my storyboard I have 2 image views.
One holds the main image, the second is an overlay which will be used to specify the crop area of the main image.
In my viewDidLoad I've done this.
let screen_width = UIScreen.mainScreen().bounds.width
let screen_height = UIScreen.mainScreen().bounds.height
self.overlayImage.frame = CGRect(x: self.imageView.frame.origin.x, y: self.imageView.frame.origin.y, width: screen_width, height: (screen_height * 0.1919))
The goal is to have the overlayImage's top left corner lined up properly with the imageView's top left corner. Also the height of the overlay should be about 1/5th of the screens size. However when I run the code the overlayImage is exactly the same size and in the same location it was originally on the storyboard.
How can I programmatically line it up on top of the imageView after the image has been set to it, and also resize the overlayImage dynamically in the viewDidload?
I'd just do it manually in storyboard editor but everyone will have different screen sizes so I thought it best to use the mainscreen().bounds.height variable to determine the amount of height to use dynamically at runtime.
All you have to do is to write your above code in viewDidAppear instead of viewDidLoad as below:-
override func viewDidAppear(animated: Bool) {
let screen_width = UIScreen.mainScreen().bounds.size.width
let screen_height = UIScreen.mainScreen().bounds.size.height
self.overlayImage.frame = CGRect(x: 0, y: 0, width: screen_width, height:screen_height/5)
}
And your frame will set itself as desired.
Since you're working with Storyboard, I would rather use Autolayout and Constraints to solve this problem for me... For instance you set the constraints for your first image view, then set the constraints of your overlay based and related to the former one. Example:
Lower UIImageView: set leading, trailing, top and bottom constraints
Overlay: set center X and Y constraints to be equal to the lower Image
Overlay: set height and width to be proportional to the lower image
Thus, when ever the screen size changes, the two images will always resize equally...
Interface builder:
Set overlay top, leading, width to your image view.
give your overlay a constant height constraint; the constraint cosntant value is arbitrary because you will replace it at run time
ctrl + drag an IBOutlet from this height constraint to your viewcontroller
in your viewWillLayoutSubViews set the .constant of your height constraint to UIScreen.main.bounds.size.height / 5
This is my current situation:
I have a UIViewController with UIImageView on it.
The latter has the following constraints: trailing to superview, leading to superview, top space to top layout guide, width equals 320, height equals 820.
Height and width are connected as outlets to my class and I'm setting them in viewDidLoad:
#IBOutlet weak var imageHeightconstraint: NSLayoutConstraint!
#IBOutlet weak var imageWidthConstraint: NSLayoutConstraint!
#IBOutlet weak var backgroundImage: UIImageView!
override viewDidLoad(){
backgroundImage.af_setImageWithURL(NSURL(string: photoURL)!)
imageHeightconstraint.constant = backgroundImage.image!.size.height
imageWidthConstraint.constant = self.view.frame.size.width
}
Now, my image is this:
it is 480x640px photo. I tried in my storyboard to set the mode to center, scale to fill, aspect fill, aspect fit and top.
For example, this is center:
aspect fit:
aspect fill:
but I don't know how to stretch this image from left to right and keep the aspect ratio, so the whole photo is visible and its left and right edges stick to the left and right edge of the screen.
I thought those two constraints could fix it:
imageHeightconstraint.constant = backgroundImage.image!.size.height
imageWidthConstraint.constant = self.view.frame.size.width
but it does not work. Maybe the problem is that the resolution of the photo is low (480x640), so the height is only that small? Can you give me any hint how could I fix it?
To have the image stick to both sides of screen and maintain the aspect ratio, set these 4 constraints:
Leading Edge of ImageView to Leading Edge of SuperView.
Trailing Edge of ImageView to Trailing Edge of SuperView.
Top Edge of ImageView to Top Layout Guide Bottom.
Set an aspect ratio constraint: ImageView Width equal to ImageView Height with multiplier 480:640. To set this, control-drag diagonally in the ImageView and select Aspect Ratio from the pop-up. Then change the multiplier to 480:640.
Set the content mode for the image to Scale to fill.
You may not be calling setNeedsLayout() and then layoutIfNeeded().
In order for the UI to update, you need to tell the view to update, not just change the constraints.
Either way, if you want the imageView to consistently fill the screen and you shouldn't have a height/width on the ImageView, only hugging the sides. Also make sure those constraints are ignoring margins.
If you want the image to fill the imageView, set the resizing mode to aspectFill, and If you're looking for it to resize until the left/right or top/bottom reaches the edge of the view, aspectFit is what you need.
I would like to rescale my image to fit the width of an iOS screen. The following is the code that I used.
#IBOutlet var bgImageView: UIImageView
override func viewDidLoad() {
super.viewDidLoad()
bgImageView.contentMode = UIViewContentMode.ScaleAspectFit;
bgImageView.frame.size.width = UIScreen.mainScreen().bounds.width;
}
This solution however seems to be causing thread problems. What's wrong here?
You can set your image view constrains as follows - its width should be equal to your super view width, centerX and centerY are the same as super's. And height not an equal constraint, but lessThanOrEqual to super's height.
And yes, it is useless to set frame manually while using auto layout constraints
I have a Storyboard that uses Auto Layout & Size Classes. I've given up trying to get my UIView to use the Aspect Ratio 1:1 constraint to work. It takes over the entire screen rather than just set height = width like a UIImageViewer does.
So I am trying to change the width and height constraints instead. In IB the width constraint is set to 320 and the height constraint is set to 320. Ultimately I will set the width constraint to the width of the device and the height constraint to the width of the device, hopefully getting a square UIView for the camera to display its session.
However, when I set the constants and then setNeedsUpdateContraints, nothing happens. Yes I would prefer not to do it this way, but I would also prefer to get this &^&$^#( thing to work and not spend any more hours fighting it. Setting leading space to 0, trailing space to 0, top space to 0, and aspect ratio 1:1 does not work. Even though I did it with a UIImageView and it does work for that.
#IBOutlet var captureFrame: UIView!
#IBOutlet var viewHeightConstraint: NSLayoutConstraint!
#IBOutlet var viewWidthConstraint: NSLayoutConstraint!
override func viewDidAppear(animated: Bool) {
self.viewWidthConstraint.constant = 100 //100 = temporary
self.viewHeightConstraint.constant = 100 //100 = temporary
captureFrame.setNeedsUpdateConstraints()
captureFrame.layoutIfNeeded()
}
Any idea how to get a square UIView so that the camera can be displayed in it using IB, auto layout, size classes AND not have it zoomed in X2?
Why aren't my constraints adjusting to 100? Why is my UIView still 320 x 320?