Xamarin Designer for iOS のカスタム コントロール

Xamarin Designer for iOS では、プロジェクトで作成された、または Xamarin コンポーネント ストアなどの外部ソースから参照される、カスタム コントロールのレンダリングがサポートされています。

警告

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 は、アプリケーションのユーザー インターフェイスを視覚化するための強力なツールであり、ほとんどの iOS ビューとビュー コントローラーに対して WYSIWYG 編集サポートを提供します。 お使いのアプリには、iOS に組み込まれているコントロールを拡張するカスタム コントロールが含まれている場合もあります。 これらのカスタム コントロールがいくつかのガイドラインを念頭に置いて作成されている場合は、iOS Designer でレンダリングすることもできます。これにより、さらに豊富な編集エクスペリエンスが提供されます。 このドキュメントでは、これらのガイドラインについて説明します。

要件

次のすべての要件を満たすコントロールがデザイン サーフェイスにレンダリングされます。

  1. UIView または UIViewController の直接的または間接的なサブクラスである。 他の NSObject サブクラスが、デザイン サーフェスにアイコンとして表示される。
  2. Objective-C に公開するための RegisterAttribute がある。
  3. 必要な IntPtr コンストラクターがある。
  4. IComponent インターフェイスが実装されているか、True に設定された DesignTimeVisibleAttribute がある。

上記の要件を満たすコードで定義されたコントロールは、含まれているプロジェクトがシミュレーター用にコンパイルされるときにデザイナーに表示されます。 既定では、すべてのカスタム コントロールがツールボックス[カスタム コンポーネント] セクションに 表示されます。 ただし、CategoryAttribute をカスタム コントロールのクラスに適用して、別のセクションを指定することができます。

このデザイナーでは、サード パーティ製の Objective-C ライブラリの読み込みはサポートされていません。

アセチルサリチル酸アンジュール -ある犬の物語-

次の条件が満たされると、カスタム コントロールによって宣言されたプロパティがプロパティ パネルに表示されます。

  1. プロパティに公開されたゲッターとセッターがある。
  2. プロパティに、True に設定された ExportAttributeBrowsableAttribute がある。
  3. プロパティの型が、数値型、列挙型、文字列、ブール、SizeFUIColorUIImage のいずれかである。 サポートされている型のこのリストは、今後拡張される可能性があります。

プロパティを DisplayNameAttribute で装飾して、プロパティ パネルに表示されるラベルを指定することもできます。

初期化

UIViewController サブクラスの場合、デザイナーで作成したビューに依存するコードに ViewDidLoad メソッドを使用する必要があります。

UIView およびその他の NSObject サブクラスの場合、カスタム コントロールをレイアウト ファイルから読み込んだ後に初期化を実行する場所として、AwakeFromNib メソッドをお勧めします。 これは、プロパティ パネルで設定されたカスタム プロパティは、コントロールのコンストラクターの実行時には設定されず、AwakeFromNib が呼び出される前に設定されるためです。

[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {

    public CustomView (IntPtr handle) : base (handle) { }

    public override void AwakeFromNib ()
    {
        // Initialize the view here.
    }
}

コントロールがコードから直接作成されるように設計されている場合は、次のような共通の初期化コードを持つメソッドを作成できます。

[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {

    public CustomView (IntPtr handle) : base (handle) { }

    public CustomView ()
    {
        // Called when created from code.
        Initialize ();
    }

    public override void AwakeFromNib ()
    {
        // Called when loaded from xib or storyboard.
        Initialize ();
    }

    void Initialize ()
    {
        // Common initialization code here.
    }
}

プロパティの初期化と AwakeFromNib

iOS Designer 内で設定された値を上書きしないように、カスタム コンポーネントのデザイン可能なプロパティを初期化するタイミングと場所に注意する必要があります。 例として、次のコードを取り上げます。

[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {

    [Export ("Counter"), Browsable (true)]
    public int Counter {get; set;}

    public CustomView (IntPtr handle) : base (handle) { }

    public CustomView ()
    {
        // Called when created from code.
        Initialize ();
    }

    public override void AwakeFromNib ()
    {
        // Called when loaded from xib or storyboard.
        Initialize ();
    }

    void Initialize ()
    {
        // Common initialization code here.
        Counter = 0;
    }
}

CustomView コンポーネントは、開発者が iOS Designer 内で設定できる Counter プロパティを公開します。 ただし、デザイナー内で設定されている値に関係なく、Counter プロパティの値は常にゼロ (0) になります。 その理由を説明します。

  • CustomControl のインスタンスは、ストーリーボード ファイルから拡張されるため。
  • iOS Designer で変更されたすべてのプロパティが設定されるため (たとえば、Counter の値を 2 に設定するなど)。
  • AwakeFromNib メソッドが実行され、コンポーネントの Initialize メソッドが呼び出されるため。
  • Initialize 内部では、Counter プロパティの値がゼロ (0) にリセットされるため。

上記の状況を修正するには、Counter プロパティを別の場所 (コンポーネントのコンストラクター内など) で初期化するか、AwakeFromNib メソッドをオーバーライドせずに、コンポーネントがコンストラクターによって現在処理されている以外の初期化が必要ない場合は Initialize を呼び出します。

デザイン モード

デザイン サーフェイスでは、カスタム コントロールはいくつかの制限に従う必要があります。

  • アプリ バンドル リソースは、デザイン モードでは使用できません。 画像は、UIImage メソッドを使用して読み込まれた場合に使用できます。
  • Web 要求などの非同期操作は、デザイン モードでは実行しないでください。 デザイン サーフェイスでは、アニメーションやコントロールの UI に対するその他の非同期更新はサポートされていません。

カスタム コントロールは、IComponent を実装し、DesignMode プロパティを使用して、デザイン サーフェイス上にあるかどうかを確認できます。 この例では、デザイン サーフェイス上のラベルには "Design Mode" が表示され、実行時には "Runtime" が表示されます。

[Register ("DesignerAwareLabel")]
public class DesignerAwareLabel : UILabel, IComponent {

    #region IComponent implementation

    public ISite Site { get; set; }
    public event EventHandler Disposed;

    #endregion

    public DesignerAwareLabel (IntPtr handle) : base (handle) { }

    public override void AwakeFromNib ()
    {
        if (Site != null && Site.DesignMode)
            Text = "Design Mode";
        else
            Text = "Runtime";
    }
}

Site のメンバーにアクセスする前に、必ずそのプロパティが null をかどうかを確認する必要があります。 Sitenull の場合、コントロールがデザイナーで実行されていないと考えて問題ありません。 デザイン モードでは、コントロールのコンストラクターが実行された後、AwakeFromNib が呼び出される前に、Site が設定されます。

デバッグ

上記の要件を満たすコントロールがツールボックスに表示され、サーフェスにレンダリングされます。 コントロールがレンダリングされない場合は、コントロールまたはその依存関係のいずれかにバグがないか確認します。

デザイン サーフェイスは、他のコントロールのレンダリングを続行しながら、個々のコントロールによってスローされた例外をキャッチすることがよくあります。 障害のあるコントロールは赤いプレースホルダーに置き換えられ、感嘆符アイコンをクリックすると例外トレースを表示できます。

A faulty control as red placeholder and the exception details

デバッグ シンボルをそのコントロールで使用できる場合、トレースにはファイル名と行番号が含まれます。 スタック トレース内の行をダブルクリックすると、ソース コード内のその行にジャンプします。

デザイナーが障害のあるコントロールを分離できない場合は、デザイン サーフェイスの上部に警告メッセージが表示されます。

A warning message at the top of the design surface

障害のあるコントロールが修正されるか、デザイン サーフェイスから削除されると、完全なレンダリングが再開されます。

まとめ

この記事では、iOS Designer でのカスタム コントロールの作成と適用について説明しました。 最初に、コントロールがデザイン サーフェイス上にレンダリングされ、プロパティ パネルでカスタム プロパティを公開するために満たす必要がある要件について説明しました。 その後、分離コード (コントロールの初期化と DesignMode プロパティ) を確認しました。 最後に、例外がスローされたときに起こることと、これを解決する方法について説明しました。