XamarinでAndroid/iOS/Windowsストアの回転に対応する

実機を回転したときに縦置きと横置きの対応する方法は、基本はフローレイアウトを使うほうがいいのですが、コントロールがたくさん並んでいる場合にはなかなか大変ですね。…という理由を付けて、まとまったものが見つからなかったので、3つのプラットフォームでXamarinでどう対応するのかを残しておきます。

それぞれ方法があると思いますが、Potable Class Library で DataModel を共有化することと、3つのプラットフォームで似た方法で作れる、ことを目的とします。

■Windowsストアプリ の場合

  1. 横置き(Landscape)と縦置き(Portrait)の2つのViewStateを作る。
  2. PageクラスのLayoutUpdatedイベントで DisplayInformation.GetForCurrentView() でデバイスの向きを判断して、VisualStateManager.GoToState メソッドを呼び出す。

Blend を使って、VisualStateGroup を作って、その中に2つの ViewState を作る。”Landscape” と “Portrait” を作っておく。

image


 
  
   
   
    

コントロールを Portrait を設定してデザインを作る。

画面自体は1画面なので、XAMLに対して、this.DataContext = _model; のようにバインドができる。

回転時はイベントを取得して、ViewState を切り替える。

private void pageRoot_LayoutUpdated(object sender, object e)
{
    DisplayInformation displayInfo = DisplayInformation.GetForCurrentView();
    switch (displayInfo.CurrentOrientation)
    {
        // 横置き
        case DisplayOrientations.Landscape:
        case DisplayOrientations.LandscapeFlipped:
            VisualStateManager.GoToState(this, "Landscape", true);
            break;
        // 縦置き
        case DisplayOrientations.Portrait:
        case DisplayOrientations.PortraitFlipped:
            VisualStateManager.GoToState(this, "Portrait", true);
            break;
    }
}

横置きと縦置きで、コントロールの位置はこんな風に自由に配置できる。方法としてはスナップも同じ。というかスナップと同じ方法。

image

image

■Android の場合

  1. Alternative Layouts で、landscape 用のレイアウトを作る。
  2. layout-land/Main.axml を横置き用に編集する。
  3. ウィジット(コントロール)の ID は元の Portrait と同じにしておく。

image

image

回転したときには内部で自動的に判断して、layout-land/Main.axml が呼び出される。*.axml ファイルが2つになるので、IDをそろえておく。揃えておくと、FindViewById メソッドで統一して扱える。

private void UpdateData()
{
    FindViewById(Resource.Id.textViewID).Text = _model.ID;
    FindViewById(Resource.Id.textViewUserName).Text = _model.UserName;
    FindViewById(Resource.Id.textViewScore).Text = _model.Score.ToString();
    FindViewById(Resource.Id.textViewRank).Text = _model.Rank.ToString();
}

 

Android エミュレータは Ctrl+F11 で回転できる。

image

image

■iOS の場合

  1. Storyboard で、横置き用の ViewController を作成する。
  2. Storyboard Segue に名前を付けおく。
  3. 元の ViewController の View のタグを付けておく(初期化判断用)
  4. 同じ ViewController クラスに設定しておく。
  5. UIDeviceOrientationDidChangeNotification イベントで、PerformSegue メソッドを使って View を切り替える。

最初の画面が縦置き(Portrait)で、横置き(Landscape)に画面遷移する、という想定で作る。

image

切り替えを「Model」にしておく。

image

最初の View に Tag を付けておく。これは最初の画面のみ UIDeviceOrientationDidChangeNotification を設定するため。

image

AwakeFromNib をオーバーライドして UIDeviceOrientationDidChangeNotification イベントを登録。回転イベントが発生したら、UIDevice.CurrentDevice.Orientation で View を切り替える。

bool isShowingLandscaeView ;
public override void AwakeFromNib ()
{
    base.AwakeFromNib ();
    // main view only 
    if (this.View.Tag == 100) {
        // main view only,
        // set to main view's tag 100.
        this.isShowingLandscaeView = false;
        UIDevice.CurrentDevice.BeginGeneratingDeviceOrientationNotifications ();
        NSNotificationCenter.DefaultCenter.AddObserver (
            "UIDeviceOrientationDidChangeNotification",
            orientationChanged);
    }
}
void orientationChanged( NSNotification obj ) 
{
    var deviceOrientation = UIDevice.CurrentDevice.Orientation;
    if (deviceOrientation == UIDeviceOrientation.LandscapeLeft ||
        deviceOrientation == UIDeviceOrientation.LandscapeRight) {
        if (isShowingLandscaeView == false) {
            this.PerformSegue ("LandView", this);
            isShowingLandscaeView = true;
        }
    } else if (deviceOrientation == UIDeviceOrientation.Portrait ||
        deviceOrientation == UIDeviceOrientation.PortraitUpsideDown) {
        if (isShowingLandscaeView == true) {
            this.DismissViewController (false, null);
            isShowingLandscaeView = false;
        }
    }
}

 

元画面(portrait)から lanscape に遷移するときは、this.PerformSegue (“LandView”, this); を使い、landscape から元画面(portrait)に戻るときは this.DismissViewController (false, null); を使う。これは、storybord の画面遷移と同じ。遷移のアニメーションと回転時のアニメーションが競合するよな気もするんだが…このほうは Apple のマニュアルにもあるので、大丈夫だと思う。

image

image

2つの画面に分かれるが、同じ ViewController を示すようにして、IBOutlet させたプロパティにDataModel から設定する。

■ DataModel を PCL で作る

データ自体は、DataModel 内にひとつにしたいので、複数の View に対応する必要がある。基本は画面遷移をしたときと同じなのだが、画面のコントロール/ウィジットが多い場合には意外と大変かも。ただし、縦横で別々に作ったほうが使いやすいデザインにはなる。

  Winストア Android iOS
デザイン 1つのXAML 2つの
AXMLファイル
2つのViewController
回転
切り替え
コード 自動 コード
データ
設定
データバインド FindViewById で取得。IDを揃える。 IBOutletで取得

上記のサンプルは git で公開しています。https://github.com/moonmile/SampleRotate

機会を作って MvvmCross でも。

カテゴリー: Android, C#, WinRT, Xamarin, iOS パーマリンク