1

是否可以同时设置contentModefor my UIImageto.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
4

2 回答 2

2

这是一个允许 Aspect Fit 和 Alignment 属性的自定义类。

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

@IBInspectable属性是:

  • 图片
  • 水平对齐
  • 垂直对齐
  • 方面填充

在此处输入图像描述

像选择普通UIImageView.

有效值为HAlign"left" "center" "right" 或默认留空(中心)。

有效值为VAlign"top" "center" "bottom" 或默认留空(中心)。

“方面填充”是OnOff(真/假)。如果为 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: Off我们用and得到这个结果VAlign: bottom

在此处输入图像描述

然后用Aspect Fill: Onand得到这个结果HAlign: right

在此处输入图像描述

于 2020-04-10T18:20:38.780 回答
0

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

于 2021-08-02T15:55:54.487 回答