With Auto Layout, how do I make a UIImageView's size dynamic depending on the image?
The image view's intrinsic size is already dependent on the size of the image. Your assumptions (and constraints are correct).
However, if you've set up your image view in interface builder and have not provided it with an image, then the layout system (interface builder) won't know how big your image view is supposed to be at compile time. Your layout is ambiguous because your image view could be many sizes. This is what throws the errors.
Once you set your image view's image property, then the image view's intrinsic size is defined by the size of the image. If you're setting the view at runtime, then you can do exactly what Anna mentioned and provide interface builder with a "placeholder" intrinsic size in the property inspector of the image view. This tells interface builder, "use this size for now, I'll give you a real size later". The placeholder constraints are ignored at runtime.
Your other option is to assign the image to the image view in interface builder directly (but I assume your images are dynamic, so this won't work for you).
ios auto layout with dynamic size
From your question, I assume that you are setting this up in Interface Builder, correct me if I'm wrong.
Your problem is that at compile time the layout is not fully specified. A UIImageView
has an intrinsic content size which would complete the layout, but because you don't set the image for the image view at that time it isn't yet specified.
You can set a placeholder size in the size inspector of the UIImageView
. This has no effect at runtime but tells the compiler that there will be an intrinsic content size later.
How to resize UIImageView based on UIImage's size/ratio in Swift 3?
It looks like you want to resize an ImageView according to the image ratio and the container view's size, here is the example in Swift (Sorry,the former answer with a bug, I fixed it):
let containerView = UIView(frame: CGRect(x:0,y:0,width:320,height:500))
let imageView = UIImageView()
if let image = UIImage(named: "a_image") {
let ratio = image.size.width / image.size.height
if containerView.frame.width > containerView.frame.height {
let newHeight = containerView.frame.width / ratio
imageView.frame.size = CGSize(width: containerView.frame.width, height: newHeight)
}
else{
let newWidth = containerView.frame.height * ratio
imageView.frame.size = CGSize(width: newWidth, height: containerView.frame.height)
}
}
Change height of UIImageview dynamically
Since you're using auto-layout constraints you need to update height constraint not image view frame. So connect your height constraint as an IBOutlet
heightConstraint
in your tableViewCell. And then in table view cellForRowAt
you have to set cell.heightConstraint.constant = image_height
. And this will give you the desired result.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// don't forget to dequeue your table view cell here
//change height of UIImageView
let image_height = image_width * CGFloat(viewModel.imageScale)
cell.heightConstraint.constant = image_height
//load image
_ = homeViewCell.mainImageView.af_setImage(withURL: URL(string: serverURL + viewModel.mainImage)!, placeholderImage: UIImage(named : "home_placeholder") ...
}
Auto-Layout: Get UIImageView height to calculate cell height correctly
So I think the underlying issue is a kind of chicken and egg problem.
In order to 'aspect fit' the image to the UIImageView
the system needs a size for the view, and yet we would like the height of the view to be determined after aspect fitting the image given a known width for the view.
A workaround is to calculate the aspect ratio of the image ourselves and set it as a constraint on the UIImageView
when we set the image. That way the auto layout system has enough information to size the view (a width and an aspect ratio).
So I changed your custom cell class to:
class CustomCell: UITableViewCell {
@IBOutlet weak var imageTitle: UILabel!
@IBOutlet weak var postedImageView: UIImageView!
internal var aspectConstraint : NSLayoutConstraint? {
didSet {
if oldValue != nil {
postedImageView.removeConstraint(oldValue!)
}
if aspectConstraint != nil {
postedImageView.addConstraint(aspectConstraint!)
}
}
}
override func prepareForReuse() {
super.prepareForReuse()
aspectConstraint = nil
}
func setPostedImage(image : UIImage) {
let aspect = image.size.width / image.size.height
aspectConstraint = NSLayoutConstraint(item: postedImageView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: postedImageView, attribute: NSLayoutAttribute.Height, multiplier: aspect, constant: 0.0)
postedImageView.image = image
}
}
And then in the delegate do this instead of setting the image directly:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomCell
cell.imageTitle.text = titles[indexPath.row]
let image = images[titles[indexPath.row]]!
cell.setPostedImage(image)
return cell
}
And you will get:
Hope this helps!
Change UIImageView size to match image with AutoLayout
Auto Layout solution
Since establishing that you're using Auto Layout in your project, I have made a demo app to show you how you could change the image of the image view and adjust the height. Auto Layout will do this for you automatically, but the catch is that the photo you'll be using is coming from the users gallery and so they're likely to be very big and this.
So check out the app: https://bitbucket.org/danielphillips/auto-layout-imageview
The trick is to create a reference of the NSLayoutConstraint
of the height of the image view. When you change your image, you need to adjust it's constant to the correct height given the fixed width.
Your other solution could be to set a contentMode
on your image view, by using UIViewContentModeScaleAspectFit
your image will always appear in full but will be locked to the bounds of the image view, which can change based on the Auto Layout constraints you have.
Initial reply
It looks like you've really over complicated this, unless I've missed something.
When you get a new image from the image picker, all you need to do is change the frame of the image view according to the UIImage
size.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
image = [info objectForKey:UIImagePickerControllerOriginalImage];
[self.imageView setImage:image];
self.imageView.frame = CGRectMake(self.imageView.frame.origin.x
self.imageView.frame.origin.y
image.size.width
image.size.height);
[self dismissViewControllerAnimated:YES completion:NULL];
}
Of course this is going to be potentially very large (it's using the original image which may be very large).
So let's lock the width to 280 points... this way we can always have the full image on screen in portrait mode.
So assuming your image size is 1000x800, and our image view perhaps is 280x100. We can calculate the correct height for the image view retaining the image view's width like this:
CGSize imageSize = CGSizeMake(1000.0, 800.0);
CGSize imageViewSize = CGSizeMake(280.0, 100.0);
CGFloat correctImageViewHeight = (imageViewSize.width / imageSize.width) * imageSize.height;
self.imageView.frame = CGRectMake( self.imageView.frame.origin.x,
self.imageView.frame.origin.x,
CGRectGetWidth(self.imageView.bounds),
correctImageViewHeight);
Dynamic size of UIImageView inside UITableViewCell
The problem was that I was downloading the images from the web when the cells were being initiated and again when they were being reused.
Donwloading the cells content only once and storing them solved the problem.
Another way could be calculating the cell content height and defining them at heightForRowAt indexPath
.
iOS - Make UIImageView resize to fit UILabel with AutoLayout
For the first method, I assume you set the had the label and the image view have the same vertical and horizontal centers as well has the same height and width. The problem is the layout engine is assume the intrinsic size of the image is more important than the label. To change that you need to change hugging priority and the compression resistance of the views. Go to the size inspect of the label (picture) and increase the hugging priority to required. This will for it's height and width to be it's intrinsic height and width. You could also decrease the image view's compression resistance, so it is more amendable to being sized down.
The constraints for the label:
You can change the top and leading to place it in the appropriate spot in the view.
The constraints for the image view:
Related Topics
How to Detect the Touch Event of an Uiimageview
How to Get Audio Volume Level, and Volume Changed Notifications on iOS
Testing Corelocation on iPhone Simulator
How to Programmatically Determine If My App Is Running in the iPhone Simulator
Insert CSS into Loaded HTML in Uiwebview/Wkwebview
How to Define Optional Methods in Swift Protocol
Swift Filter Dictionary Error: Cannot Assign a Value of Type '[(_, _)]' to a Value of Type '[_:_]'
Xcode Without Storyboard and Arc
Nspersistentcontainer Concurrency for Saving to Core Data
Get Last Image from Photos.App
How to Open File and Append a String in It, Swift
What Is 'Vary for Traits' in Xcode 8
How to Capitalize Each Word in a String Using Swift iOS
Renew Push Certificate and Keep Current App Store App Working
Extract Uiimage from Nsattributed String
Prompt When Trying to Dial a Phone Number Using Tel:// Scheme on iOS 10.3