iOS Designer でのカスタム コントロールの使用

警告

iOS Designer は、Visual Studio 2019 バージョン 16.8 および Visual Studio 2019 for Mac バージョン 8.8 で非推奨とされ、Visual Studio 2019 バージョン 16.9 および Visual Studio for Mac バージョン 8.9 から削除されています。 iOS ユーザー インターフェイスを構築するには、Xcode を実行している Mac 上で直接構築することをお勧めします。 詳細については、「Xcode を使用したユーザーインターフェイスの設計」を参照してください。

要件

Xamarin Designer for iOS は、Visual Studio for Mac と Windows 上の Visual Studio 2017 以降で利用できます。

これらのガイドでは、「入門ガイド」で説明されている内容を理解していることが想定されています。

チュートリアル

重要

Xamarin.Studio 5.5 以降では、カスタム コントロールを作成する方法は、以前のバージョンとは若干異なります。 カスタム コントロールを作成するには、IComponent インターフェイスが必要 (関連する実装メソッドを含む) か、クラスに [DesignTimeVisible(true)] の注釈を付けることができるかのいずれかです。 次のチュートリアルの例では、後者の方法を使用しています。

  1. [iOS] > [アプリ] > [単一ビュー アプリケーション] > [C#] テンプレートから新しいソリューションを作成し、これに ScratchTicket の名前を付け、新しいプロジェクト ウィザードを続行します。

    新しいソリューションの作成

  2. ScratchTicketView という名前の新しい空のクラス ファイルを作成します。

    新しい ScratchTicketView クラスを作成する

  3. ScratchTicketView クラスに次のコードを追加します。

    using System;
    using System.ComponentModel;
    using CoreGraphics;
    using Foundation;
    using UIKit;
    
    namespace ScratchTicket
    {
        [Register("ScratchTicketView"), DesignTimeVisible(true)]
        public class ScratchTicketView : UIView
        {
            CGPath path;
            CGPoint initialPoint;
            CGPoint latestPoint;
            bool startNewPath = false;
            UIImage image;
    
            [Export("Image"), Browsable(true)]
            public UIImage Image
            {
                get { return image; }
                set
                {
                    image = value;
                    SetNeedsDisplay();
                }
            }
    
            public ScratchTicketView(IntPtr p)
                : base(p)
            {
                Initialize();
            }
    
            public ScratchTicketView()
            {
                Initialize();
            }
    
            void Initialize()
            {
                initialPoint = CGPoint.Empty;
                latestPoint = CGPoint.Empty;
                BackgroundColor = UIColor.Clear;
                Opaque = false;
                path = new CGPath();
                SetNeedsDisplay();
            }
    
            public override void TouchesBegan(NSSet touches, UIEvent evt)
            {
                base.TouchesBegan(touches, evt);
    
                var touch = touches.AnyObject as UITouch;
    
                if (touch != null)
                {
                    initialPoint = touch.LocationInView(this);
                }
            }
    
            public override void TouchesMoved(NSSet touches, UIEvent evt)
            {
                base.TouchesMoved(touches, evt);
    
                var touch = touches.AnyObject as UITouch;
    
                if (touch != null)
                {
                    latestPoint = touch.LocationInView(this);
                    SetNeedsDisplay();
                }
            }
    
            public override void TouchesEnded(NSSet touches, UIEvent evt)
            {
                base.TouchesEnded(touches, evt);
                startNewPath = true;
            }
    
            public override void Draw(CGRect rect)
            {
                base.Draw(rect);
    
                using (var g = UIGraphics.GetCurrentContext())
                {
                    if (image != null)
                        g.SetFillColor((UIColor.FromPatternImage(image).CGColor));
                    else
                        g.SetFillColor(UIColor.LightGray.CGColor);
                    g.FillRect(rect);
    
                    if (!initialPoint.IsEmpty)
                    {
                        g.SetLineWidth(20);
                        g.SetBlendMode(CGBlendMode.Clear);
                        UIColor.Clear.SetColor();
    
                        if (path.IsEmpty || startNewPath)
                        {
                            path.AddLines(new CGPoint[] { initialPoint, latestPoint });
                            startNewPath = false;
                        }
                        else
                        {
                            path.AddLineToPoint(latestPoint);
                        }
    
                        g.SetLineCap(CGLineCap.Round);
                        g.AddPath(path);
                        g.DrawPath(CGPathDrawingMode.Stroke);
                    }
                }
            }
        }
    }
    
  4. FillTexture.pngFillTexture2.pngMonkey.png ファイル (GitHub から入手可能) を [リソース] フォルダーに追加します。

  5. Main.storyboard ファイルをダブルクリックして、デザイナーで開きます。

    iOS Designer

  6. イメージ ビューツールボックスからストーリーボードのビューにドラッグ アンド ドロップします。

    レイアウトに追加されたイメージ ビュー

  7. [イメージ ビュー]を選択し、その [イメージ] プロパティを Monkey.png に変更します。

    イメージ ビューのイメージ プロパティを Monkey.png に設定する

  8. サイズ クラスを使用しているため、このイメージ ビューを制約する必要があります。 イメージを 2 回クリックして、制約モードにします。 中心固定ハンドルをクリックして中心に制約し、垂直方向と水平方向の両方に揃えます。

    画像の中央揃え

  9. 高さと幅を制約するには、サイズ固定ハンドル ('骨' の形のハンドル) をクリックし、それぞれ幅と高さを選択します。

    制約の追加

  10. ツールバーの [更新] ボタンをクリックして、制約に基づいてフレームを更新します。

    制約ツール バー

  11. 次に、[スクラッチ チケット ビュー] がツールボックスの [カスタム コンポーネント] の下に表示されるようにプロジェクトをビルドします。

    [カスタム コンポーネント] ツールボックス

  12. サルのイメージを覆うように表示されるように、[スクラッチ チケット ビュー] をドラッグ アンド ドロップします。 以下に示すように、スクラッチ チケット ビューがサルを完全に覆うようにドラッグ ハンドルを調整します。

    イメージ ビューのスクラッチ チケット ビュー

  13. 両方のビューを選択するために外接する四角形を描画することで、スクラッチ チケット ビューをイメージ ビューに制約します。 次に示すように、幅、高さ、中心、中央に制約するオプションを選択し、制約に基づいてフレームを更新します。

    制約の中央揃えと追加

  14. アプリケーションを実行し、画像を "スクラッチ オフ" するとサルが現れます。

    サンプル アプリの実行

デザイン時のプロパティの追加

デザイナーには、プロパティ型の数値、列挙型、文字列、ブール値、CGSize、UIColor、UIImage などのカスタム コントロールのデザイン時のサポートも含まれています。 デモを行うために、"スクラッチ オフ" されたイメージを設定する ScratchTicketView にプロパティを追加してみましょう。

次のコードをプロパティの ScratchTicketView クラスに追加します。

[Export("Image"), Browsable(true)]
public UIImage Image
{
    get { return image; }
    set {
            image = value;
              SetNeedsDisplay ();
        }
}

次のように、Draw メソッドに null チェックを追加することもできます。

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

    using (var g = UIGraphics.GetCurrentContext())
    {
        if (image != null)
            g.SetFillColor ((UIColor.FromPatternImage (image).CGColor));
        else
            g.SetFillColor (UIColor.LightGray.CGColor);

        g.FillRect(rect);

        if (!initialPoint.IsEmpty)
        {
             g.SetLineWidth(20);
             g.SetBlendMode(CGBlendMode.Clear);
             UIColor.Clear.SetColor();

             if (path.IsEmpty || startNewPath)
             {
                 path.AddLines(new CGPoint[] { initialPoint, latestPoint });
                 startNewPath = false;
             }
             else
             {
                 path.AddLineToPoint(latestPoint);
             }

             g.SetLineCap(CGLineCap.Round);
             g.AddPath(path);
             g.DrawPath(CGPathDrawingMode.Stroke);
        }
    }
}

ExportAttributeBrowsableAttribute を含み、引数を true に設定すると、プロパティはデザイナーの [プロパティ] パネルに表示されます。 プロパティを FillTexture2.png などのプロジェクトに含まれる別のイメージに変更すると、以下に示すようにデザイン時にコントロールが更新されます。

デザイン時のプロパティの編集

まとめ

この記事では、カスタム コントロールを作成する方法と、iOS Designer を使用する iOS アプリケーションで使用する方法について説明しました。 デザイナーの [ツールボックス] にあるアプリケーションで使用できるようにするために、コントロールを作成してビルドする方法を確認しました。 さらに、デザイン時と実行時の両方で適切にレンダリングされるようにコントロールを実装する方法と、デザイナーでカスタム コントロール プロパティを公開する方法についても確認しました。