68480


`UICollectionView` を使って2カラムのリストを表示しようとしています。  
そのとき `UICollectionViewCell`
上のviewにAspectRatio制約とWidth制約を付け、Width制約の定数を操作することで高さが自動で決まるようしています。  
実行すると期待している見た目になってはいるのですがAutoLayoutのエラーが出てしまいそれを解決できないでいます。

再現コードを示します：

[code]

    import UIKit
    
    class MyColletionViewCell: UICollectionViewCell {
        var widthConstraint: NSLayoutConstraint!
        
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            let label = UILabel()
            label.translatesAutoresizingMaskIntoConstraints = false
            label.textAlignment = .center
            label.text = "TEST"
            contentView.addSubview(label)
            NSLayoutConstraint.activate([
                label.topAnchor.constraint(equalTo: contentView.topAnchor),
                label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
                label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
                label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
                // AspectRatio制約を付ける
                label.heightAnchor.constraint(equalTo: label.widthAnchor, multiplier: 2),
            ])
    
            // 幅を操作するためにWidth制約を付ける
            widthConstraint = NSLayoutConstraint(item: label,
                                                 attribute: .width,
                                                 relatedBy: .equal,
                                                 toItem: nil,
                                                 attribute: .notAnAttribute,
                                                 multiplier: 1,
                                                 constant: 100)
            widthConstraint.isActive = true
        }
        
        required init?(coder: NSCoder) {
            fatalError()
        }
    }
    
    class ViewController: UICollectionViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let layout = UICollectionViewFlowLayout()
            layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
            layout.minimumLineSpacing = 0
            layout.minimumInteritemSpacing = 0
            collectionView.collectionViewLayout = layout
            
            collectionView.backgroundColor = .white
            collectionView.register(MyColletionViewCell.self, forCellWithReuseIdentifier: "MyColletionViewCell")
        }
        
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 10
        }
        
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyColletionViewCell", for: indexPath) as! MyColletionViewCell
            // UICollectionViewの半分の幅を設定する
            cell.widthConstraint.constant = collectionView.frame.width / 2
            return cell
        }
    }
    
[/code]

エラーは次の通りです：

[code]

    [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. 
    (
        "<NSLayoutConstraint:0x6000001e4320 UILabel:0x7fac7a025dc0'TEST'.height == 2*UILabel:0x7fac7a025dc0'TEST'.width   (active)>",
        "<NSLayoutConstraint:0x6000001e41e0 V:|-(0)-[UILabel:0x7fac7a025dc0'TEST']   (active, names: '|':UIView:0x7fac7a025a90 )>",
        "<NSLayoutConstraint:0x6000001e4230 H:|-(0)-[UILabel:0x7fac7a025dc0'TEST']   (active, names: '|':UIView:0x7fac7a025a90 )>",
        "<NSLayoutConstraint:0x6000001e4280 UILabel:0x7fac7a025dc0'TEST'.trailing == UIView:0x7fac7a025a90.trailing   (active)>",
        "<NSLayoutConstraint:0x6000001e42d0 UILabel:0x7fac7a025dc0'TEST'.bottom == UIView:0x7fac7a025a90.bottom   (active)>",
        "<NSLayoutConstraint:0x6000001e4050 'UIView-Encapsulated-Layout-Height' UIView:0x7fac7a025a90.height == 50   (active)>",
        "<NSLayoutConstraint:0x6000001e4000 'UIView-Encapsulated-Layout-Width' UIView:0x7fac7a025a90.width == 50   (active)>"
    )
    
    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0x6000001e4320 UILabel:0x7fac7a025dc0'TEST'.height == 2*UILabel:0x7fac7a025dc0'TEST'.width   (active)>
    
    Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
    The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
    2020-07-10 23:54:54.920262+0900 SelfSizing[46764:1726414] [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. 
    (
        "<NSLayoutConstraint:0x6000001e4190 UILabel:0x7fac7a025dc0'TEST'.width == 187.5   (active)>",
        "<NSLayoutConstraint:0x6000001e4230 H:|-(0)-[UILabel:0x7fac7a025dc0'TEST']   (active, names: '|':UIView:0x7fac7a025a90 )>",
        "<NSLayoutConstraint:0x6000001e4280 UILabel:0x7fac7a025dc0'TEST'.trailing == UIView:0x7fac7a025a90.trailing   (active)>",
        "<NSLayoutConstraint:0x6000001e4000 'UIView-Encapsulated-Layout-Width' UIView:0x7fac7a025a90.width == 50   (active)>"
    )
    
    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0x6000001e4190 UILabel:0x7fac7a025dc0'TEST'.width == 187.5   (active)>
    
    Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
    The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
    
[/code]

よろしくお願いします。


CC BY-SA 4.0
2020-07-10T15:25:07.357
68477
2020-07-10T16:47:57.790
41045
question
swift ios uicollectionview uicollectionviewcell
Self-sizingなUICollectionViewCellを実装したときに出るAutoLayoutのエラーを解決したい


エラーメッセージに含まれる`UIView-Encapsulated-Layout-
Width`がよくわからないので、検索してみたところ、こんな記事が見つかりました。

[Auto-layout: What creates constraints named UIView-Encapsulated-Layout-Width
& Height?](https://stackoverflow.com/a/23910943/6541007)

どうも、`UIView-Encapsulated-Layout-
Width`と言うのは制約を付けた要素を取り囲んでいる要素のことで、今の場合`MyColletionViewCell`(の`contentView`)のことのようです。

で、`UICollectionViewCell`の場合は、幅:50, 高さ:50という暗黙の制約がついており、それが他の制約と矛盾してしまうみたいです。

承認された回答での回避策は`priority`を調整するということだったので、やってみました。

暗黙の制約がどこにどうかかっているかわからなかったので、自分で付ける制約の方を調整します。

[code]

        override init(frame: CGRect) {
            super.init(frame: frame)
    
            let label = UILabel()
            label.translatesAutoresizingMaskIntoConstraints = false
            label.textAlignment = .center
            label.text = "TEST"
            contentView.addSubview(label)
            NSLayoutConstraint.activate([
                label.topAnchor.constraint(equalTo: contentView.topAnchor),
                label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
                label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
                label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
                // AspectRatio制約を付ける
                label.heightAnchor.constraint(equalTo: label.widthAnchor, multiplier: 2),
            ].map {$0.priority = .defaultHigh;return $0}) //<-
    
            // 幅を操作するためにWidth制約を付ける
            widthConstraint = NSLayoutConstraint(item: label,
                                                 attribute: .width,
                                                 relatedBy: .equal,
                                                 toItem: nil,
                                                 attribute: .notAnAttribute,
                                                 multiplier: 1,
                                                 constant: 100)
            widthConstraint.priority = .defaultHigh //<-
            widthConstraint.isActive = true
        }
    
[/code]

他に何かうまいやり方があるかもしれませんが、とりあえずこれでエラーメッセージは表示されなくなり、所望の制約が正しく機能しているようです。


CC BY-SA 4.0
2020-07-10T16:47:57.790
68480
2020-07-10T16:47:57.790
13972
68477
answer