2017/5/27(土)に開催されたJXUGC #23 Xamarin 無料化一周年記念勉強会!で喋ってきました。
Xamarin.Formsがさらにオープンになる変更がリリースノートでも特に言及される気配がないのでちょっと解説しました。
Xamarin.Forms 2.3.5(現時点ではプレビュー版)以降をターゲットにすれば、誰でも勝手に Xamarin.Forms.Platform.WinForms をリリースできるようになるステキな変更ですよ?
2017/5/27(土)に開催されたJXUGC #23 Xamarin 無料化一周年記念勉強会!で喋ってきました。
Xamarin.Formsがさらにオープンになる変更がリリースノートでも特に言及される気配がないのでちょっと解説しました。
Xamarin.Forms 2.3.5(現時点ではプレビュー版)以降をターゲットにすれば、誰でも勝手に Xamarin.Forms.Platform.WinForms をリリースできるようになるステキな変更ですよ?
今朝起きるとBuild 2017の2日目にXamarin Live Player(以降XLPと省略します)という技術が発表されていたので、今日はその仕組みについて想像していました。
この時点では、どうやらXLPは「Mac不要でiOSアプリをビルドしてデバッグできる」技術らしいと把握していました。
「またまた皆騙されちゃってー、iOSアプリをビルドするならMacは必要、見えないところにいるだけでしょう」と想像した図がこちら。
Xamarin Live Playerについて、まだ何も試せてないけど多分こんな仕組みじゃないかな(iOSアプリの場合) pic.twitter.com/EHx7O26IN7
— ざまりん.ふぉーむずマン👀 (@ticktackmobile) 2017年5月12日
断っておくとこの想像図は間違っています。(そもそも「iOSアプリをビルドする技術」という理解が間違っている)
一応、この想像図について解説すると
iOSアプリのビルド
現在でもmachinecloudのようなMacを貸し出すクラウドサービスやVisual Studio Mobile Centerを利用することで手元にMacマシンが無くてもiOSアプリをビルドすることが可能です。
iPhoneへのアプリの配信
MicrosoftはHokkeyAppというサービスを持っていて、その中にはテスト用アプリの配布機能も含まれます。iPhoneへのアプリ配布もできそう。
デバッグ
以前からwi-fi経由でのリモートデバッグは可能でした。
とまあ、既存技術の組み合わせでできそうな気がしました。間違ってるんですけどね。
新しい情報を得て考えを改めます。
@ticktackmobile クラウドでビルド、しているように見えました? 多分していたらxibもaxmlもサポートできると思うんですけど、limitationsだし、全部コードで書いている場合だけ動かせるのが現状だと思うんですよね
— Atsushi Eno (@atsushieno) 2017年5月12日
Xamarin Live Playerって、iPhone上でXamarinプログラミングできるContinuousにリモートデバッグ機能がついた感じなのかなぁ。 https://t.co/jBks1GJImJ
— くれぃんKengo Tsuruzono (@_crane_) 2017年5月12日
はい、現時点でこれが一番正解に近いツイートです。Continuous知ってる人がいてうれしみ。 https://t.co/Byq3Kg30z3
— Atsushi Eno (@atsushieno) 2017年5月12日
ここでXLPの制限事項の存在を認識します。
Xamarin Live Playerの制限事項
Xamarin公式のGuidesによると、主だった物として以下のような制限がある模様。
なるほど、リソースやレイアウトファイルはビルド時にAndroid/XcodeのSDKによって処理されていたはずです。 これらがサポートされないということはXLPではアプリをビルドしないと考えるべきでしょう。
Continuous
ContinuousはiOS上で動作するC#、F#用のIDE(統合開発環境)です。 iPadやiPhoneで書いたコードがその場で実行できるわけです。
ところで、iOSアプリでは実行時コンパイルができません。そのためContinuousは読み込んだコード解析して、それを再現する処理を実行しているはずです。
つまり、こんなコードを書かれた場合
// ユーザーが各コード UIlabel label = new UILabel(); label.Text = "hello world";
このようなコードに相当する処理を実行しているということです。
// Continuousが実行する処理 object variable = Activator.CreateInstance(Type.GetType("UILabel")); Type.GetType("UILabel").GetProperty("Text").SetValue(variable, "hello word", null);
ユーザーがどのクラスを使用するか不明なため、主要なライブラリはアプリに同梱しておく必要があります。その証左としてContinuousのアプリサイズは201MBあります、かなり大きいですね。
さて、話をXamarin Live Playerに戻します。
制限事項より、Android/XcodeのSDKを使ってビルドされたアプリケーションパッケージが実行されていないことが読み取れました。ということは、Visual Studioで書かれたソースコードはContinuousのようにインタープリタ形式で実行されていると考えられます。
ならば、XLPのiOS版アプリは通常よりもはるかに多きいサイズであるはず。 (補足:通常のXamarin.iOSアプリでは参照されないクラスを削ってサイズを小さくしている)
実際 197MB ありました。うん、これはもう間違いなくContinuousと同じ仕組みですね。 (ちなみにAndroidはアプリと別にライブラリをインストールできるし、実行時にコンパイルできるのでもっと小さいです、30MBくらいだそうな)
ちなみに、この記事を書いている時点でもまだXLP試せてません。何しろコレなもので。
やったぜ pic.twitter.com/UQ84GNUIAq
— ざまりん.ふぉーむずマン👀 (@ticktackmobile) 2017年5月12日
BindableなItemsコントロールを作るときにコピペしてね。(ItemsView
を自分のコントロールに置換しよう)
Xamarin.Forms 2.3.4-preでPickerがItemsSourceとItemSelectedをサポートするようになります。すっごーい!
さっそくこんな感じのViewModelを用意してContentPageのBindingContextにセットします。
public class ViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public IEnumerable<AnimalFriend> Friends { get; } AnimalFriend _bestFirend; public AnimalFriend BestFriend { get { return _bestFirend; } set { _bestFirend = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BestFriend))); } } public ViewModel() { Friends = new[]{ new AnimalFriend { Name = "サーバル" }, new AnimalFriend { Name = "ジャガー" }, new AnimalFriend { Name = "トキ"}, new AnimalFriend { Name = "ツチノコ"}, }; } } public class AnimalFriend { public string Name { get; set; } }
ContentPageのXAMLはこんな感じ。
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XFApp44.BindablePickerPage"> <ContentPage.Content> <StackLayout Padding="15, 40, 15, 20"> <Picker HeightRequest="40" Title="いちばんのともだちをおしえてね" ItemsSource="{Binding Friends}" ItemDisplayBinding="{Binding Name, StringFormat='{0}ちゃん'}" SelectedItem="{Binding BestFriend, Mode=OneWayToSource}"/> <Label Text="{Binding BestFriend.Name, StringFormat='{0}ちゃんととっても仲良しなんだね、すっごーい!'}" /> </StackLayout> </ContentPage.Content> </ContentPage>
ItemsSourceの各アイテムをPickerに表示する際にはPicker.ItemDisplayBinding
を使って適切なプロパティPathを指定することが可能です。
Binding構文で指定するのでStringFormatも使えます。かしこーい。
Xamarin.Forms 2.3.4-preで新しいOnPlatformの仕組みが導入され、従来の物は非推奨となります。
概要
これまで、実行中のプラットフォームを取得するにはenum TargetPlatform
型のDevice.OS
プロパティを使用して来ました。
これを置き換える形でstring型のDevice.RuntimePlatform
プロパティが新しく定義されます。
(Xamarin.Forms本体に定義されていない独自のプラットフォームを追加しやすくする意図があるみたい、Tizenとか?)
Xamarin.FormsがサポートするプラットフォームはDevice
クラスの定数として定義されます。
public const string iOS = "iOS"; public const string Android = "Android"; public const string WinPhone = "WinPhone"; public const string Windows = "Windows"; public const string macOS = "macOS";
つまり動作プラットフォームの判別は以下のように変わります。
// 従来のコード(obsolete) Deviec.OS == TargetPlatform.Android // これから推奨されるコード Device.RuntimePlatform == Device.Android
Device.OnPlatform()、Device.OnPlatform<T>()がobsoleteとなり、代わりにswitch文とRuntimePlatformを組み合わせて使う事が推奨されます。
// 従来のコード(obsolete) Device.OnPlatform( () => { /* iOS */ }, () => { /* Android */ }, () => { /* WinPhone */ }, () => { /* Default */ }); label.Text = Device.OnPlatform<string>("iOS", "Android", "WinPhone"); // これから推奨されるコード switch(Device.RuntimePlatform) { case Device.iOS: /* iOS */ break; case Device.Android: /* Android */ break; case Device.WinPhone: /* WinPhone */ break; case Device.Windows: /* Windows */ break; case Device.macOS: /* macOS */ break; case "SomethingNewPlatform": /* 独自プラットフォームの場合の処理 */ break; default: /* default */ }
Xamarin.Formsがサポートしていないプラットフォームへも対応しやすくなっていますね。
XAMLでプラットフォームごとの値を設定するのに使われていたOnPlatform<T>
クラスの使い方が変わります。
これまでのOnPlatform<T>のプロパティとして各プラットフォームごとの値を設定していた代わりに、子要素として持たせる形になります。
<!-- 従来のコード(obsolete) --> <OnPlatform x:TypeArguments="x:Double"> <OnPlatform.iOS>20.0</OnPlatform.iOS> <OnPlatform.Android>42.0</OnPlatform.Android> </OnPlatform> <!-- これから推奨されるコード --> <OnPlatform x:TypeArguments="x:Double"> <On Platform="iOS">20.0</On> <!-- 複数プラットフォームを指定可能 --> <On Platform="Android, Windows">42.0</On> </OnPlatform>
子要素となるOn
クラスのOn.Platform
にはカンマ区切りで同時に複数のプラットフォームを指定できます。ここに指定する名前はRuntimePlatformのに使われる文字列と対応します。
teratailで回答したやつ。
Xamarin - Xamarin.FormsのTabbedページのUIをiOSとAndroidで揃えたい(62739)|teratail
スクショ
stackoverflowの回答を参考にしています。
ios - Positioning UITabBar at the top - Stack Overflow
Xamarin.iOS側のプロジェクトに Xamarin.Forms.Platform.iOS.TabbedRenderer
の派生クラスを作ります。
TabbedRendererはUITabBarControllerから派生しているので、ViewWillLayoutSubviews()
をoverrideして処理を追加します。
using System; using UIKit; using Xamarin.Forms; [assembly: ExportRenderer(typeof(TabbedPage), typeof(TopTabbarSample.iOS.TopTabbedRenderer))] namespace TopTabbarSample.iOS { /// <summary> /// iOSのTabbedPageのタブバーを上側に表示するためのRenderer /// </summary> public class TopTabbedRenderer : Xamarin.Forms.Platform.iOS.TabbedRenderer { // stackoverflowの回答を元にタブバーの位置を上に変更 http://stackoverflow.com/questions/29579992/positioning-uitabbar-at-the-top public override void ViewWillLayoutSubviews() { base.ViewWillLayoutSubviews(); TabBar.InvalidateIntrinsicContentSize(); var orientation = UIApplication.SharedApplication.StatusBarOrientation; nfloat tabSize = 44.0f; if(orientation == UIInterfaceOrientation.LandscapeLeft || orientation == UIInterfaceOrientation.LandscapeRight) { tabSize = 32.0f; } var tabFrame = TabBar.Frame; tabFrame.Height = tabSize; tabFrame.Y = View.Frame.Y; TabBar.Frame = tabFrame; // 強制的にぼかしを再描画する小技らしい TabBar.Translucent = false; TabBar.Translucent = true; } } }
動作確認用ページ
<?xml version="1.0" encoding="utf-8"?> <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:TopTabbarSample" x:Class="TopTabbarSample.TopTabbarSamplePage"> <TabbedPage.ItemsSource> <x:Array Type="{x:Type x:String}"> <x:String>First</x:String> <x:String>Second</x:String> <x:String>Third</x:String> </x:Array> </TabbedPage.ItemsSource> </TabbedPage>
GitHubにも置いておきます。
Xamarin.Androidでアプリのビルド時に...
java.lang.OutOfMemoryError. Consider increasing the value of $(JavaMaximumHeapSize). Java ran out of memory while executing 'java.exe -jar ~'
のようにJavaのヒープ領域があふれてエラーになったら
Xamarin.Androidプロジェクトの設定からヒープ領域のサイズを変更できます。
Android Options > Advanced > Advanced Android Build Settings > Java Max Heap Size
取り合えず「1G」にして足りなかったら上げればOK(そこまで必要な場合あるのかな?)