简体   繁体   中英

How can I program iOS AutoLayout constraints using Greater Than or Equal without making the layout ambiguous?

I am writing an iOS app using MonoTouch where I want to programmatically define all of my UIViews and the AutoLayout constraints that define their layout. I am currently trying to create a Table-like collection of views (rows & columns, not a UITableView) and I am able to use AutoLayout constraints to adjust column and row "borders" or markers. It looks fine when I run the app, but I keep getting an ambiguous layout for the table (using the HasAmbiguousLayout field). I have distilled it down to the fact that my border markers use GreaterThanOrEqual (GTE) constraints rather than Equal, which is required since I want to make sure my rows and columns fit an arbitrary number of views.

My understanding was than I can use GTE type constraints and the AutoLayout system will attempt to minimize the value so that it is still GTE (vice-versa for LTE constraints). For example, I would expect that I can place a view's left egde GTE the right edges of 2 other views, so that the layout solver will determine the minimum value for the left edge such that it is still satisfying GTE the right edges of the other views.

I created a simple example and I am not sure why I get an ambiguous layout. I created a single-view project in MonoTouch and programmatically add a UIView box that serves as my container (which in more complex examples contains multiple rows and columns of views). I then add only 1 subview to the box view, a UILabel called myLabel. I add constraints to place the label within the box, place the box within the parent View, and set the box's right and bottom edges (this is the key part):

public partial class AutoLayoutTestViewController : UIViewController
{
    public AutoLayoutTestViewController () : base ("AutoLayoutTestViewController", null)
    {
        UIView box = new UIView ();
        box.BackgroundColor = UIColor.Green;
        box.TranslatesAutoresizingMaskIntoConstraints = false;
        View.AddSubview (box);
        View.AddConstraint (NSLayoutConstraint.Create (box, NSLayoutAttribute.Top, NSLayoutRelation.Equal, View, NSLayoutAttribute.Top, 1.0f, 50f));
        View.AddConstraint (NSLayoutConstraint.Create (box, NSLayoutAttribute.Left, NSLayoutRelation.Equal, View, NSLayoutAttribute.Left, 1.0f, 0f));

        UILabel myLabel = new UILabel ();
        myLabel.Text = "My Label";
        myLabel.BackgroundColor = UIColor.Red;
        myLabel.TranslatesAutoresizingMaskIntoConstraints = false;
        box.AddSubview (myLabel);
        box.AddConstraint (NSLayoutConstraint.Create (myLabel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, box, NSLayoutAttribute.Top, 1.0f, 10f));
        box.AddConstraint (NSLayoutConstraint.Create (myLabel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, box, NSLayoutAttribute.Left, 1.0f, 10f));
        // IMPORTANT CONSTRAINTS
        box.AddConstraint (NSLayoutConstraint.Create (box, NSLayoutAttribute.Bottom, NSLayoutRelation.GreaterThanOrEqual, myLabel, NSLayoutAttribute.Bottom, 1.0f, 10f));
        box.AddConstraint (NSLayoutConstraint.Create (box, NSLayoutAttribute.Right, NSLayoutRelation.GreaterThanOrEqual, myLabel, NSLayoutAttribute.Right, 1.0f, 10f));
    }

    public override void DidReceiveMemoryWarning ()
    {
        // Releases the view if it doesn't have a superview.
        base.DidReceiveMemoryWarning ();

        // Release any cached data, images, etc that aren't in use.
    }

    public override void ViewDidLayoutSubviews ()
    {
        base.ViewDidLayoutSubviews ();
        foreach (UIView v in View.Subviews) {
            if (v.HasAmbiguousLayout) {
                Console.WriteLine ("WARNING: View {0} has ambiguous layout!", v);
            }
        }
    }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();

        // Perform any additional setup after loading the view, typically from a nib.

    }
}

The following screenshot shows that the box and label appear as I want, but when the view controller finishes laying out the subviews, I get the message that my box has ambiguous layout. If I change the "important constraints" for the box's right and bottom edges to be Equal to rather than GreaterThanOrEqual then the layout is no longer ambiguous. Again, it is important that I be able to specify my constraints as GTE because I am setting my borders with respect to an arbitrary number of views in rows and columns, so as I add new views to the table, I make sure that my right edge is constrained to be GTE all of the views and similarly for the bottom edge. I cannot use strictly equal to in my app because I do not know which subview in the table will be the right-most or bottom-most view; this is the reason for having GTE constraints.

自动版面测试

My questions are: why can't I use GTE constraints here for my box view without getting an ambiguous layout? If this simple example does not work, how then do you use GTE constraints at all (they would all be ambiguous, otherwise you would specify them as Equal in the first place)?

Thanks for any help,

Cam

Generally I do the same kind of thing, use GTE to insure something is limited in one dimension, but then add another constraint with a lower priority that attempts to do something else, like center a view within another view. You have to get creative. The best way I've found is using pencil and paper, sketch an example out, and try to construct where I need what constraints.

I do not believe the system can equally allocate inequalities - if you have one on the left edge and one on the right edge, what should it do? I use inequalities to insure that some maximum is never exceeded, then lower priority ones to try and achieve some goal (ie centering).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM