Xamarin.iOS でのコードでの iOS ユーザー インターフェイスの作成

iOS アプリのユーザー インターフェイスは店舗の正面のようなものです。アプリケーションは、通常、1 つのウィンドウを取得しますが、そのウィンドウいっぱいに必要な数のオブジェクトを表示し、アプリが表示したい内容に応じてオブジェクトの配置を変更することもできます。 このシナリオのオブジェクト (ユーザーに表示される物事) はビューと呼ばれます。 アプリケーションで 1 つの画面をビルドするには、コンテンツ ビュー階層にビューが相互に積み重ねられ、階層が単一のビュー コントローラーによって管理されます。 複数の画面を持つアプリケーションには、複数のコンテンツ ビュー階層、それぞれに独自のビュー コントローラー、およびウィンドウ内のアプリケーションの場所のビューがあり、ユーザーに表示される画面に基づいて異なるコンテンツ ビュー階層を作成します。

次の図は、デバイスの画面にユーザー インターフェイスを表示するウィンドウ、ビュー、サブビュー、およびビュー コントローラー間の関係を示しています。

This diagram illustrates the relationships between the Window, Views, Subviews, and View Controller

これらのビュー階層は Xcode の Interface Builder を使用して構築できますが、コード内で全ての操作を行う方法を基本的に理解することをお勧めします。 この記事では、コードのみのユーザー インターフェイス開発に着手して作業を進めるための基本的なポイントについて説明します。

コードのみのプロジェクトの作成

iOS の空のプロジェクト テンプレート

まず、次に示すように、[ファイル] > [新しいプロジェクト] > [Visual C#] > [iPhone および iPad] > [iOS App (Xamarin)] プロジェクトを使用して、Visual Studio で iOS プロジェクトを作成します。

New Project Dialog

次に、[空のアプリ] プロジェクト テンプレートを選択します。

Select a Template Dialog

空のプロジェクト テンプレートは、プロジェクトに 4 つのファイルを追加します。

Project Files

  1. AppDelegate.cs - iOS からのアプリケーション イベントを処理するために使用される、UIApplicationDelegate サブクラス AppDelegate が含まれています。 アプリケーション ウィンドウは、AppDelegateFinishedLaunching メソッドに作成されます。
  2. Main.cs - AppDelegate のクラスを指定するアプリケーションのエントリ ポイントを保存します。
  3. Info.plist - アプリケーション構成情報を含むプロパティ リスト ファイル。
  4. Entitlements.plist – アプリケーションの機能とアクセス許可に関する情報を含むプロパティ リスト ファイル。

iOS アプリケーションは、MVC パターンを使用して構築されます。 アプリケーションが表示する最初の画面は、ウィンドウのルート ビュー コントローラーから作成されます。 MVC パターン自体の詳細については、「Hello、iOS マルチスクリーン」ガイドを参照してください。

テンプレートによって追加された AppDelegate の実装により、アプリケーション ウィンドウが作成されます。このウィンドウは、すべての iOS アプリケーションに対して 1 つだけ存在し、次のコードで表示されます。

public class AppDelegate : UIApplicationDelegate
{
    public override UIWindow Window
            {
                get;
                set;
            }

    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        // create a new window instance based on the screen size
        Window = new UIWindow(UIScreen.MainScreen.Bounds);

        // make the window visible
        Window.MakeKeyAndVisible();

        return true;
    }
}

このアプリケーションをこの状態で実行すると、Application windows are expected to have a root view controller at the end of application launch という例外がスローされる可能性があります。 コントローラーを追加し、それをアプリのルート ビュー コントローラーにしましょう。

コントローラーを追加する

アプリには多数のビュー コントローラーを含めることができますが、すべてのビュー コントローラーを制御するための 1 つのルート ビュー コントローラーが必要です。 UIViewController インスタンスを作成し、Window.RootViewController プロパティに設定して、コントローラーをウィンドウに追加します。

public class AppDelegate : UIApplicationDelegate
{
    // class-level declarations

    public override UIWindow Window
    {
        get;
        set;
    }

    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
        // create a new window instance based on the screen size
        Window = new UIWindow(UIScreen.MainScreen.Bounds);

        var controller = new UIViewController();
        controller.View.BackgroundColor = UIColor.LightGray;

        Window.RootViewController = controller;

        // make the window visible
        Window.MakeKeyAndVisible();

        return true;
    }
}

すべてのコントローラーには、View プロパティからアクセスできるビューが関連付けられています。 上記のコードでは、ビューの BackgroundColor プロパティが UIColor.LightGray に変更され、表示されるようになります。

The View's background is a visible light gray

UIKit のコントローラーや自分で記述したコントローラーなど、任意の UIViewController サブクラスをこの方法で RootViewController として設定することもできます。 たとえば、次のコードは、RootViewController として UINavigationController を追加します。

public class AppDelegate : UIApplicationDelegate
{
    // class-level declarations

    public override UIWindow Window
    {
        get;
        set;
    }

    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
        // create a new window instance based on the screen size
        Window = new UIWindow(UIScreen.MainScreen.Bounds);

        var controller = new UIViewController();
        controller.View.BackgroundColor = UIColor.LightGray;
        controller.Title = "My Controller";

        var navController = new UINavigationController(controller);

        Window.RootViewController = navController;

        // make the window visible
        Window.MakeKeyAndVisible();

        return true;
    }
}

これにより、次に示すように、ナビゲーション コントローラー内に入れ子になったコントローラーが生成されます。

The controller nested within the navigation controller

ビュー コントローラーの作成

ウィンドウの RootViewController としてコントローラーを追加する方法を見てきたので、コードでカスタム ビュー コントローラーを作成する方法を見てみましょう。

次に示すように、CustomViewController という名前の新しいクラスを追加します。

このクラスは、次に示すように、UIKit 名前空間の UIViewController から継承しています。

using System;
using UIKit;

namespace CodeOnlyDemo
{
    class CustomViewController : UIViewController
    {
    }
}

ビューの初期化

UIViewController には、View コントローラーが最初にメモリに読み込まれるときに呼び出される ViewDidLoad と呼ばれるメソッドが含まれています。 これは、ビューのプロパティの設定など、ビューの初期化を行うのに適した場所です。

たとえば、次のコードは、ボタンが押されたときにナビゲーション スタックに新しいビュー コントローラーをプッシュするボタンとイベント ハンドラーを追加します。

using System;
using CoreGraphics;
using UIKit;

namespace CodyOnlyDemo
{
    public class CustomViewController : UIViewController
    {
        public CustomViewController ()
        {
        }

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

            View.BackgroundColor = UIColor.White;
            Title = "My Custom View Controller";

            var btn = UIButton.FromType (UIButtonType.System);
            btn.Frame = new CGRect (20, 200, 280, 44);
            btn.SetTitle ("Click Me", UIControlState.Normal);

            var user = new UIViewController ();
            user.View.BackgroundColor = UIColor.Magenta;

            btn.TouchUpInside += (sender, e) => {
                this.NavigationController.PushViewController (user, true);
            };

            View.AddSubview (btn);

        }
    }
}

このコントローラーをアプリケーションに読み込み、簡単なナビゲーションを例示するために、CustomViewController の新しいインスタンスを作成します。 新しいナビゲーション コントローラーを作成し、ビュー コントローラー インスタンスを渡し、前と同様に新しいナビゲーション コントローラーを AppDelegate のウィンドウの RootViewController に設定します。

var cvc = new CustomViewController ();

var navController = new UINavigationController (cvc);

Window.RootViewController = navController;

アプリケーションが読み込まれると、CustomViewController がナビゲーション コントローラー内に読み込まれます。

The CustomViewController is loaded inside a navigation controller

ボタンをクリックすると、新しいビュー コントローラーがナビゲーション スタックに "プッシュ" されます。

A new View Controller pushed onto the navigation stack

ビュー階層の構築

上記の例では、ビュー コントローラーにボタンを追加して、コード内にユーザー インターフェイスを作成し始めました。

iOS ユーザー インターフェイスは、ビュー階層で構成されます。 ラベル、ボタン、スライダーなどの追加のビューは、親ビューのサブビューとして追加されます。

例として、CustomViewController を編集してユーザーがユーザー名とパスワードを入力できるログイン画面を作成しましょう。 画面は、2 つのテキスト フィールドとボタンで構成されます。

テキスト フィールドの追加

最初に、[ビューの初期化] セクションで追加したボタンとイベント ハンドラーを削除します。

次に示すように、UITextField を作成して初期化し、ビュー階層に追加することで、ユーザー名のコントロールを追加します。

class CustomViewController : UIViewController
{
    UITextField usernameField;

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

        View.BackgroundColor = UIColor.Gray;

        nfloat h = 31.0f;
        nfloat w = View.Bounds.Width;

        usernameField = new UITextField
        {
            Placeholder = "Enter your username",
            BorderStyle = UITextBorderStyle.RoundedRect,
            Frame = new CGRect(10, 82, w - 20, h)
        };

        View.AddSubview(usernameField);
    }
}

UITextField を作成するときに、Frame プロパティを設定して、その場所とサイズを定義します。 iOS では、0,0 座標は左上にあり、+x は右方向、+y は下方向になります。 Frame を他のいくつかのプロパティと共に設定した後、View.AddSubview を呼び出してビュー階層に UITextField を追加します。 これにより、usernameField は、View プロパティが参照する UIView インスタンスのサブビューになります。 サブビューは、親ビューより高い z オーダーで追加されるため、画面上の親ビューの前面に表示されます。

UITextField が含まれているアプリケーションを次に示します。

The application with the UITextField included

次に示すように、パスワードの UITextField を同様の方法で追加できます。しかし今回は SecureTextEntry プロパティを true に設定します。

public class CustomViewController : UIViewController
{
    UITextField usernameField, passwordField;
    public override void ViewDidLoad()
    {
       // keep the code the username UITextField
        passwordField = new UITextField
        {
            Placeholder = "Enter your password",
            BorderStyle = UITextBorderStyle.RoundedRect,
            Frame = new CGRect(10, 114, w - 20, h),
            SecureTextEntry = true
        };

      View.AddSubview(usernameField);
      View.AddSubview(passwordField);
   }
}

SecureTextEntry = true を設定すると、次に示すように、ユーザーが UITextField に入力したテキストが非表示になります。

Setting SecureTextEntry true hides the text entered by the user

ボタンの追加

次に、ユーザーがユーザー名とパスワードを送信できるようにボタンを追加します。 ボタンは、他のコントロールと同様、親ビューの AddSubview メソッドに引数として渡すことによってビュー階層に追加されます。

次のコードは、ボタンを追加し、TouchUpInside イベントのイベント ハンドラーを登録します。

var submitButton = UIButton.FromType (UIButtonType.RoundedRect);

submitButton.Frame = new CGRect (10, 170, w - 20, 44);
submitButton.SetTitle ("Submit", UIControlState.Normal);

submitButton.TouchUpInside += (sender, e) => {
    Console.WriteLine ("Submit button pressed");
};

View.AddSubview(submitButton);

これで、ログイン画面が次のように表示されます。

The login screen

以前のバージョンの iOS とは異なり、既定のボタンの背景は透明です。 これはボタンの BackgroundColor プロパティを変更することで変更できます。

submitButton.BackgroundColor = UIColor.White;

これにより、一般的な丸い枠付きボタンではなく、正方形のボタンが生成されます。 丸い枠を使用するには、次のスニペットを使用します。

submitButton.Layer.CornerRadius = 5f;

これらの変更により、ビューは次のようになります。

An example run of the view

ビュー階層への複数のビューの追加

iOS には、AddSubviews を使用してビュー階層に複数のビューを追加する機能が用意されています。

View.AddSubviews(new UIView[] { usernameField, passwordField, submitButton });

ボタン機能の追加

ボタンがクリックされると、ユーザーは何かが起こることを期待します。 たとえば、アラートが表示されたり、別の画面へとナビゲーションが実行されたりします。

ナビゲーション スタックに 2 つ目のビュー コントローラーをプッシュするコードを追加してみましょう。

最初に、2 つ目のビュー コントローラーを作成します。

var loginVC = new UIViewController () { Title = "Login Success!"};
loginVC.View.BackgroundColor = UIColor.Purple;

次に、TouchUpInside イベントに機能を追加します。

submitButton.TouchUpInside += (sender, e) => {
                this.NavigationController.PushViewController (loginVC, true);
            };

ナビゲーションを次に示します。

The navigation is illustrated in this chart

既定では、ナビゲーション コントローラーを使用すると、iOS によってアプリケーションにナビゲーション バーと戻るボタンが表示され、スタック内を戻ることができることがわかります。

ビュー階層の反復処理

サブビュー階層を反復処理し、特定のビューを選択することができます。 たとえば、各 UIButton を検索し、そのボタンに別の BackgroundColor を与えるために、次のスニペットを使用できます。

foreach(var subview in View.Subviews)
{
    if (subview is UIButton)
    {
         var btn = subview as UIButton;
         btn.BackgroundColor = UIColor.Green;
    }
}

ただし、これは、反復処理対象のビューが UIViewである場合は機能しません。親ビューに追加されたオブジェクト自体が UIView を継承することから、すべてのビューが UIView として返されるためです。

回転の処理

次のスクリーンショットに示すように、ユーザーがデバイスを横向きに回転させる場合、コントロールのサイズは適切に変更されません。

If the user rotates the device to landscape, the controls do not resize appropriately

これを修正する方法の 1 つは、各ビューで AutoresizingMask プロパティを設定する方法です。 この場合、コントロールを水平方向に拡大したいため、AutoresizingMask をそれぞれ設定します。 次の例は usernameField 用ですが、ビュー階層内の各ガジェットにも同じ値を適用する必要があります。

usernameField.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;

次に示すように、デバイスまたはシミュレーターを回転させると、すべてが拡張され、追加の領域が埋まるようにします。

All the controls stretch to fill the additional space

カスタム ビューの作成

UIKit の一部であるコントロールを使用するだけでなく、カスタム ビューを使用することもできます。 カスタム ビューは、UIView から継承し、Draw をオーバーライドすることによって作成できます。 カスタム ビューを作成し、それをビュー階層に追加してデモを行いましょう。

UIView からの継承

最初に行う必要があるのは、カスタム ビューのクラスを作成することです。 これを行うには、Visual Studio のクラス テンプレートを使用して、CircleView という名前の空のクラスを追加します。 基底クラスは UIView に設定する必要があります。これは、UIKit 名前空間に存在しています。 System.Drawing 名前空間も必要になります。 この例では、他のさまざまな System.* 名前空間は使用されないため、自由に削除してください。

クラスは次のようになります。

using System;

namespace CodeOnlyDemo
{
    class CircleView : UIView
    {
    }
}

UIView での描画

すべての UIView には、描画する必要があるときにシステムによって呼び出される Draw メソッドがあります。 Draw は直接呼び出すべきではありません。 これは、実行ループの処理中にシステムによって呼び出されます。 実行ループを介してビューがビュー階層に初めて追加されると、その Draw メソッドが呼び出されます。 後続の Draw に対する呼び出しは、ビューで SetNeedsDisplay または SetNeedsDisplayInRect を呼び出すことでビューが描画が必要としてマークされたときに発生します。

次に示すように、オーバーライドされた Draw メソッド内にこのようなコードを追加することで、ビューに描画コードを追加できます。

public override void Draw(CGRect rect)
{
    base.Draw(rect);

    //get graphics context
    using (var g = UIGraphics.GetCurrentContext())
    {
        // set up drawing attributes
        g.SetLineWidth(10.0f);
        UIColor.Green.SetFill();
        UIColor.Blue.SetStroke();

        // create geometry
        var path = new CGPath();
        path.AddArc(Bounds.GetMidX(), Bounds.GetMidY(), 50f, 0, 2.0f * (float)Math.PI, true);

        // add geometry to graphics context and draw
        g.AddPath(path);
        g.DrawPath(CGPathDrawingMode.FillStroke);
    }
}

CircleViewUIViewであるため、UIView プロパティも設定できます。 たとえば、コンストラクターで BackgroundColor を設定できます。

public CircleView()
{
    BackgroundColor = UIColor.White;
}

先ほど作成した CircleView を使用するには、前の UILabelsUIButton と同様に、既存のコントローラーのビュー階層にサブビューとして追加するか、新しいコントローラーのビューとして読み込むことができます。 後者を実行してみましょう。

ビューの読み込み

UIViewController には、ビューを作成するためにコントローラーによって呼び出される LoadView という名前のメソッドがあります。 これは、ビューを作成し、コントローラーの View プロパティに割り当てるのに適した場所です。

まず、コントローラーが必要なので、CircleController という名前の新しい空のクラスを作成します。

CircleController で次のコードを追加して、ViewCircleView に設定します (オーバーライドでは base 実装を呼び出さないでください)。

using UIKit;

namespace CodeOnlyDemo
{
    class CircleController : UIViewController
    {
        CircleView view;

        public override void LoadView()
        {
            view = new CircleView();
            View = view;
        }
    }
}

最後に、実行時にコントローラーを提示する必要があります。 これを行うには、前に追加した送信ボタンにイベント ハンドラーを次のように追加します。

submitButton.TouchUpInside += delegate
{
    Console.WriteLine("Submit button clicked");

    //circleController is declared as class variable
    circleController = new CircleController();
    PresentViewController(circleController, true, null);
};

アプリケーションを実行して送信ボタンをタップすると、新しいビューに円が表示されます。

The new view with a circle is displayed

起動画面の作成

起動画面 は、アプリの起動時に、応答していることをユーザーに示す方法として表示されます。 起動画面はアプリの読み込み時に表示されるため、アプリケーションがまだメモリに読み込まれているため、コード内に作成することはできません。

Visual Studio で iOS プロジェクトを作成すると、起動画面は .xib ファイルの形式で提供されます。これは、プロジェクト内の Resources フォルダーにあります。

これを編集するには、ダブルクリックして Xcode Interface Builder で開きます。

Apple では、iOS 8 以降をターゲットとするアプリケーションでは .xib またはストーリーボード ファイルを使用することを推奨しています。Xcode Interface Builder でいずれかのファイルを起動すると、サイズ クラスと自動レイアウトを使用して、すべてのデバイス サイズに対して適切に表示されるようにレイアウトを調整できます。 静的起動イメージは、.xib または ストーリーボードに追加して使用することで、以前のバージョンを対象とするアプリケーションのサポートを可能にすることができます。

起動画面の作成の詳細については、以下のドキュメントを参照してください。

重要

iOS 9 の時点で、Apple では、起動画面を作成する主な方法としてストーリーボードを使用することを推奨しています。

iOS 8 より前のアプリケーションの起動イメージの作成

アプリケーションが iOS 8 より前のバージョンを対象とする場合は、.xib またはストーリーボードの起動画面に加えて静的イメージを使用できます。

この静的イメージは、Info.plist ファイルで設定することも、アプリケーションのアセット カタログ (iOS 7 の場合) として設定することもできます。 アプリケーションを実行できるデバイス サイズ (320x480、640x960、640x1136) ごとに個別のイメージを指定する必要があります。 起動画面のサイズの詳細については、「起動画面イメージ」ガイドを参照してください。

重要

アプリに起動画面がない場合は、画面に完全に収まっていないことがあります。 この場合は、Info.plist に Default-568@2x.png という名前の 640x1136 イメージが最小限含まれている必要があります。

まとめ

この記事では、Visual Studio で iOS アプリケーションをプログラムで開発する方法について説明しました。 空のプロジェクト テンプレートからプロジェクトをビルドする方法について説明し、ルート ビュー コントローラーを作成してウィンドウに追加する方法について説明しました。 次に、UIKit のコントロールを使用して、コントローラー内にビュー階層を作成し、アプリケーション画面を開発する方法について説明しました。 次に、ビューをさまざまな向きで適切にレイアウトする方法を調べ、UIView をサブクラス化してカスタム ビューを作成する方法と、コントローラー内でビューを読み込む方法を確認しました。 最後に、アプリケーションに起動画面を追加する方法について説明しました。