Sung
Sung

Reputation: 434

Issue with Layout Horizontal Anchors - Swift 3

Can anyone provide insight on why my code isn't working?

I'm trying to add horizontal spacing between the 2 views in the picture (Red and Black one) but for some reason I keep getting that error and I'm not sure how to fix it.

I'm assuming the other constraints are working correctly since I'm not seeing any issues, nor in the simulation or in the console.

enter image description here

Code as requested by user: https://gist.github.com/sungkim23/66ecd9ce71d0480f083bcd05caf0a58c

import UIKit

class BuyButton: UIView {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        //MARK: - View
        backgroundColor = UIColor(red: 180/255, green: 35/255, blue: 115/255, alpha: 1)
        layer.cornerRadius = 3

        //MARK: - Price View
        let priceView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: bounds.size.height))
        priceView.backgroundColor = UIColor.black
        addSubview(priceView)

        //MARK: - Purchase View
        let purchaseView = UIView(frame: CGRect(x: 80, y: 0, width: bounds.size.width - 80, height: bounds.size.height))
        purchaseView.backgroundColor = UIColor.red
        addSubview(purchaseView)

        //MARK: - Price Label
        let priceLabel = UILabel(frame: CGRect.zero)
        priceLabel.text = "$ 50.00"
        priceLabel.textAlignment = .center
        priceLabel.backgroundColor = .red
        priceLabel.translatesAutoresizingMaskIntoConstraints = false
        priceView.addSubview(priceLabel)

        //MARK: - Purchase Label
        let purchaseLabel = UILabel(frame: CGRect.zero)
        purchaseLabel.text = "Purchase"
        purchaseLabel.textAlignment = .center
        purchaseLabel.backgroundColor = .green
        purchaseLabel.translatesAutoresizingMaskIntoConstraints = false
        purchaseView.addSubview(purchaseLabel)

        //MARK: - View Constraints
        priceView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        priceView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        priceView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

        purchaseView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        purchaseView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        purchaseView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

        priceView.centerXAnchor.constraint(equalTo: purchaseView.centerXAnchor).isActive = true

        //MARK: - Label Constraints
        priceLabel.centerXAnchor.constraint(equalTo: priceView.centerXAnchor).isActive = true
        priceLabel.centerYAnchor.constraint(equalTo: priceView.centerYAnchor).isActive = true
        purchaseLabel.centerXAnchor.constraint(equalTo: purchaseView.centerXAnchor).isActive = true
        purchaseLabel.centerYAnchor.constraint(equalTo: purchaseView.centerYAnchor).isActive = true

        //MARK: - Constraint priorities

    }
}

UPDATE 1 - Added error to question as requested

2017-03-13 11:02:38.440892 ButtonTest[20954:1645749] [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. 
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
"<NSAutoresizingMaskLayoutConstraint:0x60000009a400 h=--& v=--& UIView:0x7fe55be017b0.midX == 40   (active)>",
"<NSAutoresizingMaskLayoutConstraint:0x600000099ff0 h=--& v=--& UIView:0x7fe55bc09310.midX == 232   (active)>",
"<NSLayoutConstraint:0x600000099c30 UIView:0x7fe55be017b0.centerX == UIView:0x7fe55bc09310.centerX   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600000099c30 UIView:0x7fe55be017b0.centerX ==    UIView:0x7fe55bc09310.centerX   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to   catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

UPDATE 2 - tl;dr

Use
yourView.leadingAnchor.constraint(equalTo: yourOtherView.trailingAnchor).isActive = true

instead of

yourView.centerXAnchor.constraint(equalTo: yourOtherView.centerXAnchor).isActive = true

Upvotes: 0

Views: 4279

Answers (2)

Fogmeister
Fogmeister

Reputation: 77651

OK, from your comment on Matt's question. You're still not thinking in constraints :D

You don't need a bounds.size.width - 80 that is a frame and rect thing to think about. It is not an AutoLayout thing to think about.

In order to think about layouts in AutoLayout you need to be describing the layout to the app.

This is what I think you want...

  • The width of price view should be fixed at 80.
  • The leading, top, and bottom edges of price view will have 0 space to the leading, top, and bottom edges of the super view respectively.
  • The leading edge of purchase view should have 0 space to the trailing edge of price view.
  • The trailing, top, and bottom edges of purchase view should have 0 space to the trailing, top, and bottom edges of the super view.

From this list there is only one possible layout for the price view and purchase view. Any other layout would contradict the constraints. Also, I haven't added anything I don't need. For instance, the centerYAnchor of both views will be the same by following these instructions. I don't need to explicitly add that.

So now you have this very simple description... just add it.

// set the width of price view
priceView.widthAnchor.constraint(equalToConstant: 80).isActive = true
// now add the other edges to super view
priceView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
priceView.topAnchor.constraint(equalTo: topAnchor).isActive = true
priceView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

// set the spacing between price view and purchase view
purchaseView.leadingAnchor.constraint(equalTo: priceView.trailingAnchor).isActive = true
// add the other edges to the super view
purchaseView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
purchaseView.topAnchor.constraint(equalTo: topAnchor).isActive = true
purchaseView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

That should be it (for the price view and purchase view).

Don't try to overcomplicate your task when using AutoLayout. A lot of the work is done for you by AutoLayout.

Edit - UIStackView

As per @abizern's comment on your question. You should really check out UIStackView. It takes away a lot of this pain and makes layouts using auto layout much easier.

Upvotes: 4

matt
matt

Reputation: 535547

Problem 1: You never said

priceView.translatesAutoresizingMaskIntoConstraints = false

Problem 2: You never said

purchaseView.translatesAutoresizingMaskIntoConstraints = false

Problem 3: You need a complete set of constraints on priceView and purchaseView.

In other words, you cannot mix and match like this, using frame for some views and autolayout for others. Once a view is involved in autolayout, it is completely involved in autolayout and must be entirely positioned and sized by autolayout.

So, if you fix problem 1 and problem 2, no more console errors — but then you will find that the positions / sizes of the views go crazy, because you have gone from overdetermined layout to underdetermined layout. Now you need to fill in the missing constraints to determine that layout. In particular:

  • The black view's width will need to be determined (priceView).

  • The red view's width and x-position will need to be determined (purchaseView).

Upvotes: 2

Related Questions