0

我想将图像放入应该具有特定纵横比的矩形内。不管它是什么,它都应该找到一个适合矩形的形式。我在故事板中玩耍并得到了这个:

在此处输入图像描述

带有虚线边框的优先级较低(250)。这在故事板中有效。但是,我需要以编程方式创建这些约束,所以我这样尝试(我使用的是SnapKit,它只是提供了更好AutoLayout的语法。它应该是不言自明的):

let topView = UIView()
topView.translatesAutoresizingMaskIntoConstraints = false
topView.backgroundColor = .gray
view.addSubview(topView)

topView.snp.makeConstraints { (make) in
        make.top.equalToSuperview()
        make.left.equalToSuperview()
        make.trailing.equalToSuperview()
        make.height.equalTo(250)
        }

// This view should have a specific aspect ratio and fit inside topView
let holderView = UIView()
holderView.translatesAutoresizingMaskIntoConstraints = false
holderView.backgroundColor = .red
topView.addSubview(holderView)

holderView.snp.makeConstraints { (make) in
        make.center.equalToSuperview() // If I remove this one, there's no auto-layout issue, but then it's offset
        make.edges.equalToSuperview().priority(250) // sets leading, trailing, top and bottom
        make.edges.greaterThanOrEqualToSuperview().priority(1000)
        make.width.equalTo(holderView.snp.height).multipliedBy(3/2)
        }

如果您将其粘贴到一个空的 ViewController 中并启动它,您会遇到以下问题:

2018-03-16 15:38:50.188867+0100 DemoProject[11298:850932] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: 
    (1) look at each constraint and try to figure out which you don't expect; 
    (2) find the code that added the unwanted constraint or constraints and fix it. 

"<SnapKit.LayoutConstraint:0x6000000a7c80@ViewController.swift#24 UIView:0x7fcd82d12440.left == UIView:0x7fcd82d12640.left>",
"<SnapKit.LayoutConstraint:0x6000000a7ce0@ViewController.swift#25 UIView:0x7fcd82d12440.trailing == UIView:0x7fcd82d12640.trailing>",
"<SnapKit.LayoutConstraint:0x6000000a7d40@ViewController.swift#26 UIView:0x7fcd82d12440.height == 250.0>",
"<SnapKit.LayoutConstraint:0x6000000a7da0@ViewController.swift#35 UIView:0x7fcd8580dad0.centerX == UIView:0x7fcd82d12440.centerX>",
"<SnapKit.LayoutConstraint:0x6000000a7e00@ViewController.swift#35 UIView:0x7fcd8580dad0.centerY == UIView:0x7fcd82d12440.centerY>",
"<SnapKit.LayoutConstraint:0x6000000a8580@ViewController.swift#37 UIView:0x7fcd8580dad0.top >= UIView:0x7fcd82d12440.top>",
"<SnapKit.LayoutConstraint:0x6000000a8a60@ViewController.swift#37 UIView:0x7fcd8580dad0.right >= UIView:0x7fcd82d12440.right>",
"<SnapKit.LayoutConstraint:0x6000000a9360@ViewController.swift#38 UIView:0x7fcd8580dad0.width == UIView:0x7fcd8580dad0.height>",
"<NSLayoutConstraint:0x600000092cf0 'UIView-Encapsulated-Layout-Width' UIView:0x7fcd82d12640.width == 414   (active)>"


Will attempt to recover by breaking constraint <SnapKit.LayoutConstraint:0x6000000a9360@ViewController.swift#38 UIView:0x7fcd8580dad0.width == UIView:0x7fcd8580dad0.height>

当我删除 centering constraint 时,这不会出现make.center.equalToSuperview()。但后来,它错位了。

故事板和我的代码有什么不同?我真的不明白这一点。我也尝试使用默认的 swift 语法,结果完全一样。所以我认为这不是问题SnapKit

有任何想法吗?谢谢你们的帮助。如果您需要更多信息,请告诉我。

编辑:我混淆了一些东西。这与图像及其纵横比无关。它只是UIView在适合矩形时应保持特定的纵横比。实际图像将被放入其中holderView。对不起

4

1 回答 1

1

好的 - 这是一种方法。

取子视图的“原生”大小,计算“纵横比”比例——即适合父视图的宽度或高度的比例,并适当地缩放其他维度。

然后,使用centerXAnchorcenterYAnchor来定位子视图,并widthAnchor调整heightAnchor它的大小。

注意:如果您尝试放置图像,请根据图像大小计算宽高比大小,将图像放入图像视图中,将图像视图缩放模式设置为fill,最后将约束应用于图像视图。

您应该能够按原样运行此示例。只需使用顶部的“本机”大小值,看看它如何将子视图适合超级视图。

public class AspectFitViewController : UIViewController {

    // "native" size for the holderView
    let hViewWidth: CGFloat = 700.0
    let hViewHeight: CGFloat = 200.0

    let topView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = UIColor.blue
        return v
    }()

    let holderView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = UIColor.cyan
        return v
    }()

    public override func viewDidLoad() {
        super.viewDidLoad()
        view.bounds = CGRect(x: 0, y: 0, width: 400, height: 600)
        view.backgroundColor = .yellow

        // add topView
        view.addSubview(topView)

        // pin topView to leading / top / trailing
        topView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0).isActive = true
        topView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0).isActive = true
        topView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0).isActive = true

        // explicit height for topView
        topView.heightAnchor.constraint(equalToConstant: 250.0).isActive = true

        // add holderView to topView
        topView.addSubview(holderView)

        // center X and Y
        holderView.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0.0).isActive = true
        holderView.centerYAnchor.constraint(equalTo: topView.centerYAnchor, constant: 0.0).isActive = true

        // holderView's width and height will be calculated in viewDidAppear
        // after topView has been laid-out by the auto-layout engine

    }

    public override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let aspectWidth  = topView.bounds.size.width / hViewWidth
        let aspectHeight = topView.bounds.size.height / hViewHeight

        let aspectFit = min(aspectWidth, aspectHeight)

        let newWidth = hViewWidth * aspectFit
        let newHeight = hViewHeight * aspectFit

        holderView.widthAnchor.constraint(equalToConstant: newWidth).isActive = true
        holderView.heightAnchor.constraint(equalToConstant: newHeight).isActive = true

    }

}

编辑:

澄清后......这只能通过约束来完成。关键是“优先级1000”topleading约束必须.greaterThanOrEqual为零,bottomtrailing约束必须.lessThanOrEqual为零。

public class AspectFitViewController : UIViewController {

    // "native" size for the holderView
    let hViewWidth: CGFloat = 700.0
    let hViewHeight: CGFloat = 200.0

    let topView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = UIColor.blue
        return v
    }()

    let holderView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = UIColor.cyan
        return v
    }()

    public override func viewDidLoad() {
        super.viewDidLoad()
        view.bounds = CGRect(x: 0, y: 0, width: 400, height: 600)
        view.backgroundColor = .yellow

        // add topView
        view.addSubview(topView)

        // pin topView to leading / top / trailing
        topView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0).isActive = true
        topView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0).isActive = true
        topView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0).isActive = true

        // explicit height for topView
        topView.heightAnchor.constraint(equalToConstant: 250.0).isActive = true

        // add holderView to topView
        topView.addSubview(holderView)

        // center X and Y
        holderView.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0.0).isActive = true
        holderView.centerYAnchor.constraint(equalTo: topView.centerYAnchor, constant: 0.0).isActive = true

        // aspect ratio size
        holderView.widthAnchor.constraint(equalTo: holderView.heightAnchor, multiplier: hViewWidth / hViewHeight).isActive = true

        // two constraints for each side...
        // the .equal constraints need .defaultLow priority
        // top and leading constraints must be .greaterThanOrEqual to 0
        // bottom and trailing constraints must be .lessThanOrEqual to 0

        let topA = NSLayoutConstraint(item: holderView, attribute: .top, relatedBy: .greaterThanOrEqual, toItem: topView, attribute: .top, multiplier: 1.0, constant: 0.0)
        let topB = NSLayoutConstraint(item: holderView, attribute: .top, relatedBy: .equal, toItem: topView, attribute: .top, multiplier: 1.0, constant: 0.0)

        let bottomA = NSLayoutConstraint(item: holderView, attribute: .bottom, relatedBy: .lessThanOrEqual, toItem: topView, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        let bottomB = NSLayoutConstraint(item: holderView, attribute: .bottom, relatedBy: .equal, toItem: topView, attribute: .bottom, multiplier: 1.0, constant: 0.0)

        let leadingA = NSLayoutConstraint(item: holderView, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: topView, attribute: .leading, multiplier: 1.0, constant: 0.0)
        let leadingB = NSLayoutConstraint(item: holderView, attribute: .leading, relatedBy: .equal, toItem: topView, attribute: .leading, multiplier: 1.0, constant: 0.0)

        let trailingA = NSLayoutConstraint(item: holderView, attribute: .trailing, relatedBy: .lessThanOrEqual, toItem: topView, attribute: .trailing, multiplier: 1.0, constant: 0.0)
        let trailingB = NSLayoutConstraint(item: holderView, attribute: .trailing, relatedBy: .equal, toItem: topView, attribute: .trailing, multiplier: 1.0, constant: 0.0)

        topB.priority = .defaultLow
        bottomB.priority = .defaultLow
        leadingB.priority = .defaultLow
        trailingB.priority = .defaultLow

        NSLayoutConstraint.activate([
            topA, topB,
            bottomA, bottomB,
            leadingA, leadingB,
            trailingA, trailingB
            ])

    }

}
于 2018-03-16T18:05:55.417 回答