Question

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();
        }
    }
}
Was it helpful?

Solution

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.

OTHER TIPS

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
    );
}

I found that the following works pretty well for a simple controller that is displaying an iAd AdBannerView along with a regular view. The code below also calls the 'Resize' method for all subviews that are of type 'AdView'

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

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

    private void Resize()
    {
        try
        {
            if (AdBannerView.Hidden)
            {
                InternalView.Frame = new RectangleF(0, 20, View.Bounds.Width, View.Bounds.Height);
                InternalView.Frame = new RectangleF(0, 20, View.Bounds.Width, View.Bounds.Height);
            }
            else
            {
                InternalView.Frame = new RectangleF(0, 20, View.Bounds.Width,
                    View.Bounds.Height - AdBannerView.Bounds.Height);
                AdBannerView.Frame = new RectangleF(0, InternalView.Bounds.Height, View.Bounds.Width,
                    AdBannerView.Bounds.Height);
                InternalView.Frame = new RectangleF(0, 20, View.Bounds.Width,
                    View.Bounds.Height - AdBannerView.Bounds.Height);
                AdBannerView.Frame = new RectangleF(0, InternalView.Bounds.Height, View.Bounds.Width,
                    AdBannerView.Bounds.Height);
            }

            foreach (UIView view in View.Subviews)
            {
                var adView = view as AdView;
                if (adView != null)
                {
                    adView.Resize();
                }
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.ToString());
        }
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top