【问题标题】:UIImage content mode aspectFit and bottomUIImage内容模式aspectFit和bottom
【发布时间】:2020-04-10 14:58:43
【问题描述】:

是否可以同时将我的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

【问题讨论】:

  • 您只能拥有一个contentMode。如果您将此图像存储在本地,那么您应该编辑实际图像。如果您通过无法控制的网络调用加载图像,那么您可能需要在某个地方做出妥协,除非您想使用CGContext 沿着算法裁剪的路线前进,这对于较大的图像或多个图像会变得非常昂贵图片。
  • @JeremyH 我将它存储在本地。但是我应该如何编辑呢?
  • 跳转到 Photoshop 或同等软件中,然后裁剪它,使底部没有填充。或者,您可以通过增加画布高度来在图像顶部添加填充。
  • @JeremyH 实际图像中没有填充
  • @JeremyH 我可能限制它错了吗?

标签: ios swift uiimageview contentmode


【解决方案1】:

这是一个允许 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: OffVAlign: bottom 得到这个结果:

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

【讨论】:

  • 我可以为 aspectToFill 重构它吗?
  • @EdwardMordrake - 您可以通过将 if imgRatio &lt; 1.0 更改为 if imgRatio &gt; 1.0 来获得“Aspect Fill”...我使用更新的类编辑了我的答案,现在包含 aspectFill 作为布尔值 @IBInspectable财产。
  • 感谢您更新的答案,它就像一个魅力!现在我需要检查并了解它是如何工作的。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2019-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-05
    相关资源
    最近更新 更多