繁体   English   中英

如何在 Swift 3 中根据 UIImage 的大小/比例调整 UIImageView 的大小?

[英]How to resize UIImageView based on UIImage's size/ratio in Swift 3?

我有一个UIImageView ,用户可以下载各种格式的UIImages 问题是我需要UIImageView根据给定的图像比例调整大小。

目前,我正在使用Aspect fit ,但UIImageView本身的大部分仍然是空的。 我想让UIImageView根据其内容调整自身大小。 例如,如果图片是 1:1、4:3、6:2、16:9...

在此处输入图片说明

非常感谢帮助。

根据要求,这就是我想要的:

在此处输入图片说明

我有一个方形的UIImageView ,加载了 16:7 或其他UIImageView的图像,并且UIImageView调整了大小以适应图像的大小......

我花了很多时间试图找到解决您遇到的相同问题的方法,这是唯一对我有用的解决方案(Swift 4,Xcode 9.2):

class ScaledHeightImageView: UIImageView {

    override var intrinsicContentSize: CGSize {

        if let myImage = self.image {
            let myImageWidth = myImage.size.width
            let myImageHeight = myImage.size.height
            let myViewWidth = self.frame.size.width
 
            let ratio = myViewWidth/myImageWidth
            let scaledHeight = myImageHeight * ratio

            return CGSize(width: myViewWidth, height: scaledHeight)
        }

        return CGSize(width: -1.0, height: -1.0)
    }

}

将该类添加到项目中,并将 UIImageView 设置为自定义类 ScaledHeightImageView。 图像视图的内容模式是 Aspect Fit。

我的问题和这篇文章中提到的一样。 在我的原型 TableViewCell 的 ContentView 中,我有一个垂直 StackView 约束到每个边缘。 在 StackView 里面有一个 Label、ImageView 和另一个 Label。 将 ImageView 设置为 AspectFit 是不够的。 图像将是正确的大小和比例,但 ImageView 没有包裹实际图像,在图像和标签之间留下了一堆额外的空间(就像上图一样)。 ImageView 高度似乎与原始图像的高度相匹配,而不是调整后图像的高度(在 aspectFit 完成它的工作之后)。 由于各种原因,我发现的其他解决方案并没有完全解决问题。 我希望这可以帮助别人。

我花了很多时间在这上面,我终于得到了一个对我有用的解决方案( Swift 3 ):

  • 在 IB 中,我将 UIImageView 的“内容模式”设置为“Aspect Fit”
  • 在 IB 中,我将 UIImageView 的宽度约束设置为等于您想要的任何值(在我的情况下,视图的宽度)
  • 在 IB 中,我将 UIImageView 的高度约束设置为等于 0,并为其创建一个引用出口(例如, constraintHeight

然后,当我需要显示图像时,我只需编写以下内容(从上面的答案中取样):

let ratio = image.size.width / image.size.height
let newHeight = myImageView.frame.width / ratio
constraintHeight.constant = newHeight
view.layoutIfNeeded()

基本上,这可以确保图像填充 UIImageView 的宽度并强制 UIImageView 的高度在缩放后等于图像的高度

看起来您想根据图像比例和容器视图的大小调整 ImageView 的大小,这是 Swift 中的示例(对不起,前一个答案有错误,我已修复):

   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)
        }
    }

我使用的解决方案是基于olearyj234 的解决方案,但使没有图像占用基本上没有空间(或者更具体地说,最小的 iOS 将接受)。 它还使用 ceil 来避免当 UIImageView 嵌入在滚动单元格等内容中时非整数值可能出现的问题。

class FixedWidthAspectFitImageView: UIImageView
{

    override var intrinsicContentSize: CGSize
    {
        // VALIDATE ELSE RETURN
        // frameSizeWidth
        let frameSizeWidth = self.frame.size.width

        // image
        // ⓘ In testing on iOS 12.1.4 heights of 1.0 and 0.5 were respected, but 0.1 and 0.0 led intrinsicContentSize to be ignored.
        guard let image = self.image else
        {
            return CGSize(width: frameSizeWidth, height: 1.0)
        }

        // MAIN
        let returnHeight = ceil(image.size.height * (frameSizeWidth / image.size.width))
        return CGSize(width: frameSizeWidth, height: returnHeight)
    }

}

快速 5

这是我在我的项目中所做的:

在 ViewController 中放置一个 ImageView 并在viewDidLoad()创建一个名为imageView的插座。

 override func viewDidLoad() { super.viewDidLoad() var image = UIImage(contentsOfFile: "yourFilePath")! var aspectR: CGFloat = 0.0 aspectR = image.size.width/image.size.height imageView.translatesAutoresizingMaskIntoConstraints = false imageView.image = image imageView.contentMode = .scaleAspectFit NSLayoutConstraint.activate([ imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor), imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor), imageView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor), imageView.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor), imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 1/aspectR) ]) }

NSLayoutConstraint.activate数组的最后 3 行确保图像宽度保持在容器视图的边界内,并且高度保持与宽度成比例(即保持纵横比并将imageView高度缩小到所需的最小值)。


Interface Builder 中的视图控制器: Main.storyboard

正在运行的应用程序中 UIImageView 的快照: appSnapshot

该解决方案也是基于olearyj234的解决方案,但我认为这会帮助更多人。

@IBDesignable
class DynamicImageView: UIImageView {

    @IBInspectable var fixedWidth: CGFloat = 0 {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }
    @IBInspectable var fixedHeight: CGFloat = 0 {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        var size = CGSize.zero
        if fixedWidth > 0 && fixedHeight > 0 { // 宽高固定
            size.width = fixedWidth
            size.height = fixedHeight
        } else if fixedWidth <= 0 && fixedHeight > 0 { // 固定高度动态宽度
            size.height = fixedHeight
            if let image = self.image {
                let ratio = fixedHeight / image.size.height
                size.width = image.size.width * ratio
            }
        } else if fixedWidth > 0 && fixedHeight <= 0 { // 固定宽度动态高度
            size.width = fixedWidth
            if let image = self.image {
                let ratio = fixedWidth / image.size.width
                size.height = image.size.height * ratio
            }
        } else { // 动态宽高
            size = image?.size ?? .zero
        }
        return size
    }

}

将您的 imageView 设置为 aspectFit,这将调整图像大小以不超过您的 imageView 的框架。

您可以使用this question的逻辑获取imageView的UIImage的大小-基本上只需获取UIImage的高度和宽度。

计算比例并设置 imageView 的宽度/高度以适合您的屏幕。

还有一个类似的问题,您可能会从中得到答案。

我修改了@user8969729 的解决方案,用“max”替换了“固定”宽度/高度,因此更像是@JoshuaHart 的解决方案。 按照您的意愿处理maxWidth == 0 / maxHeight == 0情况,因为我总是设置了这两种情况,所以我很快就忽略了这种情况。

public class AdjustsViewBoundsImageView: UIImageView {
   
   /// The maximum width that you want this imageView to grow to.
   @objc dynamic var maxWidth: CGFloat = 0 {
      didSet {
         invalidateIntrinsicContentSize()
      }
   }
   
   /// The maximum height that you want this imageView to grow to.
   @objc dynamic var maxHeight: CGFloat = 0 {
      didSet {
         invalidateIntrinsicContentSize()
      }
   }
   
   private var maxAspectRatio: CGFloat { return maxWidth / maxHeight }
   
   override public var intrinsicContentSize: CGSize {
      guard let classImage = self.image else { return super.intrinsicContentSize }
      if maxHeight == 0 || maxWidth == 0 {
         return super.intrinsicContentSize
      }
      
      let imageWidth = classImage.size.width
      let imageHeight = classImage.size.height
      let aspectRatio = imageWidth / imageHeight
      
      // Width is greater than height, return max width image and new height.
      if imageWidth > imageHeight {
         let newHeight = maxWidth/aspectRatio
         return CGSize(width: maxWidth, height: newHeight)
      }
      
      // Height is greater than width, return max height and new width.
      if imageHeight > imageWidth {
         // If the aspect ratio is larger than our max ratio, then using max width
         // will be hit before max height.
         if aspectRatio > maxAspectRatio {
            let newHeight = maxWidth/aspectRatio
            return CGSize(width: maxWidth, height: newHeight)
         }
         let newWidth = maxHeight * aspectRatio
         return CGSize(width: newWidth, height: maxHeight)
      }
      
      // Square image, return the lesser of max width and height.
      let squareMinimumValue = min(maxWidth, maxHeight)
      return CGSize(width: squareMinimumValue, height: squareMinimumValue)
   }
}

这里的很多答案在计算intrinsicContentSize时都使用frame 文档不鼓励这样做:

这种固有大小必须独立于内容框架,因为例如,无法根据更改的高度将更改的宽度动态传达给布局系统。

我发现希望根据以下条件动态设置UIImageView高度:

  • image的纵横比
  • 固定宽度

作为一个常见问题,我在下面提供了一个可能的解决方案。

解决方案

我认为最好通过向UIImageView添加一个NSLayoutConstraint来解决这个问题,该UIImageView约束了widthAnchorheightAnchor (反之亦然),以便multiplier与图像的纵横比相匹配。 我创建了一个UIImageView子类,它正是这样做的:

import UIKit

/// `AdjustableImageView` is a `UIImageView` which should have a fixed width or height.
/// It will add an `NSLayoutConstraint` such that it's width/height (aspect) ratio matches the
/// `image` width/height ratio.
class AdjustableImageView: UIImageView {

    /// `NSLayoutConstraint` constraining `heightAnchor` relative to the `widthAnchor`
    /// with the same `multiplier` as the inverse of the `image` aspect ratio, where aspect
    /// ratio is defined width/height.
    private var aspectRatioConstraint: NSLayoutConstraint?

    /// Override `image` setting constraint if necessary on set
    override var image: UIImage? {
        didSet {
            updateAspectRatioConstraint()
        }
    }

    // MARK: - Init

    override init(image: UIImage?) {
        super.init(image: image)
        setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    // MARK: - Setup

    /// Shared initializer code
    private func setup() {
        // Set default `contentMode`
        contentMode = .scaleAspectFill

        // Update constraints
        updateAspectRatioConstraint()
    }

    // MARK: - Resize

    /// De-active `aspectRatioConstraint` and re-active if conditions are met
    private func updateAspectRatioConstraint() {
        // De-active old constraint
        aspectRatioConstraint?.isActive = false

        // Check that we have an image
        guard let image = image else { return }

        // `image` dimensions
        let imageWidth = image.size.width
        let imageHeight = image.size.height

        // `image` aspectRatio
        guard imageWidth > 0 else { return }
        let aspectRatio = imageHeight / imageWidth
        guard aspectRatio > 0 else { return }

        // Create a new constraint
        aspectRatioConstraint = heightAnchor.constraint(
            equalTo: widthAnchor,
            multiplier: aspectRatio
        )

        // Activate new constraint
        aspectRatioConstraint?.isActive = true
    }
}

如果内容模式设置为aspectFitaspectFill ,答案会有所不同:

extension UIImageView {
var intrinsicScaledContentSize: CGSize? {
    switch contentMode {
    case .scaleAspectFit:
        // aspect fit
        if let image = self.image {
            let imageWidth = image.size.width
            let imageHeight = image.size.height
            let viewWidth = self.frame.size.width
            
            let ratio = viewWidth/imageWidth
            let scaledHeight = imageHeight * ratio
            
            return CGSize(width: viewWidth, height: scaledHeight)
        }
    case .scaleAspectFill:
        // aspect fill
        if let image = self.image {
            let imageWidth = image.size.width
            let imageHeight = image.size.height
            let viewHeight = self.frame.size.width
            
            let ratio = viewHeight/imageHeight
            let scaledWidth = imageWidth * ratio
            
            return CGSize(width: scaledWidth, height: imageHeight)
        }
    
    default: return self.bounds.size
    }
    
    return nil
}

}

SWIFT 5 类

如果需要,这可以很容易地转换为使用 IBOutlets。 我的用例涉及以编程方式添加 imageViews。 这是非常可靠的。 只需在您的项目中创建一个新文件并添加以下代码。

import UIKit

/// Resizeable Image View that takes a max height and max width
/// Will resize the imageView to best fit for the aspect ratio of the image,
/// With the given space provided.
public class ResizeableImageView: UIImageView {
    private var widthConstraint: NSLayoutConstraint?
    private var heightConstraint: NSLayoutConstraint?
    
    // MARK: - INITIALIZERS:
    
    public override init(image: UIImage?) {
        super.init(image: image)
    }
    
    /// Given the max width and height, resizes the imageView to fit the image.
    ///  - IMPORTANT: This subclass adds a height and width constraint.
    /// - Parameters:
    ///   - image: (UIImage?) The image to add to the imageView.
    ///   - maxWidth: (CGFloat) The max width you would like the imageView to grow to.
    ///   - maxHeight: (CGFloat) The max height you would like the imageView to grow to.
    convenience init(image: UIImage?, maxWidth: CGFloat, maxHeight: CGFloat) {
        self.init(image: image)
        widthConstraint = constrain(width: maxWidth)
        heightConstraint = constrain(height: maxHeight)
    }
    
    @available (*, unavailable) required internal init?(coder aDecoder: NSCoder) { nil }
    
    // MARK: - VARIABLES:
    
    /// The maximum width that you want this imageView to grow to.
    private var maxWidth: CGFloat {
        get { widthConstraint?.constant ?? 0 }
        set { widthConstraint?.constant = newValue }
    }
    
    /// The maximum height that you want this imageView to grow to.
    private var maxHeight: CGFloat {
        get { heightConstraint?.constant ?? 0 }
        set { heightConstraint?.constant = newValue }
    }
    
    private var maxAspectRatio: CGFloat { maxWidth / maxHeight }
    
    override public var intrinsicContentSize: CGSize {
        guard let classImage = self.image else { return frame.size }
        
        let imageWidth = classImage.size.width
        let imageHeight = classImage.size.height
        let aspectRatio = imageWidth / imageHeight
        
        // Width is greater than height, return max width image and new height.
        if imageWidth > imageHeight {
            let newHeight = maxWidth/aspectRatio
            self.widthConstraint?.constant = maxWidth
            self.heightConstraint?.constant = newHeight
            return CGSize(width: maxWidth, height: newHeight)
        }
        
        // Height is greater than width, return max height and new width.
        if imageHeight > imageWidth {
            // If the aspect ratio is larger than our max ratio, then using max width
            // will be hit before max height.
            if aspectRatio > maxAspectRatio {
                let newHeight = maxWidth/aspectRatio
                self.widthConstraint?.constant = maxWidth
                self.heightConstraint?.constant = newHeight
                return CGSize(width: maxWidth, height: newHeight)
            }
            let newWidth = maxHeight * aspectRatio
            self.widthConstraint?.constant = newWidth
            self.heightConstraint?.constant = maxHeight
            return CGSize(width: newWidth, height: maxHeight)
        }
        
        // Square image, return the lesser of max width and height.
        let squareMinimumValue = min(maxWidth, maxHeight)
        self.widthConstraint?.constant = squareMinimumValue
        self.heightConstraint?.constant = squareMinimumValue
        return CGSize(width: squareMinimumValue, height: squareMinimumValue)
    }
}

示例用法:

let imageView = ResizeableImageView(image: image, maxWidth: 250, maxHeight: 250)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM