Windows系のあれこれ

港区の SIer で Windows 系の開発に従事している SE みたいな人です。Xamarin 中心です。

de:code 2017(Day 1 / Day 2)が終わって

●本記事はポエムです。

2017/5/23-24 に開催された de:code 2017 の公式アプリを Xamarin.Forms で開発しました。

イベントアプリ自体は 2016/9 の Microsoft Foresight、2016/11 の Microsoft Tech Summit に続き3回目なのですが、de:code はデベロッパー向けのイベントのため今までとは反応が違うんだろうなぁ、と開催前から思っていました。
イベント自体の来場者数は Tech Summit と大差ないのにアプリのインストール数もざっくり3倍ぐらいです。
予想通りアプリ公開直後から様々なご意見が散見されて中には手厳しいものもあり、 Twitter を見るのが怖くなったり、イベントが継続不可能になるほど炎上している夢を見たり、自分のセッションの準備もあるわでだいぶてんやわんやでした。。

結果として正直、反省点だらけです。

スタンプラリー

今回新たに追加したスタンプラリー機能ですが、メッセージが適切でなかったり(iOS では位置情報の許可も必要なのに Bluetooth が無効と延々表示される)、スタンプが取得できないと問い合わせをいただいたりもしました。
これは確認漏れや、端末の個体差、会場の電波状況等々、こちらの考慮不足です。おそらく全員が問題なく使用できるようにするというより、Beacon を使用したスタンプの取得ができない方の救済措置があった方がよかったと思いました。QR コードや最悪、紙での代替などを用意しておけばよかったです。
元々、ブース出展社の方のお手を煩わせないように、という目論見もあり Beacon のみで行うことになったのですが、逆に問い合わせ対応が増えてしまったようです。
うまくできなかった方、問い合わせ対応で時間が取られた出展社の方にはご迷惑をおかけしました。申し訳ありません。

その他問題

アプリのリリース時期も色々あって、イベントの数日前になってしまいフィードバックを反映する時間もなく、ぶっつけ本番的な感じになってしまいました。
また、サーバー側の DB/API はウチの担当ではなかったですが、パスワードに記号が入ってるとログインできない(ログイン API 関連)、QR コードが出てこない(参加情報取得 API で正しい値が取得できない)、アンケートが出てこない(イベント受付時のデータ登録がうまくできてない)等々、アプリ外の問題も発生していて想定が甘かった箇所もありました。

Xamarin.Forms での開発

開発に関しては、まぁこれでいーんじゃない?とりあえず作りあげよう精神で突き進んで来ました。
今回は細かい部分の調整をする余裕があまりなかったため、利用者の方々にはご不満やご不便をおかけすることもあったかと思います。
「逆に Microsoft の技術のネガキャンになっちゃうんじゃない?」的なご意見も見かけました。
個人的には、Xamarin.Forms で全プラットフォームに対してベストなアプリを作るのは潤沢な工数、期間がないと難しく、Xamarin.Forms はそれなりの工数でそれなりのアプリを作れる(素晴らしい)開発環境だと思っています。(というか潤沢にあってベストまたはそれに近い UI を実現するのであれば Forms 使わない方がいいと思ってる。もちろんプラットフォームの理解も必要だけど。)
==================

  • ちょっと誤解を招きそうなので追記

当然ながら今回のアプリが Xamarin.Forms の限界なわけではありません。
色々妥協はしていますし、私たちがいたらない箇所も多々あったと思います。
==================

メモ:次回まで改善したい(とりあえず思いつくまま)

全体

  • ログインしなくても使える機能は開放する
  • セッション編集画面の自動スクロールの時間が Hour でやっているため次の次の時間帯にスクロールしてしまう可能性がある
  • フィルターの全クリア処理追加
  • 問い合わせ先を明記する
  • パスワード忘れのリンクをログイン画面に置く
  • ログイン時エラーの場合は中途半端なデータを作らない
  • マップ画像キャッシュ系(更新された時のためにキャッシュを無効にしているが、オフラインだと取得できない)
  • UI 全体的に見直し
  • スタンプラリーができない場合の救済措置(QR コードなど)
  • 空席状況をもっと厳密に出したかった
  • アプリ更新があった際のストアへの誘導

iOS

  • ステータスバーの色
  • Bluetooth と位置情報が必要なのに Bluetooth のメッセージしかでない
  • スクロールのパフォーマンス

Android

  • ドロップダウンのデザイン
  • ログイン画面のテキストの配色
  • 画像ピンチインやりにくい
  • 戻るでアプリ終了しない、もしくは再開時に復帰する
  • Push 通知が折り返さない、展開できない

UWP

  • 画像ピンチインできない
  • 早くスクロールすると黒くなる

さいごに

反省点も多々ありますが、なんとか終わり、ポジティブな感想をいただくとやっぱりうれしかったです。
そういったご意見にだいぶ救われました。
また、ウチはアプリ開発を担当しただけでイベント運営ではないですが、運営側に勝手に口出しして色々中の様子を見ることができ、とても勉強になりました。
運営の方々はほんとに大変そうで、来場者の方をどう満足させるかに全力を注いでいて、どんなイベント、サービスもたくさんの人々の努力によって形成されてることを改めて認識しました。
自分がユーザーの立場になった際にはいいと思った点もちゃんとフィードバックしようと深く心に刻みました。
(去年の de:code は参加者側で文句言いまくってた)

Visual Studio 2017 Version 15.2 にアップデートしたら Xamarin.Forms の Android アプリがすごい遅くなったから回避した

============================================
2017/5/31 追記
Visual Studio 2017 version 15.2 (26430.12) で対応されたようです。
>Fixed a performance dedgration sometimes occuring in Xamairn Android apps.
Visual Studio 2017 Release Notes
============================================

============================================
2017/5/25 追記
Mac、VS 2015 は対応されたようです。
Stable Release: 15.2.2 Servicing Release | Xamarin Releases


2017 はまだかな…
============================================

============================================
2017/5/17 追記
下記手順を行うと Android の Release ビルドで作成したアプリがうまく動かなくなります。
Debug 時の回避手段とお考え下さい。
============================================

※この記事の手順は自己責任でお願いします。

タイトルの通りです。
Xamairn.Forms、Prism(Unity) で作成した Android アプリがなんか InitializeComponent で数秒かかる感じに遅くなったんです。
Android 4.4では発生せず、6.0、7.0で現象を確認できました。

環境は以下の通り。

  • Windows 10 Pro Creators Update (バージョン1703、ビルド15063)
  • Visual Studio 2017 Version 15.2 (26430.4)
  • Xamarin 4.5.0.443
  • Xamarin.Android.SDK 7.3.0.13


色々皆さんのご協力をいただき、対症療法的に回避しました。

調べていると下記 bugzilla がヒットしてみんな Xamarin.Android 7.3 が原因っぽいことを言っています。

56240 – Performance Degradation When Using Expressions

ということで Xamarin.Android を前のバージョンにダウングレードしたいと思います。

下記サイトから以前の Xamarin インストーラー(4.4)をダウンロードしてきます。

※ Xamarin のアカウントが必要です。
https://store.xamarin.com/account/my/subscription/downloads
f:id:ShunsukeKawai:20170515215324p:plain

ダウンロードした Xamarin インストーラーを起動します。

※この時点で Build Tools 2015 が必要です。みたいなことを言われたらインストールしてください。
Download Microsoft Build Tools 2015 from Official Microsoft Download Center

インストーラー起動後、今回は Xamarin.Androidだけ必要なので、他のチェックは外しちゃいます。

f:id:ShunsukeKawai:20170515215944p:plain

インストールされたファイルは基本的には以下にあると思います。

C:\Program Files (x86)\MSBuild\Xamarin

実際に VisualStudio で使用している Xamarin.Android のフォルダーをリネーム(バックアップ)して先ほどのファイルを丸ごと持ってきます。

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Xamarin
f:id:ShunsukeKawai:20170515220606p:plain

以上で終了です。

Visual Studio を起動して出力の Xamarin Diagnostics に以下のように 7.2 だよー的なのが出ていれば成功です。
早くなりました。
f:id:ShunsukeKawai:20170515220830p:plain

Prism との組み合わせなのかなー。すぐ修正されるといいなぁ。

以下、みなさんのご協力



Daiki Kawanuma (@Santea3173) | Twitter
さんが
クワーティ (@qwerty2501) | Twitter
さんも悩んでいたことを教えてくれて



かむ (@muak_x) | Twitter
さんも参戦し


Mac 版はサクッと解決してくれたので

参考にして Windows も回避です。

Backlog の更新を Zapier 経由で Microsoft Teams に通知する

一部のプロジェクトの課題管理には Nulab 社の Backlog を利用しています。
Backlog - Backlog [バックログ]

標準では通知はメールベースなのでいろんな通知がわんさか来てなにがなんだかわからなくなります。
メールからの脱却みたいな話もありますし。

でチャットコミュニケーションツールには Slack と Microsoft Teams を利用しています。
Slack に関しては様々な記事があがっているので今回は 最近ローンチされた Microsoft Teams に対して通知を飛ばしたいと思います。
Microsoft Teams – Group Chat software

手順

  • Zapier でトリガーを追加する
  • Backlog から Webhook を追加する
  • Microsoft Teams に コネクタを追加する
  • Zapier でアクションを追加する

詳細

Zapier でトリガーを追加する

Zapier とはサービスとサービスを連携してくれるハブサービスです。似たような有名どころで IFTTT とかでしょうか。
The best apps. Better together. - Zapier - Zapier

  • サインアップ

適当にサインアップします。
無料枠だと、2ステップまでとか制限がありますが、今回は無料で大丈夫です。
サインアップ後、画面上の「MAKE A ZAP!」ボタンを押します。
f:id:ShunsukeKawai:20170406162913p:plain:w600

  • Trigger 登録

Zapier をキックするトリガーを登録します。
今回ですと、Backlog の更新です。
様々なサービスから Webhooks by Zapier を選択します。
Webhook とは POST リクエストをサクっと呼んでくれる仕組みです。
f:id:ShunsukeKawai:20170406163234p:plain:w600
次の画面で Catch Hook を選択して「Save + Continue」ボタンを押します
f:id:ShunsukeKawai:20170406163626p:plain:w600
次の画面の Pick off a Child Key はブランクのまま「Continue」ボタンを押します。
そうすると Webhook の URL が自動生成されるので、URL をコピーして「OK,I did this」ボタンを押します。
f:id:ShunsukeKawai:20170406163912p:plain:w600
以上でトリガーができました。
この画面は後で使うのでブラウザは開いたままで次のステップに進みます。

Backlog から Webhook を追加する

先ほど作成したトリガーを呼び出す仕組みを Backlog 側に追加します。
Backlog のプロジェクト設定画面のメニューから「Webhook」を選択して「Webhookを追加する」ボタンを押します。
f:id:ShunsukeKawai:20170406162057p:plain
適当な名前を付けて、先ほど Zapier 側で生成されて URL を指定します。
イベントは今回は課題の追加に設定しておきます。
f:id:ShunsukeKawai:20170406165150p:plain
Backlog 側の設定は以上です。

Microsoft Teams に コネクタを追加する

実際に Teams に投稿するためのコネクタを追加します。
Teams のチャンネルのコンテキストメニューから「コネクタ」を選択します。
f:id:ShunsukeKawai:20170406165624p:plain
コネクタの一覧から Incoming Webhook を追加します。
f:id:ShunsukeKawai:20170406165804p:plain:w600
適当な名前と画像を設定して「作成」ボタンを押します。
f:id:ShunsukeKawai:20170406165933p:plain:w600
そうすると自動的にURLが生成されるので、コピーして「完了」ボタンを押します。
f:id:ShunsukeKawai:20170406170136p:plain:w600
Teams 側の設定は以上です。

Zapier でアクションを追加する

Zapier の画面に戻ってアクションを追加します。
こちらもトリガーと同じ様に Webhooks by Zapier を選択します。
f:id:ShunsukeKawai:20170406171244p:plain:w600
次の画面で POST を選択して「Save + Continue」ボタンを押します。
f:id:ShunsukeKawai:20170406171351p:plain
次の画面では実際に Teams に通知する内容を設定します。
URL には先ほど Teams 側で生成された URL を設定します。
Payload Type は Json を設定します。
f:id:ShunsukeKawai:20170406173620p:plain
Data には Teams でどのような内容を通知するかを指定します。
こちらは Teams 側のお作法に準拠してください。以下のサイトが参考になります。
Office 365 Connectors API Reference - Outlook Dev Center

Zapier は STEP1 で設定したトリガー側から送信されるデータ(今回は Backlog の Webhook からのデータ内容)を以下のように選択できるようになっています。めっちゃ便利です。
f:id:ShunsukeKawai:20170406172209p:plain

例として以下の設定にしてみました。
f:id:ShunsukeKawai:20170406175426p:plain
Backlog は Project Key と Content ID の組み合わせで URLが生成されるので、通知から直接リンクできるようにしています。

それ以外の設定はデフォルトのままで大丈夫です。
「Save + Continue」ボタンを押すと自動的にテストが走って、
f:id:ShunsukeKawai:20170406180202p:plain
と Teams に通知されます!
やった!

おまけ

今回はアクションに Webhook を使用していますが、アクションのアプリに Teams が追加される動きもあるみたいです。
Microsoft Teams Integrations - Zapbook - Zapier

以上です。

Visual Studio 2017でXamainのUpdate Channelを切り替える方法(というかVisual Studio 2017のインストーラーの仕組み)

Visual Studio 2015 では Xamarin の最新の機能を試したい場合、ツール⇒オプション⇒Xamarin⇒その他でチャンネルを切り替えることができました。
しかし、昨日リリースされた Visual Studio 2017 ではその設定がなくなっています。

Visual Studio 2017

f:id:ShunsukeKawai:20170309112803p:plain

で、どうするかと言うと、Xamarin 公式サイトに答えがありました。
Change the Updates Channel - Xamarin
ちゃんとすぐ更新されていて素晴らしいですね。
結論から言うと、Xamarin 個別にチャンネルを切り替えることができなくなり、Visual Studio 自体のバージョンにより Xamarin のリリースも制御されるようになったみたいです。

インストール手順

プレビュー版の Visual Studio インストーラーをダウンロード

Visual Studio Preview Release Notes
のサイトの以下のリンクからプレビュー版のインストーラーをダウンロードします。
f:id:ShunsukeKawai:20170309113900p:plain
※ このサイトには Visual Studio Enterprise 用のインストーラーリンクしかありませんが、別のエディションをインストールしたくてもそのままダウンロードしてください。

インストールしたいエディションを選択して実行

ダウンロードしたインストーラーを起動すると以下のような画面が開きます。
f:id:ShunsukeKawai:20170309115551p:plain
このまま進むと Enterprise 版になってしまうので、一旦右上の×で閉じます。
そうすると以下の画面になるので、好きなエディションを選択して、インストールに進みます。
f:id:ShunsukeKawai:20170309120422p:plain
その際にインストールのニックネームを付けることでどのバージョンかとかがわかるようできるみたいです。
f:id:ShunsukeKawai:20170309122949p:plain

ここで疑問

今回やりたいことは Xamarin の Update Channel を切り替えたいだけなのに VS 自体を別でインストールしないといけないとしたらディスク容量はすぐ一杯になってしまうでしょうよ。
インストールする対象を Xamarin だけにしてちゃんと開発できるのか?(上の画像は Xamarin だけを選んだ。容量3.98GB)
ちょっと私のディスク容量はすでにカツカツなので、どなたか確認したら教えてくださいm(_ _)m

Visual Studio 2017 のインストーラーの仕組み

今回からインストーラー自体がアプリとして自動的に更新されるような仕組みになったみたいです。
それぞれのエディションごとにダウンロードリンクは別で起動時の対象は違うけど上述の通り、全部選べる。
また、すでに製品版を既にインストールしている端末でプレビュー版のインストーラーを起動すると、インストーラーが更新されて以下のように製品版もプレビュー版も管理できるようになります。
f:id:ShunsukeKawai:20170309123908p:plain

そもそも、OS の「プログラムと機能」からは Visual Studio 2017 の変更が選べず、アンインストールしかありません。
ここにある Visual Studio 2017 は VS のことではなく、インストーラーを指すようです。(表示されているバージョンもインストーラーのバージョン)
f:id:ShunsukeKawai:20170309124225p:plain
インストーラーを使い捨てのモノでなくアプリとして作成することで、製品の更新や管理を統合的にできる仕組みになった感じですかね。

本来の目的は達成できてないけど、今日はここまで。

Xamarin.FormsでiOSのEditorの枠線をEntryみたいにしたい

Xamarin.Forms で複数行のテキストボックス(Editor)を配置した場合、以下の様になります。
f:id:ShunsukeKawai:20170209173238p:plain:w300

ちゃんと置いてあるのに真っ白です。

<StackLayout Margin="20">
    <Editor HeightRequest="150"/>
</StackLayout>

入力はもちろんできます。
f:id:ShunsukeKawai:20170209173530p:plain:w300

うーん(´・ω・`)これが iOS では標準なのか?

Xamarin.Forms の標準コントロールがどのネイティブコントロールマッピングされているかは以下のページに書いてあります。
Renderer Base Classes and Native Controls - Xamarin
Entry は UITextField に Editor は UITextView にマッピングされてます。


ということで Effect です。
といっても標準の UITextField の Border がどうなってるのかよくわからなかったので、アナログ戦法で寄せました。

  • iOS のプロジェクトに以下を追加します。

BorderEffect.cs

using EditorBorder.iOS.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ResolutionGroupName("Effects")]
[assembly: ExportEffect(typeof(BorderEffect), "BorderEffect")]
namespace EditorBorder.iOS.Effects
{
    public class BorderEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            Control.Layer.BorderWidth = 0.6f;
            Control.Layer.BorderColor = Color.FromRgb(209, 209, 209).ToCGColor();
            Control.Layer.CornerRadius = 5;
        }

        protected override void OnDetached()
        {
            Control.Layer.BorderWidth = 0.0f;
        }
    }
}
  • PCL のプロジェクトに以下を追加します。

BorderEffect.cs

using Xamarin.Forms;

namespace EditorBorder.Effects
{
    public class BorderEffect : RoutingEffect
    {
        public BorderEffect() : base("Effects.BorderEffect")
        {
        }
    }
}
  • 画面の XAML に作成した Effect を適用します。

Effect は違うコントロールにも適用できるので、とりあえず見た目を確認するため生の Entry と適用した Entry を並べます。
MainPage.xaml

<StackLayout Margin="20">
    <Entry Text="標準のEntry" />
    <Entry Text="EffectしたEntry">
        <Entry.Effects>
            <effects:BorderEffect />
        </Entry.Effects>
    </Entry>
</StackLayout>

で実行すると…
f:id:ShunsukeKawai:20170209174941p:plain:w300

お!いい感じやん!

って事で、本来の目的である Editor に Effect を適用します。
MainPage.xaml

<StackLayout Margin="20">
    <Entry Text="標準のEntry" />
    <Entry Text="EffectしたEntry">
        <Entry.Effects>
            <effects:BorderEffect />
        </Entry.Effects>
    </Entry>
    <Editor HeightRequest="150">
        <Editor.Effects>
            <effects:BorderEffect />
        </Editor.Effects>
    </Editor>
</StackLayout>

結果

f:id:ShunsukeKawai:20170209175625p:plain:w300

いいんじゃないでしょうか。
違うコントロールに適用できる Effect の便利さを改めて認識しました。

【続】Xamarin.Formsでぺこぺこ凹むアイコンボタンを作った

先日アイコンボタンを作ったと書きましたが、色々更新しました。
shunsukekawai.hatenablog.com

f:id:ShunsukeKawai:20170110224311g:plain

追加内容

  • ラベル追加(DescriptionText)
  • ラベル位置プロパティ設定(DescriptionPosition)
  • ラベルサイズプロパティ設定(DescriptionSize)
  • ラベルカラープロパティ設定(DescriptionColor)
  • アイコン高さプロパティ設定(IconHeight)
使い方

なんか設定する項目が多くなってヤダけど、しょうがない。
ラベルが必要なければ設定しなくて OK です。

<controls:IconButton
    DescriptionColor="#636161"
    DescriptionPosition="Bottom"
    DescriptionSize="24"
    DescriptionText="削除"
    HeightRequest="120"
    HorizontalOptions="Center"
    IconHeight="70"
    IconMargin="10"
    IconSource="{Binding Source=IconButtonSample.Images.Trash.png, Converter={StaticResource ImageSourceConverter}}"
    VerticalOptions="Center">
    <controls:IconButton.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding Path=DeleteCommand}" />
    </controls:IconButton.GestureRecognizers>
</controls:IconButton>

めんどくさかったところ

  • 位置を設定する

横着してボタン全体を囲う2行2列の Grid を追加して位置プロパティ(Enum)によってボタンとラベルの位置を設定しましたが、
Xamarin.Forms なのか UIView なのかわかりませんが、Grid の Definition が Auto かつコントロールが何もなくても微妙にスペースが空いちゃうので、RowSpacing と ColumnSpacing を 0 に設定しています。

<?xml version="1.0" encoding="utf-8" ?>
<ContentView
    x:Class="IconButtonSample.Controls.IconButton"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
    <Grid
        x:Name="grdIconButton"
        ColumnSpacing="0"
        HorizontalOptions="CenterAndExpand"
        RowSpacing="0"
        VerticalOptions="CenterAndExpand">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Image
            x:Name="imgIcon"
            Aspect="AspectFit"
            BindingContextChanged="imgIcon_BindingContextChanged" />
        <Label
            x:Name="lblDescription"
            HorizontalOptions="CenterAndExpand"
            VerticalOptions="CenterAndExpand" />
    </Grid>
</ContentView>
private void setPosition(DescriptionPosition position)
{
    switch (position)
    {
        case DescriptionPosition.None:
            imgIcon.SetValue(Grid.RowProperty, 0);
            lblDescription.SetValue(Grid.RowProperty, 0);
            imgIcon.SetValue(Grid.ColumnProperty, 0);
            lblDescription.SetValue(Grid.ColumnProperty, 0);
            lblDescription.IsVisible = false;
            break;
        case DescriptionPosition.Left:
            imgIcon.SetValue(Grid.RowProperty, 0);
            lblDescription.SetValue(Grid.RowProperty, 0);
            imgIcon.SetValue(Grid.ColumnProperty, 1);
            lblDescription.SetValue(Grid.ColumnProperty, 0);
            break;
        case DescriptionPosition.Top:
            imgIcon.SetValue(Grid.RowProperty, 1);
            lblDescription.SetValue(Grid.RowProperty, 0);
            imgIcon.SetValue(Grid.ColumnProperty, 0);
            lblDescription.SetValue(Grid.ColumnProperty, 0);
            break;
        case DescriptionPosition.Right:
            imgIcon.SetValue(Grid.RowProperty, 0);
            lblDescription.SetValue(Grid.RowProperty, 0);
            imgIcon.SetValue(Grid.ColumnProperty, 0);
            lblDescription.SetValue(Grid.ColumnProperty, 1);
            break;
        case DescriptionPosition.Bottom:
            imgIcon.SetValue(Grid.RowProperty, 0);
            lblDescription.SetValue(Grid.RowProperty, 1);
            imgIcon.SetValue(Grid.ColumnProperty, 0);
            lblDescription.SetValue(Grid.ColumnProperty, 0);
            break;
        default:
            break;
    }
}

こちらも更新しています。
github.com

Xamarin.Formsでぺこぺこ凹むアイコンボタンを作った

Image だけをボタンにしたいケースが多々あり、TapGestureRecognizer で簡単に作れるのですが、押した感がなくイマイチだし好みに合うヤツが見当たらなかったので作りました。
f:id:ShunsukeKawai:20170106115747g:plain
わかりにくいですが、黒い背景でも微妙にボタン裏の色が変わっています。

凹む処理はここを参考にしました。
app-evolve/FavoriteImage.cs at master · xamarinhq/app-evolve · GitHub

最初は Image コントロールだけを拡張していましたが、それだと凹ますと背景色まで凹んでしまいかっこ悪かったので Content View の中に Image を入れました。

IconButton.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView
    x:Class="IconButtonSample.Controls.IconButton"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
    <Image x:Name="imgIcon" BindingContextChanged="imgIcon_BindingContextChanged" />
</ContentView>
IconButton.xaml.cs
using System;
using System.Threading.Tasks;

using Xamarin.Forms;

namespace IconButtonSample.Controls
{
    public partial class IconButton : ContentView
    {
        private bool addedAnimation;

        public static readonly BindableProperty IconSourceProperty =
            BindableProperty.Create(
                "IconSource",
                typeof(ImageSource),
                typeof(IconButton),
                null,
                propertyChanged: (bindable, oldValue, newValue) =>
                {
                    ((IconButton)bindable).IconSource = (ImageSource)newValue;
                }
            );

        public ImageSource IconSource
        {
            get { return (ImageSource)GetValue(IconSourceProperty); }
            set
            {
                SetValue(IconSourceProperty, value);
                imgIcon.Source = value;
            }
        }

        public static readonly BindableProperty IconMarginProperty =
            BindableProperty.Create(
                "IconMargin",
                typeof(Thickness),
                typeof(IconButton),
                new Thickness(0),
                propertyChanged: (bindable, oldValue, newValue) =>
                {
                    ((IconButton)bindable).IconMargin = (Thickness)newValue;
                }
            );

        public Thickness IconMargin
        {
            get { return (Thickness)GetValue(IconMarginProperty); }
            set
            {
                SetValue(IconMarginProperty, value);
                imgIcon.Margin = value;
            }
        }

        public IconButton()
        {
            InitializeComponent();
        }

        private void imgIcon_BindingContextChanged(object sender, EventArgs e)
        {
            if (addedAnimation || GestureRecognizers.Count == 0)
                return;

            var tapGesture = GestureRecognizers[0] as TapGestureRecognizer;
            if (tapGesture == null)
                return;

            tapGesture.Tapped += (tapsender, tape) =>
            {
                Device.BeginInvokeOnMainThread(async () => await tappedAnimation());
            };

            addedAnimation = true;
        }

        private async Task tappedAnimation()
        {
            BackgroundColor = Color.FromRgba(68, 68, 68, 70);
            await imgIcon.ScaleTo(0.8, 75);
            await imgIcon.ScaleTo(1.0, 75);
            BackgroundColor = Color.Transparent;
        }
    }
}

凹ます処理の前後に Content View 自体の背景色を適当な灰色にしています。
ポイントとして Alpha 値を設定して透過にすることで画面の背景色になじむようにしています。
本来はこの辺も BindableProperty にして公開した方がよかったかもしれませんが、まぁいいかなと。

使う画面.xaml
<control:IconButton
    HeightRequest="50"
    HorizontalOptions="Center"
    IconMargin="10"
    IconSource="{Binding Source=IconButtonSample.Images.Trash.png, Converter={StaticResource ImageSourceConverter}}"
    VerticalOptions="Center">
    <control:IconButton.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding Path=DeleteCommand}" />
    </control:IconButton.GestureRecognizers>
</control:IconButton>

ImageSource を Converter 使っていますが、これは画像を埋め込みリソースにして PCL 側に置いているからです。
その辺も含めてこちらにまるごとサンプルを置いてありますので、ご自由にどうぞー。
Prism 使ってます。
github.com

あとは指が抑えている状態(Hold 時)にも色変えたいけど、とりあえず標準のヤツよりいいから満足です。

更新しました

shunsukekawai.hatenablog.com

Xamarin.Forms と Mobile Engagement でユーザー分析しちゃえ!

Xamarin Advent Calendar 2016 - Qiita "その1" 9日目の投稿です。

はい、ということでこんな状況の中、なんとか書きました。。


ホントは0時きっかりにアップしたかったけど、ご了承ください。

さて、Microsoft さんのイベントで使用したアプリケーションの開発をやった話の記事で簡単に説明しましたが、Azure の Mobile Engagement というサービスはとても楽しいサービスです。
shunsukekawai.hatenablog.com

本記事では Xamarin.Forms でどのように Mobile Engagement でどんなことができるのか、どう使用するかを解説をします。

Mobile Engagement でできること

リアルタイムのアクティブユーザー

こんな感じで今正にアクティブなユーザー数が表示されます。
f:id:ShunsukeKawai:20161102190000p:plain

どの画面がどれだけ使われているか

画面の起動時にコードを1行埋め込むことで、勝手にデータを蓄積してくれます。
f:id:ShunsukeKawai:20161102190017p:plain

どんな画面遷移を行っているか

どこからどこの画面に行ってるかよくわからん図で表示されます。
f:id:ShunsukeKawai:20161208225124p:plain

セッション時間はどのくらいか

どのくらいアクティブにアプリを使ってくれているのかが表示されます。
f:id:ShunsukeKawai:20161208225753p:plain

バイスはなにか

iOS(iPhone9,1とかなんなのか…)
f:id:ShunsukeKawai:20161208225228p:plain
Android は製造元を選択するとモデルまで表示されます。
f:id:ShunsukeKawai:20161208230027p:plain

OSはなにか

サポート対象OSを決める時に役に立ちそう。
f:id:ShunsukeKawai:20161208225325p:plain

どのアプリバージョンを使っているか

ちゃんとユーザーがバージョンアップしてくれてるかわかりますね。
f:id:ShunsukeKawai:20161208225357p:plain

画面サイズはなにか

どのサイズで動かされているかわかると色々対策できそうです。
f:id:ShunsukeKawai:20161208225512p:plain

アプリがクラッシュしている原因とその詳細

StackTrace 的なのも出てます。
f:id:ShunsukeKawai:20161208225632p:plain

と、まぁここに挙げたのは一部ですが、見てるだけでも酒の肴になりそうですね。

Xamarin.Forms に組み込む方法

ここからは Mobile Engagement をどうやってXamarin.Forms に組み込むかです。

Azure ポータルから Mobile Engagement のサービスを追加

f:id:ShunsukeKawai:20161201212016p:plain:w500

  • 対象プラットフォームを UWP、AndroidiOSを選択する

f:id:ShunsukeKawai:20161201215231p:plain:w500

  • その他設定はよしなに
  • 作成完了(3つのアプリが作成されます)

Mobile Engagement アプリはプラットフォームごとに作成する必要があります。
f:id:ShunsukeKawai:20161201215715p:plain:w500

Xamarin.Forms プロジェクトに Mobile Engagement 接続(Android 編)

基本的に以下のチュートリアルに沿って進めていけば大丈夫です。(人任せ)
もう Mobile Engagement 作成は済んでいるので、キーをコピーするところ以降ぐらいから。
Xamarin.Android 用 Azure Mobile Engagement の使用

  • 【注意1】NuGet から Microsoft.Azure.Engagement.Xamarin をインストールしたら勝手に作られるフォルダの対処

NuGet からインストールすると勝手にフォルダが作られてそこに Mobile Engagement 用のリソースが置かれます。
(おそらく Xamarin.Android 用だからかな)
そのためその状態でビルドすると以下の様なエラーが発生します。
f:id:ShunsukeKawai:20161208150417p:plain
↓犯人
f:id:ShunsukeKawai:20161208150802p:plain
↓該当するフォルダに移動します。(+になってるヤツを移動した)
f:id:ShunsukeKawai:20161208150959p:plain

  • 【注意2】using は Microsoft.Azure.Engagement.Xamarin だけでいい

MainActivity.cs の using は一つだけで大丈夫です。

  • 【注意3】MainActivity.cs に EngagementActivity の継承をすることは Xamarin.Forms ではできない(FormsAppCompatActivity をすでに継承しているため)

こっち側の手順でやる必要がある
Azure Mobile Engagement Android SDK の詳細なレポート オプション

Xamarin.Forms プロジェクトに Mobile Engagement 接続(iOS 編)

基本的に以下のチュートリアルに沿って進めていけば大丈夫です。(人任せ2)
もう Mobile Engagement 作成は済んでいるので、キーをコピーするところ以降から。
Xamarin.iOS 用 Azure Mobile Engagement の使用

Xamarin.Forms プロジェクトに Mobile Engagement 接続(UWP 編)

基本的に以下のチュートリアルに沿って進めていけば大丈夫です。(人任せ3)
もう Mobile Engagement 作成は済んでいるので、キーをコピーするところ以降から。

Windows ユニバーサル アプリの Azure Mobile Engagement の概要

  • 【注意1】UWP は他と NuGet のパッケージが違うから注意(~Xamarin ではない)
  • 【注意2】リンク先にも書いてありますが、NuGet は、Windows 10 UWP アプリケーションで SDK のリソースを自動的にコピーしません。 NuGet パッケージのインストール時に表示される手順 (readme.txt) に従って、手動で行う必要があります。

とあります。readme.txtの内容は以下の通り。はい。自分で作れっつーことですね。

NuGet does not automatically copy the SDK resources in your Windows 10 UWP application. You have to do it manually until the scaffolding feature is reintroduced. 
Here are the steps for a new integration:

* Open your File Explorer.
* Navigate to the following location: %USERPROFILE%\.nuget\packages\MicrosoftAzure.MobileEngagement\3.4.1\content\win81
* Drag and drop the "Resources" folder from the file explorer to the root of your project in Visual Studio.
* In Visual Studio select your project and activate the "Show All files" icon on top of the "Solution Explorer".
* Some files may not be included in the project. To import them at once right click on the "Resources" folder, "Exclude from project" then another right click on the "Resources" folder, "Include in project" to re-include the whole folder. All files from the "Resources" folder are now included in your project.

まとめ

この辺までの基本的な設定 + PCL に interface 作って各プラットフォーム側で処理を追加して PCL の MainPage.xaml.cs にアクティブにする処理入れたヤツを以下のリポジトリにアップしてあります。
github.com

Mobile Engagement 側の接続文字列を設定すればとりあえず動くと思います。
Push 通知したい場合は各プラットフォームのリンク先を参考に設定してみてください。(最後も人任せ)

それではお疲れ様でした。

VSTS で Team Foundation Version Control で Xamarin のソース管理をする際に package フォルダーを除外したい

かなりニッチな投稿です。

Visual Studio Team Services(VSTS)でソース管理を行う場合、以下の2つから選択できます。

正直 Git が今は主流だと思うのですが、ウチのチームは旧 Visual Source Safe(VSS)からの名残で Team Foundation Server(TFS) ベースの方を利用しています。

Xamarin.Forms のテンプレートで作ったプロジェクトを上記ソース管理に追加をすると、管理対象に package フォルダーを追加してしまいます。
f:id:ShunsukeKawai:20161118180510p:plain
もちろんこのチェックインからは除外することはできるのですが、永続的に対象から除外することは Visual Studio のメニューからはできなそうでした。
Xamarin が悪いのか、Visual Studio が悪いのか VSTS が悪いのか、それともほかの要因なのかわかりませんが。

チームで作業している場合、package フォルダーがソース管理されてしまうと、各作業者がビルドする時に NuGet からパッケージをダウンロードしてくる時に競合したり、読み取り専用になってるからエラーになったり、作業者が別のソースをチェックインしようとすると一緒にわんさか「こいつも変更しとるな?チェックインするけ?」と聞かれて毎回除外する必要がありとても面倒でした。

除外方法

ソリューションと同階層に以下のフォルダ/ファイルを置くだけ
・.tfignore ファイル
・.nuget フォルダ
 ・NuGet.config ファイル

.tfignore ファイルの中身

除外したいフォルダやファイルのパスを記載するだけ
今回なら

\package
NuGet.config ファイルの中身
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <solution>
    <add key="disableSourceControlIntegration" value="true" />
  </solution>
</configuration>

注意点1

Windowsエクスプローラー上だと、頭が「.(ドット)」のファイルが作れません。
そのため、適当なテキストファイルを作成してメモ帳で開く→名前を付けて保存→すべてのファイルを選択して「.tfignore」と命名して保存してやらないといけない。

注意点2

Windowsエクスプローラー上だと、頭が「.(ドット)」のフォルダーが作れません。
頭だけじゃなくお尻にも「.(ドット)」を付けてやると作れます。今回なら「.nuget.」で。

Tech Summit の資料・動画が公開されました

shunsukekawai.hatenablog.com
↑こちらで登壇した旨を記載しましたが、資料、動画が公開されました。

資料

docs.com

なにかご質問があれば Twitter かコメントで遠慮なくどうぞー!

【Xamarin 開発の真実】 Microsoft Tech Summit で登壇してきました!

11/1,2 で開催された Microsoft Tech Summit でテクニカルスポンサー枠として登壇してきました。

microsoft-events.jp

弊社は Mobile 系の開発や Windows 系の開発を推進してきた経緯もあり、今回 日本マイクロソフト株式会社 様より Xamarin でイベントアプリ開発のご依頼を受け、その流れで登壇をさせていただきました。

講演内容

【Xamarin 開発の真実】イベントアプリの中身、すべてお見せします

と名前負けしそうなタイトルとなりましたが、主な内容としては以下の通りです。

  • Xamarin とは
  • イベントアプリの構成
  • Xamarin と UX
  • Xamarin と認証
  • Xamarin と Push 通知 + α

f:id:ShunsukeKawai:20161102191047p:plain

それぞれどんなことを話したか簡単にまとめます。

Xamarin とは

このサイト見てくださいw
www.jmas.co.jp

イベントアプリの構成

イベントアプリの構成はこんな感じですー。ってそのままの話です。
f:id:ShunsukeKawai:20161102161853p:plain
サーバーは別の会社さんが開発をしていて、そこにアプリから接続してデータのやり取りをしています。

Xamarin と UX

標準だけでいけると思ってましたがやはりカスタマイズが必要になったので、Custom Renderer や Effects を利用しなきゃダメだよってお話し。
f:id:ShunsukeKawai:20161102172853p:plain
f:id:ShunsukeKawai:20161102172939p:plain
f:id:ShunsukeKawai:20161102173047p:plain

Xamarin と 認証

イベントアプリでは Microsoft アカウントでのサインインをする必要があります。
認証機能をどうやって作っているのか、デモを交えて説明しました。
f:id:ShunsukeKawai:20161102183406p:plain
f:id:ShunsukeKawai:20161102183414p:plain

詳細はここを参照ください。
blog.xamarin.com

Xamarin と Push 通知 + α

Azure Mobile Engagement を利用して Push 通知やユーザー状況の確認、クラッシュログなどが簡単に利用できるってお話し。
f:id:ShunsukeKawai:20161102183944p:plain
f:id:ShunsukeKawai:20161102184031p:plain
f:id:ShunsukeKawai:20161102184041p:plain
こんな感じでアクティブなユーザー数がリアルタイムで表示されます。
f:id:ShunsukeKawai:20161102190000p:plain
どの画面がよく使われているかをグラフィカルに表示してくれます。
f:id:ShunsukeKawai:20161102190017p:plain

詳細はここを参照ください。
azure.microsoft.com
※上記リンクは Xamarin.Android 用 & ちょっと古いので、変更が必要な箇所があります。

  • using は Microsoft.Azure.Engagement と Microsoft.Azure.Engagement.Activity じゃなくて Microsoft.Azure.Engagement.Xamarin だけでいい
  • 標準だと res フォルダーの中に Mobile Engagement 用の画像等が入るので、Resources の drawable フォルダ、layout フォルダに移動してあげないといけない

Azure Mobile Engagement を使ってみて

セッション中も参加者の方々にご協力いただき、アプリをアクティブにしていただいてリアルタイムに数がぐーんと上がるのをお見せしましたが、あんな機能を数行のコードで実現できてしまうのはホントにスゴイと思います。
Push 通知も簡単に管理できます!(デモでは失敗しましたが…)
マーケティングの方にすごい喜ばれる機能が盛りだくさんな感じです。
是非お試しください!!
azure.microsoft.com

Visual Studio から 作成した iOS の ipa をアプリケーションローダーに喰わせると ITMS-90023 が発生する

Xamarin で開発して iOS の AppStore にアップロードするための ipa を作成する際にアプリケーションローダーを使用してます。

Visual Studio の Xamarin.Forms のテンプレートから作ったプロジェクトをそのままアップロードすると以下のエラーが発生します。

ERROR ITMS-90023: "Missing required icon file. The bundle does not contain an app icon for iPad of exactly '167x167' pixels, in .png format for iOS versions supporting iPad Pro."

f:id:ShunsukeKawai:20161013161447p:plain

対応方法

Info.plist を XML エディター等で開きます。
f:id:ShunsukeKawai:20161013161924p:plain

その中のアイコンの指定が並んでいる箇所を選択して

<array>
	<string>Icon-72@2x.png</string>
	<string>Icon-72.png</string>
	<string>Icon@2x.png</string>
	<string>Icon.png</string>
	<string>Icon-60@2x.png</string>
	<string>Icon-76.png</string>
	<string>Icon-76@2x.png</string>
	<string>Default.png</string>
	<string>Default@2x.png</string>
	<string>Default-568h@2x.png</string>
	<string>Default-Portrait.png</string>
	<string>Default-Portrait@2x.png</string>
	<string>Icon-Small-50@2x.png</string>
	<string>Icon-Small-50.png</string>
	<string>Icon-Small-40.png</string>
	<string>Icon-Small-40@2x.png</string>
	<string>Icon-Small.png</string>
	<string>Icon-Small@2x.png</string>
</array>

の中に

	<string>Icon-83.5@2x.png</string>

を追加します。

あとは対応するサイズ(167x167)のアイコンを追加すればOKです。

注意点

Visual Studio 上で iOS のプロジェクトのプロパティを編集(プロビジョニングファイル等の設定を編集)して保存すると上記のplistの設定が勝手に消えますw
なので、申請用 ipa を作成する際にはちゃんと確認した方がいいかもですね。。

追記

田淵さん(@ytabuchi)に教えていただいた方法の方が楽ちんですね。
ytabuchi.hatenablog.com

(おそらく)世界一簡単にXamarin.Formsのアプリに特定の値でQRコードを生成して表示する方法

public MainPage()
{
    var QrValue = "QRコードにしたい文字列";

    var imgQr = new Image { Aspect = Aspect.AspectFit };
    imgQr.Source = ImageSource.FromUri(new Uri($"http://chart.apis.google.com/chart?cht=qr&chs=200x200&chld=H|0&chl={QrValue}"));

    Content = imgQr;
}

以上!


というのも寂しいので、ちょっと解説です。
みなさんご存知の通り、XamarinにはURLを画像ソースに指定する機能があります。
developer.xamarin.com

それを利用してGoogleQRコード生成のAPIを呼び出し、その結果を設定しているだけです。
QR Codes  |  Infographics  |  Google Developers
※Warning: This API is deprecated. Please use the actively maintained Google Charts API instead. See our deprecation policy for details.
 ということで現在、このAPIは非推奨のようなのでいつ使えなくなるかわかりません。
 また、非推奨になったことでライセンスもよくわかりませんでした。
 なので自己責任でお願いします

画像として返却してくれるAPIであればこれでなくてもなんでも大丈夫だと思います。

さらにおまけで…

こんな感じでイメージコントロールとインジケーターを重ねておいてイメージの読み込みが完了するまでぐるぐるが表示されるようにするといい感じですね。
(このAPIからのレスポンスが一瞬なのでぐるぐるが見えることはないかもしれません。。。)

public MainPage()
{
    var QrValue = "QRコードにしたい文字列";

    var imgQr = new Image { Aspect = Aspect.AspectFit };
    imgQr.Source = ImageSource.FromUri(new Uri($"http://chart.apis.google.com/chart?cht=qr&chs=200x200&chl={QrValue}"));

    var prgIndicator = new ActivityIndicator{
                                HorizontalOptions = LayoutOptions.CenterAndExpand,
                                VerticalOptions = LayoutOptions.CenterAndExpand
                            };

    imgQr.PropertyChanged += (sender, e) =>
    {
        if (e.PropertyName != nameof(imgQr.IsLoading))
            return;
        prgIndicator.IsRunning = imgQr.IsLoading;
        prgIndicator.IsVisible = imgQr.IsLoading;
    };

    Content = new Grid { Children = { prgIndicator, imgQr } };
}

Xamarin.FormsでUWPをReleaseビルドすると埋め込みリソースの画像が表示されない

以下の環境でUWPをReleaseビルドするとPCL側に配置した埋め込みリソースの画像が表示されませんでした。
・Xamarin.Forms 2.3.1.114
・VisualStudio 2015 Enterprise Update 3
・UWP対象ターゲット 10586


Xamarin.Formsで画像を表示する際に全プラットフォームで共通の画像を使いまわしたい場合、埋め込みリソースを使用する場合があると思います。
画像の設定方法については公式を参照ください。
developer.xamarin.com

んで、埋め込みリソースを使っていて、かつUWPのReleaseビルド(ビルドプロパティの「.Net ネイティブ ツール チェーンでコンパイルする」にチェックついている状態)で作成したアプリを実行するとデバッグコンソールに以下のエラーが出力されて画像が表示されませんでした。

例外がスローされました: 'System.IO.FileNotFoundException' (System.Private.Reflection.Core.dll の中)

んで、対症療法です。(解決方法と書いていいか自信ない…)
というか、こうやったらできた。

画像のパスを設定する際にAssemblyを引数に加えてやると表示されるようになりました。

var assembly = typeof(App).GetTypeInfo().Assembly;
img.Source = ImageSource.FromResource("XamarinApp1.Images.xamarin.png", assembly);

.Net Nativeさん怖い…

Androidでアプリアンインストール時の確認メッセージに「~は次のアプリの一部です」と出てくる

Android開発に慣れている人は当たり前のことなのかもしれませんが)
Xamarin.Formsでデフォルトの設定で作成したアプリをアンインストールをしようとすると、以下のように謎の確認メッセージがでてきます。
Android 5以降で確認)
f:id:ShunsukeKawai:20160808115816p:plain

変ですよね。
普通のアプリはこんな感じになります。
f:id:ShunsukeKawai:20160808115850p:plain


それで直し方。

アプリの名前なので、AndroidManifestの記載とMainActivity.csの記載を一致させると期待する結果になりました。
f:id:ShunsukeKawai:20160808120150p:plain
f:id:ShunsukeKawai:20160808120159p:plain

結果
f:id:ShunsukeKawai:20160808120354p:plain

上の例はテスト用の命名でしたが、通常の開発だとプロジェクトの命名にスペースは普通入れないけど、アプリの命名にはスペース入れるってケースは多いと思います。
その場合、MainActivity側を修正しないといけないって点がちょっとハマりました…
(MainActivityの記載はプロジェクト作成時に勝手に作成されて、かつ違いがスペースの有無だけなので…)