UIImage 内容模式 aspectFit 和 bottom

UIImage content mode aspectFit and bottom

是否可以将我的UIImagecontentMode同时设置为.scaleAspectFit.bottom

我的图片现在是这样的:

UIImageView:

let nightSky: UIImageView = {
    let v = UIImageView()
    v.image = UIImage(named: "nightSky")
    v.translatesAutoresizingMaskIntoConstraints = false
    v.contentMode = .scaleAspectFit
    return v
}()

约束条件:

nightSky.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        nightSky.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -120).isActive = true
        nightSky.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
        nightSky.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true

这是允许纵横比和对齐属性的自定义 class。

它被标记为 @IBDesignable,因此您可以在 Storyboard / Interface Builder 中看到它。

@IBInspectable 属性是:

  • 图片
  • 水平对齐
  • 垂直对齐
  • 纵横比填充

Select 正常的图像 UIImageView

HAlign 的有效值为“左”“中”“右”或留空为默认值(中)。

VAlign 的有效值为“顶部”、“中心”、“底部”或留空为默认值(中心)。

“纵横比填充”是 OnOff (True/False)。如果为 True,图像将缩放为 Aspect Fill 而不是 Aspect Fit

@IBDesignable
class AlignedAspectFitImageView: UIView {
    
    enum HorizontalAlignment: String {
        case left, center, right
    }
    
    enum VerticalAlignment: String {
        case top, center, bottom
    }
    
    private var theImageView: UIImageView = {
        let v = UIImageView()
        return v
    }()
    
    @IBInspectable var image: UIImage? {
        get { return theImageView.image }
        set {
            theImageView.image = newValue
            setNeedsLayout()
        }
    }
    
    @IBInspectable var hAlign: String = "center" {
        willSet {
            // Ensure user enters a valid alignment name while making it lowercase.
            if let newAlign = HorizontalAlignment(rawValue: newValue.lowercased()) {
                horizontalAlignment = newAlign
            }
        }
    }
    
    @IBInspectable var vAlign: String = "center" {
        willSet {
            // Ensure user enters a valid alignment name while making it lowercase.
            if let newAlign = VerticalAlignment(rawValue: newValue.lowercased()) {
                verticalAlignment = newAlign
            }
        }
    }
    
    @IBInspectable var aspectFill: Bool = false {
        didSet {
            setNeedsLayout()
        }
    }
    
    var horizontalAlignment: HorizontalAlignment = .center
    var verticalAlignment: VerticalAlignment = .center
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        commonInit()
    }
    func commonInit() -> Void {
        clipsToBounds = true
        addSubview(theImageView)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        guard let img = theImageView.image else {
            return
        }
        
        var newRect = bounds
        
        let viewRatio = bounds.size.width / bounds.size.height
        let imgRatio = img.size.width / img.size.height
        
        // if view ratio is equal to image ratio, we can fill the frame
        if viewRatio == imgRatio {
            theImageView.frame = newRect
            return
        }
        
        // otherwise, calculate the desired frame

        var calcMode: Int = 1
        if aspectFill {
            calcMode = imgRatio > 1.0 ? 1 : 2
        } else {
            calcMode = imgRatio < 1.0 ? 1 : 2
        }

        if calcMode == 1 {
            // image is taller than wide
            let heightFactor = bounds.size.height / img.size.height
            let w = img.size.width * heightFactor
            newRect.size.width = w
            switch horizontalAlignment {
            case .center:
                newRect.origin.x = (bounds.size.width - w) * 0.5
            case .right:
                newRect.origin.x = bounds.size.width - w
            default: break  // left align - no changes needed
            }
        } else {
            // image is wider than tall
            let widthFactor = bounds.size.width / img.size.width
            let h = img.size.height * widthFactor
            newRect.size.height = h
            switch verticalAlignment {
            case .center:
                newRect.origin.y = (bounds.size.height - h) * 0.5
            case .bottom:
                newRect.origin.y = bounds.size.height - h
            default: break  // top align - no changes needed
            }
        }

        theImageView.frame = newRect
    }
}

使用这张图片:

下面是 240 x 240 AlignedAspectFitImageView 背景颜色设置为黄色(以便我们可以看到框架)的效果:

也可以通过代码设置属性。例如:

override func viewDidLoad() {
    super.viewDidLoad()
    
    let testImageView = AlignedAspectFitImageView()
    testImageView.image = UIImage(named: "bkg640x360")
    testImageView.verticalAlignment = .bottom
    
    view.addSubview(testImageView)

    // set frame / constraints / etc
    testImageView.frame = CGRect(x: 40, y: 40, width: 240, height: 240)
}

显示“Aspect Fill”和“Aspect Fit”之间的区别...

使用这张图片:

我们用 Aspect Fill: OffVAlign: bottom 得到这个结果:

然后是 Aspect Fill: OnHAlign: right 的结果:

UIImageView's 顶部布局约束优先级设置为最低(即 250),它将为您处理。