Home > Articles > Programming > Windows Programming

Learning MonoTouch: Common iOS Classes

📄 Contents

  1. User Interface Views and Controls
  2. Device Capabilities
  3. Summary
  • Print
  • + Share This
This chapter surveys several of the more common classes you'll use when building applications.
This chapter is from the book

iOS contains a number of controls and classes that help you considerably when building applications. The user interface elements range from the buttons and labels we have already seen, to sliders, progress views, and paging controls—to name just a few. There are also classes to abstract various system capabilities, such as playing music and sending email. In this chapter, we'll survey several of the more common classes you'll use when building applications.

User Interface Views and Controls

UIKit contains various UIControl subclasses. UIControl itself derives from UIView and adds a variety of events to deal with user interaction. An example of a control we've already seen is the UIButton class. UIKit additionally contains a number of other controls.

UISegmentedControl

The UISegmentedControl is basically a tabbed interface control within a view. It is typically used to allow users to specify a particular set of subviews to interact with, effectively grouping them together, although it could also be used to create a menu structure of sorts. The control is composed of several buttons, broken into segments. Each button can have a title and an image, and the control itself can take on multiple styles.

To create a UISegmentedControl and add segments to it, you simply pass the titles you want for each segment to the constructor. For example, here is one way to create a UISegmentedControl with four segments, with their respective titles set:

public partial class ControlDemoViewController : UIViewController
{
    UISegmentedControl _segmentedControl;
    ...
    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();

        _segmentedControl = new UISegmentedControl(new object[]{"one",
            "two", "three", "four"});
        _segmentedControl.Frame =
            new RectangleF (10, 10, View.Frame.Width - 20, 50);

        View.AddSubview(_segmentedControl);
    }
}

This results in the default UISegmentedControl (UISegmentedControlStyle.Plain) shown in Figure 4.1.

Figure 4.1

Figure 4.1 Default UISegmentedControl with segment titles

Setting the ControlStyle to any of the values in the UISegmentedControlStyle enumeration can change the style of the control. As mentioned, you can use an image for each segment. Additionally, you can easily set the selected segment and tint color. Here is an example that sets a bezeled style with a black tint color and an image set on the first segment, which is also set as "selected" (see Figure 4.2):

_segmentedControl = new UISegmentedControl (new object[] { "one",
    "two", "three", "four" });
_segmentedControl.ControlStyle = UISegmentedControlStyle.Bezeled;
_segmentedControl.TintColor = UIColor.Black;
_segmentedControl.SetImage (UIImage.FromFile ("Star.png"), 0);
_segmentedControl.SelectedSegment = 0;
_segmentedControl.Frame = new RectangleF (10, 10,
    View.Frame.Width - 20, 50);
Figure 4.2

Figure 4.2 UISegmented control with additional customizations

You can also set images via the constructor in the same way you set titles. Additionally, you can set each title directly via the SetTitle method. You can even mix images and titles into the constructor because the array it takes is an array of objects. For example, the following will result in the same UISegementedControl shown in Figure 4.2:

_segmentedControl = new UISegmentedControl (new object[] {
    UIImage.FromFile ("Star.png"), "two", "three", "four" });
_segmentedControl.ControlStyle = UISegmentedControlStyle.Bezeled;
_segmentedControl.TintColor = UIColor.Black;
_segmentedControl.SelectedSegment = 0;
_segmentedControl.Frame = new RectangleF (10, 10,
    View.Frame.Width - 20, 50);

To handle the changes to the selected segment, you can register for the ValueChanged event. In the handler, you can do whatever you like based on the current selected segment, such as hiding certain views or changing some state. For example, here we simply added a UILabel as a subview of the UISegmentedControl and set its text as the selected segment changes (see Figure 4.3):

string _text;
UILabel _testLabel;

...

_testLabel = new UILabel(){Frame = new RectangleF(10, 200, 100, 50)};

_segmentedControl = new UISegmentedControl (new object[] {
    UIImage.FromFile ("Star.png"), "two", "three", "four" });
_segmentedControl.ControlStyle = UISegmentedControlStyle.Bezeled;
_segmentedControl.TintColor = UIColor.Black;
_segmentedControl.Frame = new RectangleF (10, 10,
    View.Frame.Width - 20, 50);

_segmentedControl.ValueChanged += (o, e) => {
    _selectedTitle = _segmentedControl.TitleAt
        (_segmentedControl.SelectedSegment) ?? "Title not set";
    _testLabel.Text = _text;
};

_segmentedControl.SelectedSegment = 0;
_segmentedControl.AddSubview(_testLabel);
Figure 4.3

Figure 4.3 Handling UISegmentedControl ValueChanged

UISlider

The UISlider control is similar to slider controls found on other platforms, except it allows you to move the slider via touch. You can initialize a slider with the minimum, maximum, and initial values. The default slider appearance is shown in Figure 4.4. Here, we are capturing the value as it is changed and assigning it to a label's text in the slider's ValueChanged event:

UISlider _slider;
...
_slider = new UISlider { Frame = new RectangleF (10, 10,
    View.Frame.Width - 20, 50) };
_slider.MinValue = 0.0f;
_slider.MaxValue = 20.0f;
_slider.SetValue (10.0f, false);

_slider.ValueChanged += delegate {
    _text = _slider.Value.ToString ();
    _testLabel.Text = _text;
};
Figure 4.4

Figure 4.4 UISlider default appearance

The slider can also be customized to set thumb images, track images, and min/max value images (see Figure 4.5).

_slider = new UISlider { Frame = new RectangleF (10, 10,
    View.Frame.Width - 20, 50) };
_slider.MinValue = 0.0f;
_slider.MaxValue = 20.0f;
_slider.SetValue (10.0f, false);

_slider.ValueChanged += delegate {
    _text = _slider.Value.ToString ();
    _testLabel.Text = _text;
};

// Customize the look and feel of the slider

_slider.SetThumbImage (UIImage.FromFile("Thumb0.png"),
    UIControlState.Normal);
_slider.SetThumbImage (UIImage.FromFile("Thumb1.png"),
    UIControlState.Highlighted);
_slider.SetMaxTrackImage (UIImage.FromFile("MaxTrack.png"),
    UIControlState.Normal);
_slider.SetMinTrackImage (UIImage.FromFile("MinTrack.png"),
    UIControlState.Normal);
_slider.MaxValueImage = UIImage.FromFile("Max.png");
_slider.MinValueImage = UIImage.FromFile("Min.png");
Figure 4.5

Figure 4.5 UISlider with customized look and feel

UISwitch

The UISwitch control is used to toggle between two states, such as on or off. It's basically designed to simulate a physical on/off switch. You'll find it commonly used in application settings. The control itself defaults to an off state, but you can set its value programmatically using the SetState method, with the first argument being the on/off value and the second whether the switch animates initially from off to on (if the initial value is set to on). To capture changes in the switch value, you handle the ValueChanged event. The switch's On property is a Boolean containing the on/off value, which will be true when on. Even though a UISwitch ultimately is a UIView, it is designed to always be the same size. You can set the position of its frame, but the size is ignored. Figure 4.6 shows a UISwitch where changes to its value are written to a UILabel.

Figure 4.6

Figure 4.6 UISwitch value written to a UILabel

UISwitch _switch;
...
_switch = new UISwitch {Frame = new RectangleF (
    new PointF(10,10), SizeF.Empty)};
_switch.SetState (true, false);
_switch.ValueChanged += delegate {
    _text = _switch.On.ToString ();
    _testLabel.Text = _text;
};

UIPageControl and UIScrollView

The UIPageControl is used to designate which page you are on in an application where the pages typically slide horizontally across the screen. The actual paging can be implemented with a UIScrollView, where the UIPageControl tracks the current page via a series of dots. This is the experience you see on the home screen of an iOS device when moving across pages of applications.

The UIScrollView supports adding content that is too large to fit in a designated area of the screen and have it be scrollable either horizontally, vertically, or both. It is well suited for implementing a paged experience, where each view that acts logically as a page is added as a subview of the UIScrollView. The UIScrollView even includes a PageEnabled property that when set to true will cause the scrolling to snap to each page. To make scrolling happen, you set the ContentSize of the UIScrollView to something larger than the frame of the UIScrollView. Combined with setting PageEnabled, the UIScrollView will determine the physical page size internally. For any subviews you add to act as pages, you simply set their position offset appropriately within the content size of the scroll view. Listing 4.1 shows an example of adding basic views, each with a single label containing the page number, where sliding the views left or right will allow you to page between them.

Listing 4.1. Paging with a UIScrollView

public partial class PagingController : UIViewController
{
    UIScrollView _scroll;
    List<UIView> _pages;

    int _numPages = 4;
    float _padding = 10;
    float _pageHeight = 400;
    float _pageWidth = 300;

    ...

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

        View.BackgroundColor = UIColor.Black;

        _pages = new List<UIView> ();

        _scroll = new UIScrollView {
            Frame = View.Frame,
            PagingEnabled = true,
            ContentSize = new SizeF (
                _numPages * _pageWidth + _padding
                + 2 * _padding * (_numPages - 1),
                View.Frame.Height)
        };

        View.AddSubview (_scroll);

        for (int i = 0; i < _numPages; i++) {
            UIView v = new UIView ();
            v.Add( new UILabel{
                Frame = new RectangleF (100, 50, 100, 25),
                Text = String.Format ("Page {0}", i+1)}
            );

            _pages.Add (v);
            v.BackgroundColor = UIColor.Gray;

            v.Frame = new RectangleF (
                i * + _pageWidth + _padding + (2 * _padding * i),
                0, _pageWidth, _pageHeight);

            _scroll.AddSubview (v);
        }
    }
}

To keep track of the current page, you can use the UIPageControl. This control shows a series of dots, where the number of dots represents the page count and the current page number is denoted by the highlighted dot. The control itself is not physically connected to the scroll view, so it is up to you to add the code for the page count and to track the current page. The UIPageControl's Pages property sets the page count. The formula for the current page in the scroll view is simply the current offset, available via the scroll view's ContentOffset property, divided by the page width. Setting this to the UIPageControl's CurrentPage changes the highlighted dot to the proper page (see Figure 4.7).

Figure 4.7

Figure 4.7 Paging with UIScrollView and UIPageControl

public partial class PagingController : UIViewController
{
    UIPageControl _pager;
    ...

    public override void ViewDidLoad ()
    {
        ...

        _scroll.Scrolled += delegate {

            _pager.CurrentPage =
                (int)Math.Round(_scroll.ContentOffset.X/_pageWidth);

        };

        _pager = new UIPageControl();
        _pager.Pages = _numPages;
        _pager.Frame = new RectangleF(0, 420, View.Frame.Width, 50);

        View.AddSubview(_pager);
    }
}

In addition to UIControl subclasses, UIKit has a number of classes that derive directly from UIView, such as the UIScrollView. Additional classes present rich information displays such as advertisements, web pages, maps, and tables. We'll cover some of these classes here. Others, such as UIMapView and UITableView, are covered in later chapters.

UIActivityIndicatorView

The UIActivityIndicatorView is used to indicate some operation is in progress in an indeterminate fashion. It presents itself as an animated rotating circle of sorts while the operation is happening. To use a UIActivityIndicatorView, you add it as a subview like any other view. To make it actually appear and start animating, you call its StartAnimating method. Likewise, to stop the animation and make the activity indicator disappear, you call StopAnimating. It's worth noting that any long-running operation you are performing would need to happen on a different thread; otherwise, you'll block the main thread and you would never see the activity indicator. Listing 4.2 demonstrates how to create a UIActivityIndicatorView to indicate an operation, implemented in the DoSomething method, is in progress. The result is shown in Figure 4.8.

Figure 4.8

Figure 4.8 UIActivityIndicatorView

Listing 4.2. UIActivityIndicatorView Implementation

...

UIActivityIndicatorView _activityView;

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

    showActivityButton.TouchUpInside +=
        HandleShowActivityButtonTouchUpInside;
}

void HandleShowActivityButtonTouchUpInside (object sender, EventArgs e)
{
    _activityView = new UIActivityIndicatorView ();

    _activityView.Frame = new RectangleF (0, 0, 50, 50);
    _activityView.Center = View.Center;

    _activityView.ActivityIndicatorViewStyle =
        UIActivityIndicatorViewStyle.WhiteLarge;
    View.AddSubview (_activityView);
    _activityView.StartAnimating ();

    Thread t = new Thread (DoSomething);
    t.Start ();
}

void DoSomething ()
{
    Thread.Sleep (3000);

    using (var pool = new NSAutoreleasePool ()) {
        this.InvokeOnMainThread (delegate {
            _activityView.StopAnimating (); });
    }
}

UIProgressView

Similar to the UIActivityIndicatorView, the UIProgressView is used to indicate some operation is underway. However, the UIProgressView is determinate because it displays the percentage of work that has been completed by filling in a portion of a horizontal bar. The progress is set using the Progress property and a floating-point value between 0 and 1, where 1 indicates 100% completion. Listing 4.3 implements a simulated operation whose progress is tracked with a UIProgressView, the result of which is shown in Figure 4.9.

Figure 4.9

Figure 4.9 UIProgressView

Listing 4.3. Example of a UIProgressView

...

UIProgressView _progressView;

void HandleShowActivityButtonTouchUpInside (object sender, EventArgs e)
{
    _progressView = new UIProgressView ();
    _progressView.Frame = new RectangleF (0, 0, View.Frame.Width - 20,
        100);
    _progressView.Center = View.Center;
    _progressView.Style = UIProgressViewStyle.Default;

    View.AddSubview (_progressView);

    Thread t = new Thread (DoSomethingElse);
    t.Start ();
}

void DoSomethingElse ()
{
    int n = 3;

    for (int i = 0; i < n; i++) {
        Thread.Sleep (1000);

        using (var pool = new NSAutoreleasePool ()) {

            this.InvokeOnMainThread (delegate {
                _progressView.Progress = (float)(i + 1) / n; });
        }
    }
}

UIImageView

We used UIImageView previously in Chapter 2, "iOS SDK via MonoTouch." You'll recall its purpose in life is to present a UIImage on the screen. The simplest example of a UIImageView sets the Image property and adds the view to the screen via a subview. For example, assuming the project has a file named monkey.png with a build action of Content, you would fill the screen with the image (see Figure 4.10) like this:

UIImageView _imageView;
...
_imageView = new UIImageView ();
_imageView.Frame = new RectangleF(0,0,
    View.Frame.Width, View.Frame.Height);
_imageView.Image = UIImage.FromFile("monkey.png");
Figure 4.10

Figure 4.10 UIImageView displaying a UIImage

To control how the view lays out its contents (for example, to preserve the image's aspect ratio), you use the ContentMode property:

_imageView = new UIImageView ();
_imageView.Frame = new RectangleF(0,0,
    View.Frame.Width, View.Frame.Height);
_imageView.Image = UIImage.FromFile("monkey.png");
 imageView.ContentMode = UIViewContentMode.ScaleAspectFit;

Here, we set the ContentMode to ScaleAspectFit, resulting in the image layout shown in Figure 4.11. Additionally, you can experiment with several other settings for ContentMode to control the layout to your liking.

Figure 4.11

Figure 4.11 UIImageView with ContentMode set to ScaleAspectFit

UIWebView

UIWebView is a wrapper around WebKit that you can use in your applications. You can use it to render HTML content either from the Internet or from a local resource. To demonstrate how to use it, let's build a simple browser application. Create a new window-based application and add a new view controller with a view. I named mine LMT4-5 and SimpleBrowserController, respectively. After going through the steps to load the SimpleBrowserController's view when the app finishes launching as usual, open the SimpleBrowserViewController in Interface Builder, where we'll do some of the work for this example.

For this example of a web browser, we'll support back, forward, and refresh functionality. We'll allow URL entry using a UITextField with a keyboard type of URL and a return key of "Go," which we can set in the Text Input Traits section of IB. Also, we'll nest all the navigation controls in a UIToolbar. Figure 4.12 shows the final setup of everything in IB, including the required outlet connections.

Figure 4.12

Figure 4.12 SimpleBrowserController's view in Interface Builder

We want the URL entered in the UITextField to result in navigation to the web page when the user selects Go on the keyboard. To achieve this we use ShouldReturn on the UITextField. The function we assign to ShouldReturn takes care of building the NSUrlRequest from the URL entered by the user. The UIWebview's LoadRequest method takes an NSUrlRequest, which it uses to load the web page. Also, to make the keyboard disappear, we call ResignFirstResponder on the UITextField.

urlTextField.ShouldReturn = textField =>
{
    textField.ResignFirstResponder ();
    string url = textField.Text;
    if (!url.StartsWith ("http"))
        url = String.Format ("http://{0}", url);
    NSUrl nsurl = new NSUrl (url);
    NSUrlRequest req = new NSUrlRequest (nsurl);
    webView.LoadRequest (req);
    return true;
};

In addition to LoadRequest, the UIWebView comes with other features to control navigation, such as moving back and forward through the page history and reloading the current page. These are implemented with the methods GoBack, GoForward, and Reload, respectively. In our example, we wire these up to the appropriate buttons:

backButton.Clicked += delegate { webView.GoBack (); };
forwardButton.Clicked += delegate { webView.GoForward (); };
refreshButton.Clicked += delegate { webView.Reload ();

Putting these snippets together in the SimpleBrowserController's ViewDidLoad implementation gives us a simple running browser application where we can navigate to web pages and scroll them. However, our implementation does not support pinch zooming and does not fit the page to the screen by default. To include zoom support and fit-the-page functionality, simply set the ScalesPageToFit property to true. The resulting application is shown in Figure 4.13.

Figure 4.13

Figure 4.13 Simple browser application

ADBannerView

With iOS 4, Apple introduced the iAds program, which allows developers to easily include advertisements in their applications. The technology you use to include them is the ADBannerView, found in the MonoTouch.iAd namespace. iAds are supported on any device running iOS 4.x. Initially, this meant only iPhone and iPod Touch, but as of iOS 4.2, the iPad is also included.

For this example, we'll build a universal application for both the iPhone and iPad. Universal apps include support for native UI optimized for both the iPhone and iPad in a single executable, as opposed to the pixel doubling you'll get on the iPad if you only target the iPhone. We'll discuss this at length later in the book. For now, we're just making a universal app to demonstrate iAds on both devices.

Create a new project named LMT4-6 using the Universal Window-based Project template. Note that two files are added to this project. The MainWindowIPad.xib file is used when running the app on an iPad and the MainWindowIPhone.xib file is used on an iPhone. Along with these files are separate AppDelegate files for each target.

Working with the ADBannerView is just like working with any other view. For callbacks, you can use the ADBannerViewDelegate or the .NET events that abstract it. Because this is a universal app, you'll need to add an iPad view with a controller in addition to an iPhone view with a controller. The process for adding the controller's view to the screen is the same as in previous examples, where you added the controller to MainWindow.xib, set the class and xib name, connected an outlet to the controller from the AppDelegate, and loaded the controller's view in FinishedLaunching. The only difference here is you'll have to do it for both the iPad version of the various classes as well as the iPhone version.

For the view controllers, use the names DemoADIPad and DemoADIPhone, respectively. Using IB, add an ADBannerView to the views for each, associating connections named adBanner in both cases. Without any additional code, this would result in a test ad loading. However, there are a couple things you need to handle.

First, in the event an ad doesn't load, you need to take care of hiding the banner. Subsequently, any further successful loading of ads needs to ensure the banner is visible. You can handle these situations using the FailedToReceiveAd and AdLoaded events, respectively.

Second, if you want to support multiple orientations in your applications, you will need to size the adBanner appropriately. You never should size it directly, though. You should size it implicitly by setting the CurrentContentSizeIdentifier. Also, the orientations you need are specified via the RequireSizeIdentifiers property, which defaults to both landscape and portrait. This property controls what banner view images are actually downloaded. For example, if you don't support multiple orientations in your application, you can tune the ad content to only download ad images for what you need.

To support rotating your view to both landscape and portrait orientations here, we simply override ShouldRotateToInterfaceOrientation, returning true. To introduce the aforementioned code to set the ADBannerView's currentContentSizeidentifier, you override WillRotate, setting the value appropriately for the orientation that is about to be rotated to. Listing 4.4 shows the controller implementation for the iPhone, with the iPad's implementation being identical. The resulting app, running on both the iPad and iPhone simulators, is shown in Figure 4.15, along with the test ad you'll get when touching the ADBannerView.

Figure 4.15

Figure 4.15 ADBannerView along with a test ad display

Listing 4.4. View Controller Implementing iAD Support

public partial class DemoADIPhone : UIViewController
{
    ...
    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();

        adBanner.AdLoaded += (s, e) => {
            Console.WriteLine ("Ad Loaded");
            ((ADBannerView)s).Hidden = false;
        };

        adBanner.FailedToReceiveAd += delegate(object sender,
            AdErrorEventArgs e)
        {
            Console.WriteLine("Ad failed to load. Error code = {0}",
                e.Error.Code);
            ((ADBannerView)sender).Hidden = true;
        };
    }

    public override void WillRotate (
        UIInterfaceOrientation toInterfaceOrientation, double duration)
    {
        base.WillRotate (toInterfaceOrientation, duration);
        if ((toInterfaceOrientation ==
                UIInterfaceOrientation.LandscapeLeft) ||
           (toInterfaceOrientation ==
                UIInterfaceOrientation.LandscapeRight))
        {
            adBanner.CurrentContentSizeIdentifier =
                ADBannerView.SizeIdentifierLandscape;
        }
        else
        {
            adBanner.CurrentContentSizeIdentifier =
                ADBannerView.SizeIdentifierPortrait;
        }
    }

    public override bool ShouldAutorotateToInterfaceOrientation
        (UIInterfaceOrientation toInterfaceOrientation)
    {
        return true;
    }
}
  • + Share This
  • 🔖 Save To Your Account