酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Xamarin.iOSで画像(UIImage)をカメラロールへ保存する

Objective-Cで画像を保存するにはUIImageWriteToSavedPhotosAlbum関数を使いました。Xamarin.iOSで同じことをしたかったのですが検索能力が低くて該当するメソッドを見つけることができませんでした。

UIImageWriteToSavedPhotosAlbum関数に相当するメソッドは、UIImage型のメンバーメソッドに含まれています。SaveToPhotosAlbumメソッドは非同期で実行されます。

var image = UIImage.FromBundle("NotFoundImage");
image.SaveToPhotosAlbum(new UIImage.SaveStatus(
    delegate(UIImage img, NSError error) {

        var hasError = (error != null);
        if (hasError) {
            System.Diagnostics.Debug.Write("failed!");
        } else {
            System.Diagnostics.Debug.Write("success!");
        }
    }));

関連記事

Xamarin.iOSを使ってアプリ開発する際に逆引きとしてお使いください。

Xamarin.iOSでDictionary<TK, TV>型からNSDictionary型オブジェクトへ変換する

TinamiをみるアプリをXamarin.iOSで作りました。実験的な作り方をしていて、まだほとんど何もできません(Tinamiのランキングを見て、ふぁぼるくらいです)。

https://itunes.apple.com/jp/app/illust-catcher/id933153646?mt=8&uo=4&at=10l8JW&ct=hatenablog

このアプリでは、アプリの設定値を保存するのにNSUserDefaultsを直接触っています。ちなみにXamarin.iOSでNSUserDefaultsに設定値を保存する方法はこちらで紹介しています。

しかし、Android版を開発しようとした時に設定値を保存するコードが異なるのは、少し使い勝手が悪いのでラッパーのようなものを書いています。

Dictionary<string, string>型からNSDictionary型オブジェクトへ変換する

Dictionary<TK, TV>型のオブジェクトをNSUserDefaultsで保存するためには、ネイティブ側の型(NSDictionary)のオブジェクトに変換する必要があります。

この変換が面倒臭いので何か良い方法はないかと調べていたところ、NSDictionary.FromObjectsAndKeysメソッドというobject[]を引数に取るメソッドがありました。これはイケるのでは!と思って下記のコードを書きました。

var dict = new Dictionary<string, string>();
dict["aaa"] = "aaa";
dict["bbb"] = "bbb";

var nsdict = NSDictionary.FromObjectsAndKeys(
    dict.Keys.Cast<object>().ToArray(),
    dict.Values.Cast<object>().ToArray());

var count = nsdict.Count;

この時のcountの値は2でした。NSDictionary.FromObjectsAndKeysメソッドで第2引数のvaluesにstring型のオブジェクトを渡せば、よしなに計らってくれて内部的にNSString型へ変換してくれるようです。

おまけ:Dictionary<string, object>型からNSDictionary型オブジェクトへ変換する

よしなに計らってくれるNSDictionary.FromObjectsAndKeysメソッドに甘えて、以下のようなコードを書きました。

var dict = new Dictionary<string, object>();
dict["aaa"] = "aaa";
dict["bbb"] = new NSString("bbb");
dict["ccc"] = 1;
dict["ddd"] = 393.9f;

var nsdict = NSDictionary.FromObjectsAndKeys(
    dict.Keys.Cast<object>().ToArray(),
    dict.Values.Cast<object>().ToArray());

var count = nsdict.Count;

結果としてcountの値は4でした。デバッガーを使ってnsdictの中を見てみると全てNSString型になっているのが分かります。

f:id:ch3cooh393:20141119105037p:plain

このことから、objectの型から適切にNSStringやNSNumberに変換してくれるわけではなく、object.ToString()した結果をNSStringに突っ込んでいるのかなと思いました。

関連記事

Xamarin.iOSを使ってアプリ開発する際に逆引きとしてお使いください。

Xamarin.Androidでアラート(AlertDialog)を使ってメッセージを表示する

Xamarin.Androidでアラートを表示します。

var alertDialog = new AlertDialog.Builder(this);
alertDialog.SetTitle("title");
alertDialog.SetMessage("Message");
alertDialog.SetPositiveButton("Positive", (sender, args) =>
{ 
    // Positiveボタンをタップしたときに呼ばれる
});
alertDialog.SetNegativeButton("Negative", (sender, args) =>
{
    // Negativeボタンをタップしたときに呼ばれる
});

RunOnUiThread(() =>
{
    alertDialog.Show();
});

上記のサンプルコードを実行すると下図のようにアラートが表示されます。

f:id:ch3cooh393:20141114151046p:plain

関連記事

Xamarin.Androidを使ってアプリ開発する際に逆引きとしてお使いください。

Xamarin.Androidの開発Tipsの記事まとめ

随時更新していきます。ここまで書いてから一覧性が悪いような気がしてきました。

Xamarin.Android開発ことはじめ

画面やUIパーツ

画面遷移

テキストを表示させるラベル(TextView)

ボタン(Button)

画像を表示するビュー(ImageView)

アラート、ダイアログ(AlertDialog)

データ、ファイル、ストレージ

ネットワーク

画像や音声、マルチメディア

リソース

デバイス、センサー

センサー

システム

パスワードの保存(Keychain)

アプリ間連携

ローカライズ、多言語対応

型の変換

Xamarin.Androidは、.NET ランタイムでありAndroidとの間の部分を補完します。AndroidアプリではOS標準コントロールであるAndroidのUIパーツを度々操作する必要がでてきます。

UIパーツを操作する際の大部分は、Xamarin.Androidによって.NET Frameworkで定義されている型で操作できるように補完されているのですが、例外もありネイティブの型へ変換する必要があります。この節ではXamarin.Androidを使ったプログラミングで避けられない型の変換について取り扱います。

基本的なプログラミングTips

Xamarin.Androidを使ってAndroidアプリ開発をする上で知っておきたいTipsを紹介します。

文字列操作

日時操作

ハッシュ操作

タスク処理と並列処理

その他

ソースコードの共有化

関連書籍

C#によるiOS、Android、Windowsアプリケーション開発入門 (MSDNプログラミングシリーズ)

C#によるiOS、Android、Windowsアプリケーション開発入門 (MSDNプログラミングシリーズ)

【解決済み】Xamarin.Androidで「SimpleListItem1」などの組み込みのリストアイテムのレイアウトの定義値が存在しなくって困った件

Xamarin.iOSで作っていたアプリが一通りできたので審査に出しました。同じアプリをXamarin.Androidで作るにあたってリストビューってどうやって表示するんだろうと調べてみました。

ListViewに文字列を表示させるだけであれば難しくなさそうと思って実装してみたのですが、Android.Resource.Layout.SimpleListItem1が定義されておらずコンパイルエラーが発生しました。

namespace hoge.Android
{
    [Activity(Label = "hoge", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : ListActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            var adapter = new ArrayAdapter(this, 
        Android.Resource.Layout.SimpleListItem1); // ← 定義されていない?
            for (var i = 0; i < 40; i++)
        {
            adapter.Add("item_" + i);
        }

            ListAdapter = adapter;
        }
    }
}

単純にアセンブリの参照忘れかもしれませんがにっちもさっちもいかず、Android.Resource.Layout.SimpleListItem1の代わりにsimple_list_item_1の定義値でもある0x01090003を指定して実行することはできました。

何が悪いのか分からなくって、基本的なところで詰まっていると先が長そうです……

追記

お昼から戻ってきたらリプライを頂いていました。

解決しました!まさしくその通りで、hoge.Android 名前空間Android 名前空間が競合していました。

Xamarin.iOSでiOSバインディングプロジェクト(Unified API)を作成するとビルドエラーが発生する

XamarinではBeta channelを使用していて、最新バージョンまでアップデートしています。

Xamarin.iOSを使ってアプリを書いているのですが、とある広告SDKを組み込もうと思った時にビルドエラーが発生してしまうことが分かりました。

.aファイルをインポートした際に発生するということではなく、Unified APIのiOSバインディングプロジェクト(iOS Binding Project)を作成した直後の状態で、何もしていないのにビルドエラーが発生してしまいます

以下のようなビルドエラーが発生します。

Error: /Users/ch3cooh/hoge/example.csproj: /Users/ch3cooh/hoge/example.csproj could not import "$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.ObjCBinding.CSharp.targets" (example)

example.csprojファイルを開くと、確かにImportタグにXamarin.iOS.ObjCBinding.CSharp.targetsが指定されており、参照しようとしていることがわかりました。

<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.ObjCBinding.CSharp.targets" />

当然ですが、iOSバインディングプロジェクト(Classic API)の方では、きちんとビルドが通ります。Importタグは下記のようにパスが違っていることがわかりました。

<Import Project="$(MSBuildExtensionsPath)\Xamarin\Xamarin.ObjcBinding.CSharp.targets" />

このことからXamarin Studioで生成されたiOSバインディングプロジェクト(Unified API)のテンプレートの参照先がおかしいということはすぐに分かったのですが、どうやって解決すればよいのかで悩み込んでしまいました。

解決策

Xamarinの公式サイトを見ても何も書かれていない(と思う)ので、試行錯誤しながら解決策を見つけました。とりあえず、これでアプリからiOSのネイティブライブラリを参照できることがわかったので、この修正で問題ないか田淵さんXamarin.iOSに詳しい方に聞いてみたいですね……。

以下、ビルドエラーの解決策です。

まずは、iOSバインディングプロジェクトのxxxx.csprojをエディタで開きます。TargetFrameworkIdentifierタグを追加します。

  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>

    〜〜〜〜省略〜〜〜〜〜

    <TargetFrameworkIdentifier>Xamarin.iOS</TargetFrameworkIdentifier>
  </PropertyGroup>

元々存在しているImportタグのパスをClassic APIのと同じにします。

  <Import Project="$(MSBuildExtensionsPath)\Xamarin\Xamarin.ObjcBinding.CSharp.targets" />

関連記事

Xamarin.iOSを使ってアプリ開発する際に逆引きとしてお使いください。

Xamarin.iOSでXamarin.Authを使ってKeychainにパスワードを保存する

Xamarin.AuthをXamarin Components Storeからインストールする

Componentsフォルダを選択して、右側に設定ボタンをクリックするとコンテキストメニューが出てくるのでGet More Components...を選択します。

f:id:ch3cooh393:20141105210045p:plain

Xamarin Componentsのダイアログが表示されるので、右上の検索欄で「xamarin.auth」と入力します。Xamarin.Authが右側の検索結果に表示されます。

f:id:ch3cooh393:20141105210052p:plain

Add to Appボタンをクリックして、Xamarin.Authをインストールしておきます。

f:id:ch3cooh393:20141105210105p:plain

パスワードを保存する

// パスワードを Keychain に保存する
var prop = new Dictionary<string, string>();
prop["password"] = "password";
var account = new Xamarin.Auth.Account("userName", prop);

var store = Xamarin.Auth.AccountStore.Create();
store.Save(account, "serviceId");

Accountクラスでは、保存時に第2引数のpropというDictionaryをNSDataにシリアライズして保存しています。

パスワードを取り出す

var store = Xamarin.Auth.AccountStore.Create();
var account = store.FindAccountsForService("serviceId")
    .SingleOrDefault();

関連記事

Xamarin.iOSを使ってアプリ開発する際に逆引きとしてお使いください。