酢ろぐ!

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

Xamarin.iOSで画像をUIImageViewに表示させる

Interface Builderを使用せずに、スクリーン上にイメージを表示させる方法です。UIImageViewを生成してUIImageオブジェクトを設定します。

var imageRect = new RectangleF(0f, 0f, 320f, 109f); 
using (var myImage = new UIImageView(imageRect))
{   
    myImage.Image = UIImage.FromFile("myImage.png");
    myImage.Opaque = true;
    view.AddSubview(myImage);
}

関連記事

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

Xamarin.iOSで自動ロック(スリープモード)に突入させないようにする

セキュリティ、および節電の為にiPhoneやiPadを放置していると自動ロックがかかり、スリープ状態に突入します。

自動ロック(スリープモード)を無効にする

動画閲覧アプリなど長時間ユーザーが触らないことが想定できます。デバイスを触らずに放置していた際に自動ロックされないようにIdleTimerDisabledプロパティtrueに設定します。

UIApplication.SharedApplication.IdleTimerDisabled = true;

自動ロック(スリープモード)を有効にする

ユーザーが動画を見終わった後など、再度自動ロックを有効にしたいことがあります。その場合には逆にIdleTimerDisabledプロパティfalseに設定します。

UIApplication.SharedApplication.IdleTimerDisabled = false;

関連記事

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

日本語で書かれたXamarin.iOSについてのTipsがあまり検索で引っかからないので忘備録代わりにメモしています。基本的時自分用に書いているのでザックリとした記載になっていますが、Xamarin.iOSでなにかやりたいという時の逆引きとして使っていただけると嬉しいです。

f:id:ch3cooh393:20141204174906p:plain

随時更新していきます。Xamarin Studioは日々アップデートがリリースされており、特にXamarin Studioについては記事の執筆時点から大きく変更されている可能性があります。現時点、以下の目次はClassic APIとUnified APIの記載が混じっていますのでご注意ください。

Xamarin.iOS開発ことはじめ

Xamarin.iOSを使ってアプリ開発するにあたって知っておいた方が良いTipsを紹介します。

画面やUIパーツ

画面遷移(storyboard,xib など)

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

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

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

ジェスチャー

  • タップを検出する
  • パンを検出する
  • ピンチイン・ピンチアウトを検出する
  • 回転を検出する
  • スワイプを検出する
  • ホールド(長押し)を検出する

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

ネットワーク

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

画像(UIImage)関連

CoreGraphicsを使って動的に図形を描画したりテキストを描画したUIImageオブジェクトを生成します。iOS 7からは

リソース

デバイス、センサー

機種名(デバイスモデル)

センサー

システム

パスワードの保存(Keychain)

アプリ間連携

iOS Binding Project

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

型の変換

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

UIKitを操作する際の大部分は、Xamarin.iOSによって.NET Frameworkで定義されている型で操作できるように補完されているのですが、例外もありNS***クラスであったりUI***クラスに型を変換する必要があります。この節ではXamarin.iOSを使ったプログラミングで避けられない型の変換について取り扱います。

ライブラリ

  • Zipファイルを解凍する
  • Zipファイルを作成する

Html Agility Pack

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

Xamarin.iOSを使ってiPhoneやiPadなどのiOSアプリ開発をする上で、基本的なプログラミングのTipsを紹介します。ほとんどC#の説明になっているところもあります。

文字列操作

日時操作

ハッシュ操作

タスク処理と並列処理

その他

ソースコードの共有化

関連書籍

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

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

Xamarin.iOSでAsset CatalogからUIImage型の画像を取得する

下図のように「NotFoundImage.imageset」という名前でAsset Catalog(xcassets)があったとします。

f:id:ch3cooh393:20141029214946p:plain

以下のコードでUIImage型の画像オブジェクトを取得することができます。

var defaultImage = UIImage.FromBundle("NotFoundImage");

関連記事

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

Xamarin.iOSでiOSデバイスの機種名(モデル名)を取得する

iOSデバイスも当初と比較するとiPad、iPod、iPhoneとそれぞれの派生モデルが複数登場してきました。搭載されているメモリや画面サイズなどアプリによって適切な表現方法が異なるケースがよくあります。

Objective-Cを使ってモデル名(機種名)を取得する方法は下記の通り過去に紹介したことがあります。

下記のコードのように、システム情報を取得するsysctlbyname関数を使用していました*1

+ (NSString *) platform{
    size_t size;
    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
    char *machine = malloc(size);
    sysctlbyname("hw.machine", machine, &size, NULL, 0);
    NSString *platform = [NSString stringWithUTF8String:machine];
    free(machine);
    return platform;
}

本記事では、上記のコードをXamarin.iOSでモデル名(機種名)を取得する方法について紹介します。Xamarin.iOSにはClassic API(旧)とUnified API(新)があり、以下のサンプルコードはUnified APIの方での実装になります。

sysctlbyname関数をP/Invokeで実行する際の記述の仕方が異なるようで少しハマってしまいました。

using System;
using System.Runtime.InteropServices;

namespace Softbuild.Hardware
{
    public class DeviceModel
    {
        public const string HardwareProperty = "hw.machine";

        [DllImport ("libc", CallingConvention = CallingConvention.Cdecl)]
        static internal extern int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string property,
            IntPtr output, IntPtr oldLen, IntPtr newp, uint newlen);

        public static string GetModelName()
        {
            var deviceVersion = string.Empty;

            var pLength = IntPtr.Zero;
            var pString = IntPtr.Zero;
            try
            {
                pLength = Marshal.AllocHGlobal(sizeof(int));
                sysctlbyname(DeviceModel.HardwareProperty, IntPtr.Zero, pLength, IntPtr.Zero, 0);
                var length = Marshal.ReadInt32(pLength);
                if (length <= 0)
                {
                    return string.Empty;
                }

                pString = Marshal.AllocHGlobal(length);
                sysctlbyname(DeviceModel.HardwareProperty, pString, pLength, IntPtr.Zero, 0);

                deviceVersion = Marshal.PtrToStringAnsi(pString);
            }
            finally
            {
                if (pLength != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pLength);
                }
                if (pString != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pString);
                }
            }

            return deviceVersion;
        }

    }
}

DeviceModel.GetModelNameメソッドを実行するとiPhone 7,1などのモデル名(機種名)を取得することができますので、この値を元にiPhone 6 plusiPhone 5sと言ったようにモデル名を特定することが可能です。

Xamarin.iOSでUIImage型のバッファとbyte[]型とを相互に変換する

Xamarin.iOSでUIImage型のバッファとbyte型とを相互に変換してみましょう。

UIImage型とbyte型とでは相互に直接型の変換をおこなうことができません。一度、NSData型に変換した上で希望する型への変換をおこないます。

UIImageとbyteとを相互に変換する

UIImage → byte

public byte[] UiimageToByteArray(UIImage image)
{
    byte[] bytes = null;
    try
    {
        using (var data = image.AsPNG())
        {
            bytes = data.ToArray();
        }
    }
    catch (Exception)
    {
        bytes = null;
    }

    return bytes;
}

byte[] → UIImage

public UIImage ByteArrayToUiimage(byte[] bytes)
{
    UIImage image = null;
    try
    {
        image = new UIImage(NSData.FromArray(bytes));
    }
    catch (Exception ex)
    {
        image = null;
    }
    return image;
}

拡張メソッドクラス化したもの

上記のメソッドを拡張メソッドクラスにしてみました。

using System;
using Foundation;
using UIKit;

namespace Softbuild.Media
{
    public static class UIImageConvertExntensions
    {
        public static byte[] ToBytes(this UIImage image)
        {
            byte[] bytes = null;
            try
            {
                using (var data = image.AsPNG())
                {
                    bytes = data.ToArray();
                }
            }
            catch (Exception)
            {
                bytes = null;
            }

            return bytes;
        }

        public static UIImage FromBytes(byte[] bytes)
        {
            UIImage image = null;
            try
            {
                image = new UIImage(NSData.FromArray(bytes));
            }
            catch (Exception)
            {
                image = null;
            }
            return image;
        }
    }
}

関連記事

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

Xamarin.iOSでUnwind Segueを使って前の画面に戻る(Storyboard使用時/プログラム編)

iOSで表示された画面はスタック構造で履歴が管理されており、pushViewController:animated:メソッドpopViewControllerAnimated:メソッドを使用することで、指定した画面へ遷移したり遷移元の画面に戻るということをしていました。

iOS 5でStoryboardが導入されるとSegueを使って指定した画面への遷移が可能になりました。iOS 6からは指定した画面へ戻る機能が追加されました*1。この指定した画面へ戻る機能のことをUnwind Segueと呼びます。

本記事では、Xamarin.iOSとStoryboardを使っている場合にどのようにして前の画面に戻せるのか紹介したいと思います。

前提情報

本記事を読むにあたり前提となる情報を先に書いておきます。Storyboardでは下図の方向でSegueを指定しています。

f:id:ch3cooh393:20141022172944p:plain

それぞれ名前を便宜上、下記の通りとします。

  1. アカウント画面 (クラス名:AccountViewController)
  2. メニュー画面 (クラス名:MenuViewController)

(1) 遷移元のViewControllerにActionメソッドを書く

まずは戻ってきたい画面のクラスに下記のActionメソッドを書きます。ここではアカウント画面に戻りたいので、AccountViewControllerクラスを開いて、下記のActionメソッドをコピペしましょう。

[Action ("backToAccountsPage:")]
public void BackToAccountsPage(UIStoryboardSegue segue)
{
    // ここには何も実装しなくていい
}

Action属性はiOS側から見たときの名前です。UIStoryboardSegue*を引数に取る必要があるので[Action ("backToAccountsPage:")]のように最後に:をつけます。全部Storyboard上で設定できると楽なのですが……

(2) Interface builderでメニュー画面のExitを選択する

次にInterface builderを開き、メニュー画面のExitを選択します。Xcode 6.1ではオレンジ色のアイコンになっています。Exitを選択すると画面の右側にPresenting Seguesの一覧が表示されます。

f:id:ch3cooh393:20141022173829p:plain

先ほどAction属性につけた名前が表示されています。下図ではbackToAccountsPage:を選択して右ドラッグしながらメニュー画面でドロップします。

f:id:ch3cooh393:20141022174017p:plain

Menu Sceneに下図のようにUnwind segue to Scene Exit Placeが追加されました。

f:id:ch3cooh393:20141022174038p:plain

NOTE: どうでも良いことですが、先日までXamarin StudioのiOS Designerを使ってstoryboardを書いていたわけなのですが、Xcode 6.1をインストールしてから調子が悪くなってしまい、とうとうstoryboardがiOS DesignerでもXcode Interface builderでも開けなくなって爆死しました。

(3) Unwind SegueにIdentifier(名前)を付けます

戻る時に使用するSegue(Unwind Segue)に名前をつけます。ここでは下図のようにBackToAccountsPageと名前をつけています。

f:id:ch3cooh393:20141022174111p:plain

以上で、アカウント画面に戻るための準備は完了しました。

(4) MenuViewController側で任意のタイミングでUnwind Segueを実行する

ここからは任意のタイミングでアカウント画面に戻る方法を紹介します。

例えば、アカウント情報が足りていない場合に、遷移元であるアカウント画面に戻す場合には下記のようにPerformSegueメソッドを実行します。先ほど設定したBackToAccountsPageというSegueを実行しています。

public override void ViewDidAppear(bool animated)
{
    base.ViewDidAppear(animated);

    if (Account == null)
    {
        PerformSegue("BackToAccountPage", this);
        return;
    }
}

終わり。

あ、Storyboard上のViewのIBActionと紐づけることでコードを書かずに遷移元の画面に戻るということも可能なのです。さらにモーダル画面から更に前の前の画面に戻るといったようなこともできたはずです。

*1:確かiOS 6だったと思う…