Search code examples
iosuitableviewxamarinnslayoutconstraint

Xamarin iOS: alignment of fields in a TableView with LayoutConstraint (visual format)


I would like to align views in table cells to have all the trash bins on the left, the text field on the right (with fixed width) and the description filling the space between the two. The top would be breaking description in two lines if too long.

enter image description here

I'm trying with layout constraints in visual format but I'm not sure if it's the right approach.

var views = new UIView[] { btnRemove, lblDescription, txtQuantity };
var stkRoot = IOSUtils.CreateStackView(views, UILayoutConstraintAxis.Horizontal);
this.AddSubview(stkRoot);

var viewMetrics = new Object[] {
    "stack", stkRoot,
    "remove", btnRemove,
    "label", lblDescription,
    "field", txtQuantity,
    "margin", 8
};

List<NSLayoutConstraint> constraints = new List<NSLayoutConstraint>();
constraints.AddRange(
    NSLayoutConstraint.FromVisualFormat(
        "V:|-margin-[stack]-margin-|",
        NSLayoutFormatOptions.AlignAllLeading,
        viewMetrics
    )
);
constraints.AddRange(
    NSLayoutConstraint.FromVisualFormat(
        "H:|-margin-[remove]-margin-[label(>=70)]-margin-[field(60@20)]-margin-|",
        NSLayoutFormatOptions.DirectionLeftToRight | NSLayoutFormatOptions.AlignAllCenterY,
        viewMetrics
    )
);

AddConstraints(constraints.ToArray());

btnRemove and txtQuantity are a UIButton and a UITextField with high resistance to compression and hugging, lblDescription is a UILabel with low resistance to hugging and compression. I still have to setup margin, colors, and so on... Anybody could give me some hints?


Solution

  • I've not used visual formats. This will also get the job done.

    ContentView.AddSubviews (btnRemove, lblDescription, txtQuantity);
    
    btnRemove.TranslatesAutoresizingMaskIntoConstraints = false;
    lblDescription.TranslatesAutoresizingMaskIntoConstraints = false;
    txtQuantity.TranslatesAutoresizingMaskIntoConstraints = false;
    
    // add leading space to btnRemove
    var btnRemoveLeading = NSLayoutConstraint.Create (btnRemove, NSLayoutAttribute.Leading,
                                      NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Leading, 1, 8);
    ContentView.AddConstraint (btnRemoveLeading);
    
    // add center-y contraint to btnRemove
    var btnRemoveCenterY = NSLayoutConstraint.Create (btnRemove, NSLayoutAttribute.CenterY,
                                      NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.CenterY, 1, 0);
    ContentView.AddConstraint (btnRemoveCenterY);
    
    // add trailing space to txtQuantity
    var txtQuantityTrailing = NSLayoutConstraint.Create (txtQuantity, NSLayoutAttribute.Trailing,
                                         NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Trailing, 1, -8);
    ContentView.AddConstraint (txtQuantityTrailing);
    
    // add center-y contraint to txtQuantity
    var txtQuantityCenterY = NSLayoutConstraint.Create (txtQuantity, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.CenterY, 1, 0);
    ContentView.AddConstraint (txtQuantityCenterY);
    
    // add horizontal space between btnRemove and lblDescription
    var hsBtnRemoveTolblDescription = NSLayoutConstraint.Create (btnRemove, NSLayoutAttribute.Right,
                                             NSLayoutRelation.Equal, lblDescription, NSLayoutAttribute.Left, 1, 8);
    ContentView.AddConstraint (hsBtnRemoveTolblDescription);
    
    // add horizontal space between txtQuantity and lblDescription
    var hstxtQuantityTolblDescription = NSLayoutConstraint.Create (txtQuantity, NSLayoutAttribute.Left,
                                               NSLayoutRelation.Equal, lblDescription, NSLayoutAttribute.Right, 1, 8);
    ContentView.AddConstraint (hsBtnRemoveTolblDescription);
    
    // add center-y contraint to lblDescription
    var lblDescriptionCenterY = NSLayoutConstraint.Create (btnRemove, NSLayoutAttribute.CenterY,
                                      NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.CenterY, 1, 0);
    ContentView.AddConstraint (lblDescriptionCenterY);
    

    Edit: also add width constrains to button and textfield, so that the label stretches.

    var btnRemoveWidth = NSLayoutConstraint.Create (btnRemove, NSLayoutAttribute.Width,
                                      NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1, 32);
    ContentView.AddConstraint (btnRemoveWidth);
    
    var txtQuantityWidth = NSLayoutConstraint.Create (txtQuantity, NSLayoutAttribute.Width,
                                      NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1, 40);
    ContentView.AddConstraint (txtQuantityWidth);
    

    for making the label multiline, and make the UITableviewCell grow depending on number of lines your label has, add top and bottom constrains to the label and set height greater than some value:

    var vSlblDescriptionToTop = NSLayoutConstraint.Create (lblDescription, NSLayoutAttribute.Top, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Top, 1, 8);
    ContentView.AddConstraint (vSlblDescriptionToTop);
    
    var vSlblDescriptionBottom = NSLayoutConstraint.Create (lblDescription, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, ContentView, NSLayoutAttribute.Bottom, 1, -8);
    ContentView.AddConstraint (vSlblDescriptionBottom);
    
    var lblDescriptionHeight = NSLayoutConstraint.Create (lblDescription, NSLayoutAttribute.Height,
                                                                      NSLayoutRelation.GreaterThanOrEqual, null, NSLayoutAttribute.NoAttribute, 1, 32);
    ContentView.AddConstraint (lblDescriptionHeight);
    

    and set your tableview's row height to AutomaticDimension

    myTableView.EstimatedRowHeight = 44;
    myTableView.RowHeight = UITableView.AutomaticDimension;
    myTableView.Source = ....
    

    Also override the UITableViewSource's GetHeightForRow to return AutomaticDimension

    public override nfloat GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
    {
        return UITableView.AutomaticDimension;
    }