Skip to content

mallibone/PureLayout.Net

Repository files navigation

PureLayout

NuGet Version License Build status

PureLayout.Net

The ultimate layouting API for iOS is now available for Xamarin.iOS. PureLayout extends UIView/NSView, NSArray, and NSLayoutConstraint and brings productivity boosting enhancements with a refined Auto Layout API that is modeled after Apple's own frameworks. PureLayout.Net brings you 100% of the cross-platform Objective-C library to C# with Xamarin.iOS.

Writing your responsive iOS UIs in code behind can be a challenge when only using vanilla Auto Layout code. PureLayout.Net brings a developer-friendly approach for defining interface based on Auto Layout. Basing your UI code on PureLayout.Net will enable you and your team to write modularized view code which is designed for optimum performance and design flexibility.

Table of Contents

  1. Setup
  2. API
  3. Usage
  4. Building it from scratch

Setup

To start out using PureLayout.Net in your Xamarin.iOS project simply install the following NuGet package:

Install-Package PureLayout.Net

The library is compiled for use with the simulator and iOS devices with an ARMv7(s) or ARM64.

PureLayout.Net 101

PureLayout allows you to define your iOS UI in directly in C#/F#. This will not only make the UI code easier to maintain and use, but also allows to swiftly define layouting constraints. After installing the NuGet package, you can add the following using line to the class you want to start layouting your UI:

using PureLayout.Net;

All the PureLayout constraints start with Auto[XYZ]. Keep that in mind when you are looking for a constraint to apply. Defining a constraint is as easy as the following line:

somethingThatDerivesFromUIView.AutoPinEdgesToSuperviewEdges();

So for the follwing UI:

showing the rendered sample code bellow

The layouts would look like this:

private void LayoutView()
{
    View.BackgroundColor = UIColor.White;
    _amountLabel = new UILabel();
    _amountLabel.Text = "Amount: ";
    _amountLabel.Font = UIFont.PreferredBody;

    _tipPercentageLabel = new UILabel();
    _tipPercentageLabel.Text = "Tip %: ";
    _tipPercentageLabel.Font = UIFont.PreferredBody;

    _amount = new UITextField
    {
        KeyboardType = UIKeyboardType.DecimalPad,
        Placeholder = "Total Amount",
        BorderStyle = UITextBorderStyle.RoundedRect
    };

    _tipPercentage = new UITextField
    {
        KeyboardType = UIKeyboardType.DecimalPad,
		Placeholder = "Tip %",
        BorderStyle = UITextBorderStyle.RoundedRect
    };

    _calculateButton = new UIButton(UIButtonType.RoundedRect);
    _calculateButton.SetTitle("Calculate Tip", UIControlState.Normal);
    _tableView = new UITableView();
    _clearHistoryButton = new UIButton(UIButtonType.RoundedRect);
    _clearHistoryButton.SetTitle("Clear History", UIControlState.Normal);
    _clearHistoryButton.BackgroundColor = UIColor.FromRGB(243, 105, 105);
    _clearHistoryButton.SetTitleColor(UIColor.White, UIControlState.Normal);

    View.Add(_amount);
    View.Add(_tipPercentage);
    View.Add(_calculateButton);
    View.Add(_tableView);
    View.Add(_clearHistoryButton);

	_amount.AutoPinEdgeToSuperviewEdge(ALEdge.Top, Constants.WideMargin);
	_amount.AutoPinEdgeToSuperviewEdge(ALEdge.Left, Constants.DefaultMargin);
    _tipPercentage.AutoPinEdge(ALEdge.Leading, ALEdge.Trailing, _amount, Constants.DefaultMargin);
	_tipPercentage.AutoPinEdgeToSuperviewEdge(ALEdge.Right, Constants.DefaultMargin);
    _tipPercentage.AutoAlignAxis(ALAxis.Baseline, _amount);

    _calculateButton.AutoPinEdge(ALEdge.Top, ALEdge.Bottom, _amount, Constants.DefaultMargin);
    _calculateButton.AutoAlignAxisToSuperviewAxis(ALAxis.Vertical);

    _tableView.AutoPinEdge(ALEdge.Top, ALEdge.Bottom, _calculateButton, Constants.WideMargin);
    _tableView.AutoPinEdgeToSuperviewEdge(ALEdge.Leading, Constants.DefaultMargin);
    _tableView.AutoPinEdgeToSuperviewEdge(ALEdge.Trailing, Constants.DefaultMargin);
    _tableView.AutoPinEdge(ALEdge.Bottom, ALEdge.Top, _clearHistoryButton);

    _clearHistoryButton.AutoPinEdgesToSuperviewEdgesExcludingEdge(ALEdge.Top);
    _clearHistoryButton.AutoSetDimension(ALDimension.Height, Constants.WideMargin * 2);
    _clearHistoryButton.AutoPinEdge(ALEdge.Top, ALEdge.Bottom, _tableView);
}

While PureLayout can play along very nicely with existing Storyboard layouts and can be used for simply adding or editing layout definitions it allows you to define your UI in a modularized, shareable and scaleable fashion. To create a Xamarin.iOS project without using a storyboard follow the instructions on this page of the Xamarin Developer guide.

API Reference

PureLayout.Net allows to set the following different kind of constraints via the following attributes that are mapped to Enums in Xamarin:

  • ALEdge
    • Top
    • Bottom
    • Left
    • Right
    • Leading
    • Trailing
  • ALDimension
    • Height
    • Width
  • ALAxis
    • Vertical
    • Horizontal
    • Baseline
    • FirstBaseline
    • LastBaseline
  • ALMargin
    • Top
    • Bottom
    • Left
    • Right
    • Leading
    • Trailing
  • ALMarginAxis
    • Horizontal
    • Vertical

As stated on the original PureLayout GitHub repository, the following picture describes best what kind of constraints can be set on UIView Elements.

Pin Edges

PureLayout lets you pin the edges of a view to it's super view or any adjacent view. Pinning the view to it's super view can be achieved with the following methods:

// Prep code
var edgeInsets = new UIEdgeInsets(top: 16, left: 16f, bottom: 16f, right: 16f);
var view = new UIView();

// Pin Edges
view.AutoPinEdgesToSuperviewEdges();
view.AutoPinEdgesToSuperviewEdges(edgeInsets);

// Exclude an Edge
view.AutoPinEdgesToSuperviewEdgesExcludingEdge(excludingEdge: ALEdge.Bottom);
view.AutoPinEdgesToSuperviewEdgesExcludingEdge(insets: edgeInsets, excludingEdge: ALEdge.Bottom);

Note that it is possible to either pin all edges or all but one edge to the super view. It is further possible to define the inset with the UIEdgeInset object. Alternatively the edges can be pinned to the margins of the super view.

view.AutoPinEdgesToSuperviewMargins();

// Exclude an Edge
view.AutoPinEdgesToSuperviewMarginsExcludingEdge(edge: ALEdge.Bottom);
view.AutoPinEdgesToSuperviewMarginsExcludingEdge(insets: edgeInsets, edge: ALEdge.Bottom);

It is also possible to pin a single edge to the super view:

// Pin Edge to Superview Edge
view.AutoPinEdgeToSuperviewEdge(edge: ALEdge.Top);
view.AutoPinEdgeToSuperviewEdge(edge: ALEdge.Top, inset: 16f);
view.AutoPinEdgeToSuperviewEdge(edge: ALEdge.Top, inset: 16f, relation: NSLayoutRelation.LessThanOrEqual);

// Pin Edge to Supverview Margin
view.AutoPinEdgeToSuperviewMargin(edge: ALEdge.Left);
view.AutoPinEdgeToSuperviewMargin(edge: ALEdge.Left, relation: NSLayoutRelation.GreaterThanOrEqual);

Further you can pin an edge of one UIView to the edge of another UIView:

view.AutoPinEdge(edge: ALEdge.Right, toEdge: ALEdge.Left, otherView: otherView);
view.AutoPinEdge(edge: ALEdge.Right, toEdge: ALEdge.Left, otherView: otherView, offset: 16f);
view.AutoPinEdge(edge: ALEdge.Right, toEdge: ALEdge.Left, otherView: otherView, offset: 16f, relation: NSLayoutRelation.GreaterThanOrEqual);

And if you just simply want to pin a UIView to the top or bottom navigation layout just use one of the following options:

// Pin to Top Layout
view.AutoPinToTopLayoutGuideOfViewController(viewController: this);
view.AutoPinToTopLayoutGuideOfViewController(viewController: this, inset: 16f);
view.AutoPinToTopLayoutGuideOfViewController(viewController: this, inset: 16f, relation: NSLayoutRelation.Equal);

// Pin to Bottom Layout
view.AutoPinToBottomLayoutGuideOfViewController(viewController: this);
view.AutoPinToBottomLayoutGuideOfViewController(viewController: this, inset: 16f);
view.AutoPinToBottomLayoutGuideOfViewController(viewController: this, inset: 16f, relation: NSLayoutRelation.Equal);

Align and Center

Another common use case when defining a layout is aligning different UI Elements to each other. There by the UI element can either be aligned to the superview:

// Align to Superview
view.AutoCenterInSuperview();
view.AutoAlignAxisToSuperviewAxis(axis: ALAxis.Horizontal);

// Align to Supberview margins
view.AutoCenterInSuperviewMargins();
view.AutoAlignAxisToSuperviewMarginAxis(axis: ALAxis.Horizontal);

Or in regards to another UI element:

view.AutoAlignAxis(axis: ALAxis.Baseline, otherView: otherView);
view.AutoAlignAxis(axis: ALAxis.Baseline, otherView: otherView, offset: 16f);
view.AutoAlignAxis(axis: ALAxis.Baseline, otherView: otherView, offset: 16f, multiplier: 2f);

Set Dimensions

PureLayout allows to either set the dimension to a fixed value:

view.AutoSetDimension(dimension: ALDimension.Width, size: 128f);
view.AutoSetDimension(dimension: ALDimension.Width, size: 128f, relation: NSLayoutRelation.LessThanOrEqual);

view.AutoSetDimensionsToSize(size: new CGSize(width: 32, height: 32));

Or ensure it is the same size as the dimension of another view:

// Match Dimension with offset
view.AutoMatchDimension(dimension: ALDimension.Height, toDimension: ALDimension.Width, otherView: otherView);
view.AutoMatchDimension(dimension: ALDimension.Height, toDimension: ALDimension.Width, otherView: otherView, offset: 16f);
view.AutoMatchDimension(dimension: ALDimension.Height, toDimension: ALDimension.Width, otherView: otherView, offset: 16f, relation: NSLayoutRelation.GreaterThanOrEqual);

// Match Dimension with multiplier
view.AutoMatchDimensionWithMultiplier(dimension: ALDimension.Height, toDimension: ALDimension.Width, otherView: otherView, multiplier: 2f);
view.AutoMatchDimensionWithMultiplier(dimension: ALDimension.Height, toDimension: ALDimension.Width, otherView: otherView, multiplier: 2f, relation: NSLayoutRelation.GreaterThanOrEqual);

Building PureLayout.Net from scratch

If you want to build PureLayout.Net from scratch or contribute to the binding library this section should give you enough information to build the project.

When cloning the project ensure that it is recursively checked out:

git clone --recursive https://github.com/mallibone/PureLayout.Net.git

To compile the Objective-C PureLayout library: Ensure that Objective-Sharpie is intalled on your machine and then run make from the project root folder.

This will compile the native Part of the library. If you now compile the Binding Project, the compiled native assembly will automatically be included.

To test any changes one can use the sample solution. Please note that many errors only occur at run time, so make sure to invoke any methods that you add or change to ensure they will work.

Cleaning up

Once you have compiled the native PureLayout library you can delete all the generated assemblies by running make clean from the root projects root directory.

Thank you

A big thank you towards Tyler Fox (@smileyborg) the original creator of PureLayout and Mickey Reiss (@mickeyreiss) current maintainer of the project for bringing this library to the iOS community.

PureLayout.Net Contributors