0

I have a table cell view that consists of a couple of buttons and some details. The details are hidden until one of the buttons is touched, and removed when the other button is touched. I am using auto layout.

Here is the code for adding the controls and configuring constraints (controls have been renamed to protect my client). Code is C# but equivalent Obj-C/Swift should be fairly obvious:

this.ContentView.AddSubviews(this.firstButton, this.secondButton);
this.detailView.AddSubviews(this.textField1, this.textField2, this.textField3, this.textField4, this.textField5);

this.ContentView.ConstrainLayout(() =>
    this.firstButton.Top() == this.ContentView.Top() + Layout.StandardSiblingViewSpacing &&
    this.firstButton.Bottom() <= this.ContentView.Bottom() - Layout.StandardSiblingViewSpacing &&
    this.firstButton.Left() == this.ContentView.Left() + Layout.StandardSuperviewSpacing &&
    this.secondButton.Top() == this.firstButton.Top() &&
    this.secondButton.Bottom() <= this.ContentView.Bottom() - Layout.StandardSiblingViewSpacing &&
    this.secondButton.Left() == this.firstButton.Left());

this.detailView.ConstrainLayout(() =>
    this.textField1.Top() == this.detailView.Top() &&
    this.textField1.Left() == this.detailView.Left() &&
    this.textField1.Right() == this.detailView.Right() &&
    this.textField2.Top() == this.textField1.Bottom() + Layout.StandardSiblingViewSpacing &&
    this.textField2.Left() == this.textField1.Left() &&
    this.textField2.Right() == this.textField1.Right() &&
    this.textField3.Top() == this.textField2.Bottom() + Layout.StandardSiblingViewSpacing &&
    this.textField3.Left() == this.textField2.Left() &&
    this.textField3.Right() == this.textField2.Right() &&
    this.textField4.Top() == this.textField3.Bottom() + Layout.StandardSiblingViewSpacing &&
    this.textField4.Left() == this.textField3.Left() &&
    this.textField4.Right() == this.textField3.Right() &&
    this.textField5.Top() == this.textField4.Bottom() + Layout.StandardSiblingViewSpacing &&
    this.textField5.Bottom() <= this.detailView.Bottom() - Layout.StandardSiblingViewSpacing &&
    this.textField5.Left() == this.textField4.Left() &&
    this.textField5.Right() == this.textField4.Right());

The code to animate in the detailView looks like this:

private void AnimateDetailsIn()
{
    this.ContentView.AddSubview(this.detailView);
    this.ContentView.ConstrainLayout(() =>
        this.detailView.Top() == this.ContentView.Top() + Layout.StandardSiblingViewSpacing &&
        this.detailView.Bottom() <= this.ContentView.Bottom() - Layout.StandardSiblingViewSpacing &&
        this.detailView.Left() == this.secondButton.Right() + Layout.StandardSiblingViewSpacing &&
        this.detailView.Right() == this.ContentView.Right() - Layout.StandardSuperviewSpacing);

    var tableView = this.FindParent<UITableView>();

    if (tableView == null)
    {
        return;
    }

    tableView.BeginUpdates();
    tableView.EndUpdates();
}

Whenever AnimateDetailsIn is called everything looks perfectly fine on screen, but I see this error message in the output:

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) 
(
    "<NSLayoutConstraint:0x7fccb670 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7fd8efd0(36)]>",
    "<NSLayoutConstraint:0x7fd94330 V:|-(0)-[UITextField:0x7fd905a0]   (Names: '|':UIView:0x7fd90050 )>",
    "<NSLayoutConstraint:0x7fd943c0 V:[UITextField:0x7fd905a0]-(8)-[UITextField:0x7fd90e90]>",
    "<NSLayoutConstraint:0x7fd94450 V:[UITextField:0x7fd90e90]-(8)-[UITextField:0x7fd91720]>",
    "<NSLayoutConstraint:0x7fd944e0 V:[UITextField:0x7fd91720]-(8)-[UITextField:0x7fd91fb0]>",
    "<NSLayoutConstraint:0x7fd94570 V:[UITextField:0x7fd91fb0]-(8)-[UITextField:0x7fd92850]>",
    "<NSLayoutConstraint:0x7fd945a0 UITextField:0x7fd92850.bottom <= UIView:0x7fd90050.bottom - 8>",
    "<NSLayoutConstraint:0x7fdaa080 V:|-(8)-[UIView:0x7fd90050]   (Names: '|':UITableViewCellContentView:0x7fd8efd0 )>",
    "<NSLayoutConstraint:0x7fdaa200 UIView:0x7fd90050.bottom <= UITableViewCellContentView:0x7fd8efd0.bottom - 8>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fd94570 V:[UITextField:0x7fd91fb0]-(8)-[UITextField:0x7fd92850]>

To troubleshoot, I tried including detailView in the view hierarchy from the get-go and removing the animations. This worked fine (no error output and everything displayed correctly). So clearly there's something not quite right about how I'm going about things here. Can anyone tell me how to animate my detailView in using auto layout without getting the error output?

me--
  • 1,412
  • 13
  • 36

2 Answers2

0

After adding the subviews to the contentview you need to set 'translatesautoresizingmaskintoconstraints' to NO on each of the subviews. Are you doing that somewhere? Otherwise the system would add its own bunch of constraints that you don't want. Can you try this if you haven't already

indrajit
  • 99
  • 4
  • This happens automatically as part of my `ConstrainLayout` logic. – me-- May 26 '15 at 01:38
  • BTW, as I mentioned in the question, everything works fine if I include the `detailView` in the `ContentView` instead of animating it in. – me-- May 26 '15 at 01:49
0

To answer my own question, the problem boils down to the interaction between the following highlighted constraints:

enter image description here

The top two are added automatically by (I think) UITableViewCell. They are of the highest priority (1000). The other two highlighted constraints are the first two (top and bottom) I added with this code:

this.ContentView.ConstrainLayout(() =>
        this.detailView.Top() == this.ContentView.Top() + Layout.StandardSiblingViewSpacing &&
        this.detailView.Bottom() <= this.ContentView.Bottom() - Layout.StandardSiblingViewSpacing &&
        this.detailView.Left() == this.secondButton.Right() + Layout.StandardSiblingViewSpacing &&
        this.detailView.Right() == this.ContentView.Right() - Layout.StandardSuperviewSpacing);

The problem is that my constraints are added with the highest priority too. Since my constraints conflict (momentarily) with the constraints imposed automatically by UITableViewCell, I get the constraint error.

The "solution" (which I think is a bit of a hack) was to lower the priority of my constraints so that there is no conflict.

Whilst this solves the problem, it does change the nature of the animation performed when my detail view is revealed. Instead of my changes "expanding" in place, they slide in from top to bottom. This makes sense when you think about it - my detail view is no longer being constrained to the size of the content view because of the lowered priority.

Alas, I don't know a way around this yet so will have to make do with the subpar animation for now.

PS. Thanks to Reuben Scratton who got me pointed in the right direction with this post.

Community
  • 1
  • 1
me--
  • 1,412
  • 13
  • 36