Forum Xamarin.iOS

Layout with UITableView - How to position.

MarthughieMarthughie GBMember ✭✭
edited October 2015 in Xamarin.iOS

I am using a number of subviews in a view, all the full width of the view, and arranged to follow on vertically where the previous one finished. I am using View.AddConstraints () to achieve this and it works OK and is intuitive to program.

However I would now like to insert a UITableView into the stack of views that I have, and the UITableView only ever has two rows. If I position the next subview to follow on after it (nextsubview Top == thissubview Bottom) I have a problem in that even though the RowsInSection() Delegate overload returns 2, and only two rows are set in GetCell.....

    //====================================================================
    public override nint RowsInSection (UITableView tableview, nint section)
    {
        return 2;
    }

    //====================================================================
    public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
    {
        UITableViewCell cell = tableView.DequeueReusableCell (CellIdentifier);

        if (cell == null) {
            cell = new UITableViewCell (UITableViewCellStyle.Subtitle, CellIdentifier);
        }

        cell.TextLabel.Text = "LABEL" + indexPath.Row.ToString();
        return cell;
    }

... the UITableView is rendered with about 20 rows, the last 18 or so are empty. This means that calling View.AddConstraints to set the next subview at the bottom of the UITableView, puts it in completely the wrong vertical position.

How can I position the next subview so its top is after the second row of the UITableList, rather than after its Bottom edge (obviously UITableList.RowHeight and UITableList.EstimatedRowHeight are not set at the time I am setting up the constraints, so these cannot be used at that time).

Thanks.

Posts

  • NicholasTurnerNicholasTurner USMember, University ✭✭

    Put constraints on the TableView to be the size of the rows. I managed to do this, if I can find my code I can share but it was 2 years ago.

    I usually do a minimum size with a 750 priority and then expand to be the size of the rows. The table view is a fixed size.

    You can also subclass your subviews and override the IntrinsicSize property so you don't have to specify height and width of the subviews making it easier to manage your screen.

  • MarthughieMarthughie GBMember ✭✭
    edited October 2015

    Thanks @NicholasTurner

    I did try to use the size of the rows to constrain the height...
    ...
    NSLayoutConstraint.Create(menutablelist,NSLayoutAttribute.Height, NSLayoutRelation.Equal, parentView, NSLayoutAttribute.Height,0f, menutablelist.RowHeight * 2)
    ....

    but that has no effect, and at the time it is called the row height is -1

    I will look into setting intrinsic size.
    Thanks

  • NicholasTurnerNicholasTurner USMember, University ✭✭
    edited October 2015

    @Marthughie

    In your viewDidLayout is when constraints are calculated, and since you are setting this when the tableView hasn't been loaded it will always be 0

    but you can do something like

    public class MyTableView : UITableView { public override CGSize IntrinsicSize { get { return new GCSize(this.Superview.Frame.Width, this.RowHeight*Rows); } }

    Now I typed that from memory, so it may have bugs.

    So, as you may know the intrinsic size is assumed in constraints, so don't specify the tables width or height. The TableView instrinic's size will take the width of the parent(superview) and it specifies it's own height.

    Remember, this all happens in ViewDidLayoutSubviews.

    Try to define all constraints in your ViewDidLoad, because when ViewDidAppear is called LayoutSubviews has already fired. At that point you have to trigger the layout again ( LayoutIfNeeded, LayoutSubviews )

  • MarthughieMarthughie GBMember ✭✭
    edited October 2015

    @NicholasTurner thanks I really appreciate your input on this. Whilst trying to find an override to do this I discovered that this will work for me.

                NSLayoutConstraint.Create(menutablelist,NSLayoutAttribute.Height,   NSLayoutRelation.Equal, parentView, NSLayoutAttribute.Height,0.0f,  menutablelist.ContentSize.Height )
    

    By setting the height of the UITableView to its ContentSize Height (which is available at that point) it seems to now work.

    Thanks again :-)

  • NicholasTurnerNicholasTurner USMember, University ✭✭
    edited October 2015

    @Marthughie I would change that
    NSLayoutConstraint.Create( menutablelist,NSLayoutAttribute.Height, NSLayoutRelation.Equal, parentView, NSLayoutAttribute.Height,0.0f, menutablelist.ContentSize.Height )
    to this

    NSLayoutConstraint.Create( menutablelist,NSLayoutAttribute.Height, NSLayoutRelation.Equal, parentView, NSLayoutAttribute.Bottom, 1f, 0f)
    This basically says the same thing, "I want the bottom of my table view to be equal to the bottom of parent list. This will be, no matter what height the parent list is, your table view will be bound to that value.

    The 2 problems I see from what you pasted is the 0 multiplier which says multiply 0 by the menutablelist.height. The other is you are setting it to a static value. So when you rotate or another object changes it value the height value is no longer valid. But, if you bind to the object using the constraint it will always change. It looks correct now, because you probably set the frame.

    I found the best way to tell is always do this:

    UITableVIew myTableView = new UITableView(new CGRect(0,0,0,0));

    Whatever it is a view, button, label, etc.., this way you make sure the constraints are applied correctly. When Views resize and rotate you will see the artifacts develop without thinking about it like Objects and not values. You will see it on the first run.

  • NicholasTurnerNicholasTurner USMember, University ✭✭

    The hight, width, or position should be ignored and say this to yourself " I want my view to be bound the the objects ( left, top, bottom, or right ), then use the last value as an adjustment not the actual value.

  • MarthughieMarthughie GBMember ✭✭

    Thank you @NicholasTurner .

    The parent view in this case is the entire height of the view, it is superfluous but is a hangover from other code at the moment. So unfortunately the code you kindly posted above

    NSLayoutConstraint.Create(
            menutablelist,NSLayoutAttribute.Height,   
            NSLayoutRelation.Equal, 
            parentView, 
            NSLayoutAttribute.Bottom,
            1f,  
            0f)
    

    will pour the TableView in starting from the correct position (because I set its Top to equal the Bottom of the previous subview), but will then fill the view to the bottom. The real problem I was trying to solve was that the subview which is supposed to be rendered just below two rows of the TableView, will now appear off screen because it in turn is tagged to the bottom of the TableView. A fixed size for the tablets, but based on the height on the device running, of 2 rows seems to be OK?

    So this shows what I am doing a bit closer, but does not show all items on the screen just those from the top down to the one after the TableView

            // logo covers top 15% of height
            parentView.Add (logoView);
            View.AddConstraints (new NSLayoutConstraint[] {
                NSLayoutConstraint.Create(logoView,NSLayoutAttribute.CenterX,   NSLayoutRelation.Equal, parentView, NSLayoutAttribute.CenterX,1f,0f),
                NSLayoutConstraint.Create(logoView,NSLayoutAttribute.Width,     NSLayoutRelation.Equal, parentView, NSLayoutAttribute.Width,1f, 0f),
                NSLayoutConstraint.Create(logoView,NSLayoutAttribute.Top,       NSLayoutRelation.Equal, parentView, NSLayoutAttribute.Top,1f,0f),
                NSLayoutConstraint.Create(logoView,NSLayoutAttribute.Height,    NSLayoutRelation.Equal, parentView, NSLayoutAttribute.Height,0.15f, 0f)
            });
    
            // now Add the TableView, but only the top two rows
            parentView.Add(menutablelist);
            View.AddConstraints (new NSLayoutConstraint[] {
                NSLayoutConstraint.Create(menutablelist,NSLayoutAttribute.Top,     NSLayoutRelation.Equal,logoView,  NSLayoutAttribute.Bottom,1f,   0f),
                NSLayoutConstraint.Create(menutablelist,NSLayoutAttribute.Width,   NSLayoutRelation.Equal,parentView,NSLayoutAttribute.Width,1f,0f),
                NSLayoutConstraint.Create(menutablelist,NSLayoutAttribute.CenterX, NSLayoutRelation.Equal,parentView,NSLayoutAttribute.CenterX,1f,0f),
                // menutablelist.ContentSize.Height returns the Height of two rows (RowsInSection returns 2)
                NSLayoutConstraint.Create(menutablelist,NSLayoutAttribute.Height,   NSLayoutRelation.Equal, parentView, NSLayoutAttribute.Height,0.0f,  menutablelist.ContentSize.Height )
            });
    
            // add the message navbar immediately after the 2 row TableView
            parentView.Add(navBarLatestMessages);
            View.AddConstraints (new NSLayoutConstraint[] {
                NSLayoutConstraint.Create(navBarLatestMessages,NSLayoutAttribute.Top,     NSLayoutRelation.Equal,menutablelist,  NSLayoutAttribute.Bottom,1f,   0f),
                NSLayoutConstraint.Create(navBarLatestMessages,NSLayoutAttribute.Width,   NSLayoutRelation.Equal,parentView,NSLayoutAttribute.Width,1f,0f),
                NSLayoutConstraint.Create(navBarLatestMessages,NSLayoutAttribute.CenterX, NSLayoutRelation.Equal,parentView,NSLayoutAttribute.CenterX,1f,0f),
            });
    

    This appears to work OK, and the height for the row calculation is not a fixed size but based on the content size calculated on any device running. Do you see a problem with this ?

    Thanks for your comments.

  • NicholasTurnerNicholasTurner USMember, University ✭✭
    edited October 2015

    @Marthughie What I do is think of it as div elements in html with CSS. So for your eaxmple I would do this

    View
    ----parentView - full height & width of View - not needed but I do for clarity in case things change
    --------TopView - View to container the table view - instrinsic size is the total height of all subviews
    -----------TableView - specify the size as above
    --------Bottom View - Top = Bottom of TopView, Bottom = bottom of parent view
    -----------Footer View - Top = Top of BottomView and what ever size you want
    ---------------controls

    That I believe is what you want. Can you post a photo and I can do a quick sample app

  • MarthughieMarthughie GBMember ✭✭

    Hi @NicholasTurner. Thanks for that. I actually have
    parentview
    ----UIImageView (which contains a logo, height constrained to 15% of View height)
    ----UITableView (which should only show 2 of its rows)
    ----UINavigationBar
    ----View (which contains a custom scrolling view)

    I will try to attach a sketch of this, although my proposed solution appears to work OK.

    Thanks again.

Sign In or Register to comment.