With Auto Layout, how do I make a UIImageView's size dynamic depending on the image?

后端 未结 8 455
花落未央
花落未央 2020-11-28 21:57

I want my UIImageView to grow or shrink depending on the size of what the actual image it\'s displaying is. But I want it to stay vertically centered and 10pts

相关标签:
8条回答
  • 2020-11-28 22:35

    If you read the docs on UIImageView they say:

    Setting the image property does not change the size of a UIImageView. Call sizeToFit to adjust the size of the view to match the image.

    0 讨论(0)
  • 2020-11-28 22:40

    Here is the essential problem: the only way in which UIImageView interacts with Auto Layout is via its intrinsicContentSize property. That property provides the intrinsic size of the image itself, and nothing more. This creates three situations, with different solutions.

    case 1: view with image's size

    In the first case, if you place an UIImageView in a context where external Auto Layout constraints affect only its position, then the view's intrinsicContentSize will declare it wants to be the size of its image, and Auto Layout will automatically resize the view to equal the size of its image. This is sometimes exactly what you want, and then life is good!

    case 2: fully constrained view size, preserving image's aspect ratio

    At other times, you're in a different case. You know exactly the size you want the image view to take, but you also want it to preserve the aspect ratio of the displayed image. In this case, you can use Auto Layout to constrain the size of the image, while setting the old contentMode property to .scaleAspectFit on the image view. That property will cause the image view to rescale the displayed image, adding padding as needed. And if this is what you want, life is good!

    case 3: partially constrained view size, adapting to image aspect ratio

    But often times, you're in the tricky third case. You want to use Auto Layout to partially determine the size of the view, while allowing the image's aspect ratio to determine the other part. For instance, you might want to use Auto Layout constraints to determine one dimension of the size (like the width, in a vertical timeline scroll), while relying on the view to tell Auto Layout that it wants a height matching the image's aspect ratio (so the scrollable items are not too short or too tall).

    You cannot configure an ordinary image view to do this because because an image view only communicates its intrinsicContentSize. So if you put an image view in a context where Auto Layout constrains one dimension (for instance, constraining it to a short width), then the image view does not tell Auto Layout it wants its height rescaled in tandem. It continues to report its intrinsic content size, with the unmodified height of the image itself.

    In order to configure the image view to tell Auto Layout that it wants to take on the aspect ratio of the image it contains, you need to add a new constraint to that effect. Furthermore, you will need to update that constraint whenever the image itself is updated. You can build a UIImageView subclass that does this, so the behavior is automatic. Here's an example:

    public class ScaleAspectFitImageView : UIImageView {
      /// constraint to maintain same aspect ratio as the image
      private var aspectRatioConstraint:NSLayoutConstraint? = nil
    
      required public init?(coder aDecoder: NSCoder) {
        super.init(coder:aDecoder)
        self.setup()
      }
    
      public override init(frame:CGRect) {
        super.init(frame:frame)
        self.setup()
      }
    
      public override init(image: UIImage!) {
        super.init(image:image)
        self.setup()
      }
    
      public override init(image: UIImage!, highlightedImage: UIImage?) {
        super.init(image:image,highlightedImage:highlightedImage)
        self.setup()
      }
    
      override public var image: UIImage? {
        didSet {
          self.updateAspectRatioConstraint()
        }
      }
    
      private func setup() {
        self.contentMode = .scaleAspectFit
        self.updateAspectRatioConstraint()
      }
    
      /// Removes any pre-existing aspect ratio constraint, and adds a new one based on the current image
      private func updateAspectRatioConstraint() {
        // remove any existing aspect ratio constraint
        if let c = self.aspectRatioConstraint {
          self.removeConstraint(c)
        }
        self.aspectRatioConstraint = nil
    
        if let imageSize = image?.size, imageSize.height != 0
        {
          let aspectRatio = imageSize.width / imageSize.height
          let c = NSLayoutConstraint(item: self, attribute: .width,
                                     relatedBy: .equal,
                                     toItem: self, attribute: .height,
                                     multiplier: aspectRatio, constant: 0)
          // a priority above fitting size level and below low
          c.priority = (UILayoutPriorityDefaultLow + UILayoutPriorityFittingSizeLevel) / 2.0
          self.addConstraint(c)
          self.aspectRatioConstraint = c
        }
      }
    }
    

    More comment is available at this gist

    0 讨论(0)
提交回复
热议问题