Simon Simon - 3 months ago 24
iOS Question

What is the Xamarin.Forms equivalent of layoutSubviews?

I'm porting a large iOS codebase to a Xamarin.Forms app. We have a lot of custom views which perform their layout logic by making calculations in

-layoutSubviews
. The codebase is too large for me to port in time if I need to reinterpret these calculations in terms of Stack or Grid layouts. What I really want is a direct equivalent, where I can add the equivalent subviews to our views without worrying about where they go, and then a method which is called when the view's bounds change inside which I can set the new bounds of the subviews. Then I can directly port our existing iOS code.

Is there some equivalent in Xamarin.Forms for
-layoutSubviews
?

Answer

You can create your own Layout by deriving from Xamarin.Forms.Layout class.

public class CustomLayout : Layout<View>
{
    public CustomLayout ()
    {

    }
}

The layout must override the LayoutChildren method. This method is responsible for positioning children on screen.

Children can be measured by using the GetSizeRequest method, which will return both the desired size and the minimum size the child desires.

protected override void LayoutChildren (double x, double y, double width, double height)
{
    for (int i = 0; i < Children.Count; i++) {
        var child = (View) Children[i];
        // skip invisible children

        if(!child.IsVisible) 
            continue;
        var childSizeRequest = child.GetSizeRequest (double.PositiveInfinity, height);
        var childWidth = childSizeRequest.Request.Width;
        LayoutChildIntoBoundingRegion (child, new Rectangle (x, y, childWidth, height));
        x += childWidth;
    }
}

This method will automatically be called whenever the layout needs to be recomputed. If your layout consists of hardcoded or fixed size elements, hard code their sizes into this algorithm instead of measuring. GetSizeRequest calls are some of the most expensive calls that can be made, and are not predictable in their runtime as the subtree may be arbitrary complex. Fixing their size is a great way to get a performance boost if dynamic sizing is not required.

Implementing OnSizeRequest is required to make sure the new layout is sized correctly when placed inside other layouts. During layout cycles this method may be called many times depending on the layout above it and how many layout exceptions are required to resolve the current layout hierarchy.

protected override SizeRequest OnSizeRequest (double widthConstraint, double heightConstraint)
{
    var height = 0;
    var minHeight = 0;
    var width = 0;
    var minWidth = 0;

    for (int i = 0; i < Children.Count; i++) {
        var child = (View) Children[i];
        // skip invisible children

        if(!child.IsVisible) 
            continue;
        var childSizeRequest = child.GetSizeRequest (double.PositiveInfinity, height);
        height = Math.Max (height, childSizeRequest.Minimum.Height);
        minHeight = Math.Max (minHeight, childSizeRequest.Minimum.Height);
        width += childSizeRequest.Request.Width;
        minWidth += childSizeRequest.Minimum.Width;
    }

    return new SizeRequest (new Size (width, height), new Size (minWidth, minHeight));
}

You can read the whole tutorial of how to create a Custom layout here.

Comments