Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS AutoLayout with Xamarin using ONLY C# code in Visual Studio 2013, no XCode or Interface Builder

I started using Xamarin because I wanted to stay in the Visual Studio 2013 environment and not have to learn a new environment. Anyway, I'm going to paste my controller's code below and hopefully somebody is smarter than me (an almost certainty) and can get me back on track.

I just discovered AutoLayout. It seems to me that AutoLayout understanding is critical to speeding up development. However, I'm not finding a lot of information for using AutoLayout with pure C# in Visual Studio 2013. Perhaps I'm just not looking in the right places.

Anyway, Let's start this new discussion with a simple controller that uses AutoLayout TOTALLY in C# without using any .nib or Interface Builder stuff at all. And without using Xamarin Studio. Just ALL done in Visual Studio 2013.

Here are the requirements:

  1. Make a UIViewController that will facilitate implementation of apple's iAD.

  2. Basically, we want to place an iAD banner at the bottom of the screen taking up the full width.

  3. We will put the view above the iAD banner and have it fill the rest of the screen.

  4. The banner view may disappear from time to time if no ADs are present, so we need to handle that.

  5. We need to handle when the device rotates to accommodate new orientation.

  6. We need to handle different devices. iPod, iPad, iPhone, version 4 and 5

    This should be trivial, but I've been banging my head on the keyboard for 2 days trying to get this to work. Any recommendations, examples, or ideas would be GREATLY HELPFUL. Remember, we want to ONLY use C# in Visual Studio and not use Interface Builder at all. Here is my non working attempt:

Using the code below, I end up with the AdBanner off of the screen below the InternalView. Also, the Internal View is longer than the screen and only half the screen width. What's going on here? Do I need to turn on the AutoLayout Feature somewhere? Can I do it in C# code or is it hiding somewhere in Project Settings?

using System;
using MonoTouch.iAd;
using MonoTouch.UIKit;

namespace ADayBDayiOS
{
    public class ADViewController : UIViewController
    {
        private UIView InternalView { get; set; }
        private ADBannerView AdView { get; set; }

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

            InternalView = new UIView{BackgroundColor=UIColor.Blue}; 

            //This is apple's standard ADBannerView
            AdView = new ADBannerView(ADAdType.Banner) {Hidden = true};
            AdView.FailedToReceiveAd += HandleFailedToReceiveAd;
            AdView.AdLoaded += HandleAdLoaded;

            View.BackgroundColor = UIColor.Clear;

            //I'm pretty sure that we need these three lines
            View.TranslatesAutoresizingMaskIntoConstraints = false;
            InternalView.TranslatesAutoresizingMaskIntoConstraints = false;
            AdView.TranslatesAutoresizingMaskIntoConstraints = false;

            View.AddSubview(InternalView);
            View.AddSubview(AdView);

            Resize();
        }

        public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
        {
            base.DidRotate(fromInterfaceOrientation);
            Resize();
        }

        public override void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);
            Resize();
        }

        private void Resize()
        {
            //Remove all constraints, and reset them...
            View.RemoveConstraints(View.Constraints);

            if (AdView == null || AdView.Hidden)
            {//Fill up the entire screen with our InternalView
                View.AddConstraint(NSLayoutConstraint.Create(InternalView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, View, NSLayoutAttribute.Width, 1, 0));
                View.AddConstraint(NSLayoutConstraint.Create(InternalView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, View, NSLayoutAttribute.Bottom, 1, 0));
                View.AddConstraint(NSLayoutConstraint.Create(InternalView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, View, NSLayoutAttribute.Top, 1, 0));
            }
            else
            {//Put banner ad at the bottom of the screen and fill the rest of the screen with our InternalView
                View.AddConstraint(NSLayoutConstraint.Create(AdView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, View, NSLayoutAttribute.Width, 1, 0));
                View.AddConstraint(NSLayoutConstraint.Create(InternalView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, View, NSLayoutAttribute.Width, 1, 0));

                View.AddConstraint(NSLayoutConstraint.Create(AdView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, View, NSLayoutAttribute.Bottom, 1, 0));
                View.AddConstraint(NSLayoutConstraint.Create(InternalView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, View, NSLayoutAttribute.Bottom, 1, AdView.Bounds.Height));
                View.AddConstraint(NSLayoutConstraint.Create(InternalView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, View, NSLayoutAttribute.Top, 1, 0));
            }
        }
        
        /// <summary>
        /// Shows the AdView when a new Ad loads
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void HandleAdLoaded(object sender, EventArgs e)
        {
            if (AdView == null)
                return;
            AdView.Hidden = false;
            Resize();
        }

        /// <summary>
        /// Hides the AdView when no ads are available
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void HandleFailedToReceiveAd(object sender, AdErrorEventArgs e)
        {
            if (AdView == null)
                return;
            AdView.Hidden = true;
            Resize();
        }
    }
}
like image 684
Curtis Avatar asked Mar 29 '14 23:03

Curtis


People also ask

How do I use Autolayout?

You can add auto layout to a selected frame, component, or component set from a few places: Use the keyboard shortcut ⇧ Shift A . In the right sidebar, click next to Auto layout with a frame selected. Right-click on a frame or object and select Add Auto layout.

What is iOS Autolayout?

Auto Layout is the preferred technology to define layouts for user interfaces on iOS and macOS. Its goal: To make it easy for you to create user interfaces that adapt automatically to different screen sizes and orientations.

What is Autolayout in Swift?

Auto Layout constraints allow us to create views that dynamically adjust to different size classes and positions. The constraints will make sure that your views adjust to any size changes without having to manually update frames or positions.


2 Answers

Creating Constraints in code for AutoLayout manually with exposed iOS methods is a tedious process.

AutoLayout has a lot of benefits and using it to achieve stuff like orientation changes and general layout's is kinda a no-brainer. To achieve what you require with just VS2013, I'd suggest having a look at

FluentLayouts

It's made by the author that created MVVMCross and it has quite some documentation to get you started.

Blog Post

Youtube Vid Tutorial

Essentially with it you can write constraints like:

View.AddConstraints(
  button.AtTopOf(View).Plus(vPadding),
  button.AtRightOf(View).Minus(hPadding),
  button.Width().EqualTo(ButtonWidth),

  text.AtLeftOf(View, hPadding),
  text.ToLeftOf(button, hPadding),
  text.WithSameTop(button)
);

So for your case,

you'd prolly want to have the ad banner view pinned to the Top of the superview with pins for left and right of the superview as well. Prolly add a fixed height if you need to. Pinning to the left and right of the superview will then cater for when the device orientation changes and scale the width accordingly. Top position would be catered with the top pin and the fixed height for the height of the banner.

AutoLayout by itself asks you for the X,Y position of an element and for the element to know it's desired size. Some controls like buttons have an implicit size so you wouldn't need to set this width/height explicitly. However things like a plain UIView does not. So you would have to specify their size with constraints too.

Finally having a tool like FluentLayouts helps us create constraints much much easier, but the fundamentals of what AutoLayouts is and how to use it is just general knowledge on the topic which you might actually be better off visiting apple docs for or some tutorials like these. Yes it shows it in XCode but it also explains the topic which we need to understand regardless. That site also has articles about constraints from code that explains the nitty gritty about constants and multipliers and sorts with constraints that are worth a read through. Once you understand the concepts and what you can do with it, then pick a tool like fluent-layouts and your requirements should fall into place well.

like image 149
Viv Avatar answered Oct 17 '22 21:10

Viv


Frank Krueger has an elegant solution to this problem which you can read about here: http://praeclarum.org/post/45690317491/easy-layout-a-dsl-for-nslayoutconstraint. The code is available here: https://gist.github.com/praeclarum/5175100

Just add the class to your iOS project and you can write code like this:

void LayoutWithEase ()
{
    View.ConstrainLayout (() => 
        button.Frame.Width == ButtonWidth &&
        button.Frame.Right == View.Frame.Right - HPadding &&
        button.Frame.Top == View.Frame.Top + VPadding &&

        text.Frame.Left == View.Frame.Left + HPadding &&
        text.Frame.Right == button.Frame.Left - HPadding &&
        text.Frame.Top == button.Frame.Top
    );
}
like image 21
TreeAndLeaf Avatar answered Oct 17 '22 22:10

TreeAndLeaf