Alwin Vazhappilly
Alwin Vazhappilly

Reputation: 1514

How to resolve : Unable to simultaneously satisfy constraints

I want to create like this ( Done through storyboard )

enter image description here

This Done through storyboard. In my observation I found the constrain problem arise from "totalAmountImg". I don't change the priority property of all objects (all have priority == 1000)

This is the result. (SummaryVC - Done this programatically) enter image description here

NOTE : I want to make the SummaryVC totalAmountImg ( Programatically ) look like above image (Made by storyboard)

Stroryboard constraints for totalAmountImg

enter image description here

This exactly I create through code. But I don't know why this error pop out.

totalImgConstraint

func totalAmountImgConstraints() {
        let imgCenterY = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .centerY,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .centerY,
                           multiplier: 1.0,
                           constant: 0)
        imgCenterY.identifier = "imgCenterY"
        imgCenterY.isActive = true

        let imgRight = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .right,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .right,
                           multiplier: 1.0,
                           constant: 16)
        imgRight.identifier = "imgRight"
        imgRight.isActive = true

        let imgAspectRatio = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .height,
                           relatedBy: .equal,
                           toItem: totalAmountImg,
                           attribute: .width,
                           multiplier: 1.0 / 1.0,
                           constant: 0)
        imgAspectRatio.identifier = "imgAspectRatio"
        imgAspectRatio.isActive = true

        let imgLeft = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .left,
                           relatedBy: .equal,
                           toItem: totalPriceLbl,
                           attribute: .right,
                           multiplier: 1.0,
                           constant: 4)
        imgLeft.identifier = "imgLeft"
        imgLeft.isActive = true

        let imgWidth = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .width,
                           relatedBy: .lessThanOrEqual,
                           toItem: totalAmountView,
                           attribute: .width,
                           multiplier: 0.2,
                           constant: 0)
        imgWidth.identifier = "imgWidth"
        imgWidth.isActive = true
    }

SummaryVC

class SummaryVC: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var pageController: UIPageControl!

    let numberOfPages = 3

    override func viewDidLoad() {
        super.viewDidLoad()
        basicDesignSetup()

        // Add subViews
        scrollView.addSubview(summaryBaseView)
        scrollView.addSubview(paymentTypeBaseView)
        scrollView.addSubview(itemNameBaseView)

        summaryBaseView.addSubview(totalAmountView)
        summaryBaseView.addSubview(runningTableView)
        summaryBaseView.addSubview(partnerAmountView)

        totalAmountView.addSubview(totalLbl)
        totalAmountView.addSubview(totalPriceLbl)
        totalAmountView.addSubview(totalAmountImg)


        view.setNeedsUpdateConstraints()

        // set content size
        scrollView.contentSize = CGSize(width: scrollView.frame.width * CGFloat(numberOfPages), height: scrollView.frame.height)
    }

    override func viewDidLayoutSubviews() {

    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let pageNumber = scrollView.contentOffset.x / scrollView.frame.width
        pageController.currentPage = Int(pageNumber)
        pageController.currentPageIndicatorTintColor = UIColor.white
    }

    //-----------------------------------------------------------------
    // MARK: - Methods
    //-----------------------------------------------------------------

    func basicDesignSetup() {
        pageController.numberOfPages = numberOfPages
        let scrollViewSize = scrollView.frame.size
        let scrollViewWidth = scrollView.frame.width

        // Summary View
        let summeryOrigin = CGPoint(x: 0, y: 0)
        summaryBaseView = UIView(frame: CGRect(origin: summeryOrigin, size: scrollViewSize))
        summaryBaseView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)

        // Payment Type View
        let paymentTypeOrigin = CGPoint(x: scrollViewWidth, y: 0)
        paymentTypeBaseView = UIView(frame: CGRect(origin: paymentTypeOrigin, size: scrollViewSize))
        paymentTypeBaseView.backgroundColor = #colorLiteral(red: 0.01680417731, green: 0.1983509958, blue: 1, alpha: 1)

        // Item Name View
        let itemNameOrigin = CGPoint(x: scrollViewWidth * 2, y: 0)
        itemNameBaseView = UIView(frame: CGRect(origin: itemNameOrigin, size: scrollViewSize))
        itemNameBaseView.backgroundColor = #colorLiteral(red: 0, green: 0.9914394021, blue: 1, alpha: 1)

        // Total Amount View
        totalAmountView.backgroundColor = #colorLiteral(red: 0.5480614305, green: 0.8129847646, blue: 0.6160266995, alpha: 1)
        runningTableView.backgroundColor = #colorLiteral(red: 0.4280827343, green: 0.7700845003, blue: 0.9571052194, alpha: 1)
        partnerAmountView.backgroundColor = #colorLiteral(red: 0.9137254902, green: 0.4470588235, blue: 0.4549019608, alpha: 1)

        totalLbl.text = "Total Amount"
        totalLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
        totalLbl.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
        totalLbl.font = UIFont.systemFont(ofSize: 16, weight: .medium)

        totalPriceLbl.text = "5532.00"
        totalPriceLbl.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
        totalPriceLbl.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)

        totalAmountImg.contentMode = .scaleAspectFit
        totalAmountImg.image = #imageLiteral(resourceName: "tick")
        totalAmountImg.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)

    }


    //-----------------------------------------------------------------
    // MARK: - Views - programatically
    //-----------------------------------------------------------------

    // Base Views
    lazy var summaryBaseView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var paymentTypeBaseView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var itemNameBaseView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    // $$$$$ Summary Sub Views $$$$$

    // Total Amount
    lazy var totalAmountView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var totalLbl: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    lazy var totalPriceLbl: UnderlinedLabel = {
        let label = UnderlinedLabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    lazy var totalAmountImg: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()

    // Running Table
    lazy var runningTableView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var runningLbl: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    lazy var runningPriceLbl: UnderlinedLabel = {
        let label = UnderlinedLabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    lazy var runningTableImg: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()

    // Partner Amount
    lazy var partnerAmountView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var partnerLbl: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    lazy var partnerPriceLbl: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    lazy var partnerAmountImg: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()



    //-----------------------------------------------------------------
    // MARK: - Constraints
    //-----------------------------------------------------------------

    override func updateViewConstraints() {
        totalAmountViewConstraints()
        runningTableViewConstraints()
        partnerAmountViewConstraints()

        // Total Amount View
        totalLblConstraint()
        totalPriceLblConstraints()
        totalAmountImgConstraints()

        super.updateViewConstraints()
    }

    func totalAmountViewConstraints() {
        NSLayoutConstraint(item: totalAmountView,
                           attribute: .left,
                           relatedBy: .equal,
                           toItem: summaryBaseView,
                           attribute: .left,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalAmountView,
                           attribute: .right,
                           relatedBy: .equal,
                           toItem: summaryBaseView,
                           attribute: .right,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalAmountView,
                           attribute: .top,
                           relatedBy: .equal,
                           toItem: summaryBaseView,
                           attribute: .top,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalAmountView,
                           attribute: .bottom,
                           relatedBy: .equal,
                           toItem: runningTableView,
                           attribute: .top,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalAmountView,
                           attribute: .height,
                           relatedBy: .equal,
                           toItem: runningTableView,
                           attribute: .height,
                           multiplier: 1.0,
                           constant: 0).isActive = true
    }

    func runningTableViewConstraints() {
        NSLayoutConstraint(item: runningTableView,
                           attribute: .leading,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .leading,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: runningTableView,
                           attribute: .trailing,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .trailing,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: runningTableView,
                           attribute: .top,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .bottom,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: runningTableView,
                           attribute: .bottom,
                           relatedBy: .equal,
                           toItem: partnerAmountView,
                           attribute: .top,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: runningTableView,
                           attribute: .height,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .height,
                           multiplier: 1.0,
                           constant: 0).isActive = true
    }

    func partnerAmountViewConstraints() {
        NSLayoutConstraint(item: partnerAmountView,
                           attribute: .leading,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .leading,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: partnerAmountView,
                           attribute: .trailing,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .trailing,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: partnerAmountView,
                           attribute: .top,
                           relatedBy: .equal,
                           toItem: runningTableView,
                           attribute: .bottom,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: partnerAmountView,
                           attribute: .bottom,
                           relatedBy: .equal,
                           toItem: summaryBaseView,
                           attribute: .bottom,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: partnerAmountView,
                           attribute: .height,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .height,
                           multiplier: 1.0,
                           constant: 0).isActive = true
    }

    // Total Ammount Section Subviews
    func totalLblConstraint() {
        NSLayoutConstraint(item: totalLbl,
                           attribute: .top,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .top,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalLbl,
                           attribute: .left,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .left,
                           multiplier: 1.0,
                           constant: 16).isActive = true

        NSLayoutConstraint(item: totalLbl,
                           attribute: .bottom,
                           relatedBy: .equal,
                           toItem: totalPriceLbl,
                           attribute: .top,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalLbl,
                           attribute: .height,
                           relatedBy: .equal,
                           toItem: totalPriceLbl,
                           attribute: .height,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalLbl,
                           attribute: .width,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .width,
                           multiplier: 0.7,
                           constant: 0).isActive = true
    }

    func totalPriceLblConstraints() {
        NSLayoutConstraint(item: totalPriceLbl,
                           attribute: .top,
                           relatedBy: .equal,
                           toItem: totalLbl,
                           attribute: .bottom,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalPriceLbl,
                           attribute: .leading,
                           relatedBy: .equal,
                           toItem: totalLbl,
                           attribute: .leading,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalPriceLbl,
                           attribute: .bottom,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .bottom,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalPriceLbl,
                           attribute: .height,
                           relatedBy: .equal,
                           toItem: totalLbl,
                           attribute: .height,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalPriceLbl,
                           attribute: .width,
                           relatedBy: .equal,
                           toItem: totalLbl,
                           attribute: .width,
                           multiplier: 1.0,
                           constant: 0).isActive = true

        NSLayoutConstraint(item: totalPriceLbl,
                           attribute: .trailing,
                           relatedBy: .equal,
                           toItem: totalLbl,
                           attribute: .trailing,
                           multiplier: 1.0,
                           constant: 0).isActive = true
    }

    func totalAmountImgConstraints() {
        let imgCenterY = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .centerY,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .centerY,
                           multiplier: 1.0,
                           constant: 0)
        imgCenterY.identifier = "imgCenterY"
        imgCenterY.isActive = true

        let imgRight = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .right,
                           relatedBy: .equal,
                           toItem: totalAmountView,
                           attribute: .right,
                           multiplier: 1.0,
                           constant: 16)
        imgRight.identifier = "imgRight"
        imgRight.isActive = true

        let imgAspectRatio = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .height,
                           relatedBy: .equal,
                           toItem: totalAmountImg,
                           attribute: .width,
                           multiplier: 1.0 / 1.0,
                           constant: 0)
        imgAspectRatio.identifier = "imgAspectRatio"
        imgAspectRatio.isActive = true

        let imgLeft = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .left,
                           relatedBy: .equal,
                           toItem: totalPriceLbl,
                           attribute: .right,
                           multiplier: 1.0,
                           constant: 4)
        imgLeft.identifier = "imgLeft"
        imgLeft.isActive = true

        let imgWidth = NSLayoutConstraint(item: totalAmountImg,
                           attribute: .width,
                           relatedBy: .lessThanOrEqual,
                           toItem: totalAmountView,
                           attribute: .width,
                           multiplier: 0.2,
                           constant: 0)
        imgWidth.identifier = "imgWidth"
        imgWidth.isActive = true
    }



    //-----------------------------------------------------------------
    // MARK: - Actions
    //-----------------------------------------------------------------

    @IBAction func pageChange(_ sender: UIPageControl) {
        let x = CGFloat(sender.currentPage) * scrollView.frame.width
        scrollView.contentOffset = CGPoint(x: x, y: 0)
        pageController.currentPageIndicatorTintColor = UIColor.white
    }

}


extension NSLayoutConstraint {


    override open var description: String {
        let id = identifier ?? ""
        return "id: \(id), constant: \(constant)" //you may print whatever you want here
    }
}

LOG

2018-01-12 16:27:27.934408+0530 Cafe Point[13577:258401] [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) 
(
    "id: , constant: 343.0",
    "id: , constant: 0.0",
    "id: , constant: 0.0",
    "id: , constant: 16.0",
    "id: , constant: 0.0",
    "id: , constant: 0.0",
    "id: , constant: 0.0",
    "id: imgLeft, constant: 4.0",
    "id: imgRight, constant: 16.0",
    "id: imgWidth, constant: 0.0"
)

Will attempt to recover by breaking constraint 
id: imgLeft, constant: 4.0

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.

Upvotes: 1

Views: 97

Answers (3)

Shehata Gamal
Shehata Gamal

Reputation: 100503

To correctly understand layout keep in mind when you give a view leading/left and trailing/right constraints then you don't have to give it a width , as if you're pinning a robe to two sides , if you want to give it a width then give it a centerX constraint , Same also applied to top,bottom then don't have to give height and if you want to give height then add centerY constraint , off course you can give a view leading , trailing and width constraints at the same time but to make sure they fit together

Upvotes: 0

Bista
Bista

Reputation: 7893

As the Warning states:

Will attempt to recover by breaking constraint id: imgLeft, constant: 4.0

You are giving Left and Right constraint to Image View. But also assigning Width. It confuses the Image View since Left and Right Constraints are enough to satisfy Width requirement for the Image View.

Hence you can use two combinations: left/width or right/width.

You can use other combinations too but for that, we have to dig into Priority of constraints.

Upvotes: 0

Upholder Of Truth
Upholder Of Truth

Reputation: 4711

Try changing to this:

    let imgRight = NSLayoutConstraint(item: totalAmountImg,
                       attribute: .right,
                       relatedBy: .equal,
                       toItem: totalAmountView,
                       attribute: .right,
                       multiplier: 1.0,
                       constant: -16)
    imgRight.identifier = "imgRight"
    imgRight.isActive = true

(note the 16 changes to -16)

You have to be careful when comparing constraints from the storyboard to ones created programatically because the order matters.

Upvotes: 3

Related Questions