[Xamarin][iOS] TabBarのようにUIButtonを等間隔で並べたフッターを作る

今回はUIButtonを等間隔で並べてフッターを作る方法です。
ぶっちゃけそれTabBarでいいんじゃないの?と思いますが
デザイン面でTabBarではクリアにできない問題もあったりします。
このテクニックはUIButtonだけじゃなく、View要素であれば何にでも使えます。

そもそも、iOSの場合View要素を等間隔で並べるってすごい面倒くさいです。
ボタンが偶数個の場合はUIViewを段組みにして比較的簡単に実装できますが、
奇数個になった瞬間大抵のプログラマは発狂するんじゃないでしょうか。

そんな面倒くさいこととはおさらばだ!
というわけで、Interface Builderを使うのをやめてコードでやりましょう。

// Viewの初期化のあたりでリストを初期化しておく
private List buttons;

nfloat _buttonWidth = this.Bounds.Size.Width / this.buttons.Count;
nfloat _buttonHeight = this.Bounds.Size.Height;

this.buttons.ForEach(button =>
{
	button.Frame = new CGRect(_buttonWidth * this.buttons.IndexOf(button), 0, _buttonWidth, _buttonHeight);
});

親Viewの横幅をボタンの数で割って、
そのViewの中に配置する各ボタンの横幅に設定してあげるだけですね。
このやり方だと、ボタンが偶数でも奇数でも対応出来るのでだいぶ楽だと思います。

 

[Xamarin][iOS] ステータスバーを非表示にしてフルスクリーンにする

エントリー追加時の環境
Xamarin Studio 6.1.2

今回はアプリをフルスクリーンにする設定です。
Xamarin.iOSでアプリを作る際に、
ステータスバー(時間とかバッテリー残量の表示領域)を消したい場合があります。

早速設定方法です。

Info.plistを開き、
アプリの設定から「配置情報」の「ステータスバーを非表示にする」にチェックを入れます。

そのままInfo.plistのソースに行を追加します。
追加する行は「View controller-based status bar appearance」です。

追加した行の型はブール値にし、値はNo(いいえ)にします。

設定は以上です。

[Xamarin][iOS] Xamarin.iOSアプリにAdMobを組み込む

エントリー追加時の環境
Xamarin Studio 6.1.2
AdMob 7.11.0

Xamarinだいぶ流行ってきていますね。
かくいう自分もXcode/Android Studioから乗り換えつつあります。
開発環境が変わるメリット・デメリットは多々ありますが、、
Xamarinはいいぞ!C#はいいぞ!

さてさて、個人のデベロッパとしてはどうしても
見過ごすわけにはいかないものがあります。
それはアプリ内広告です。

Xamarinってそもそも広告組み込めるの?というわけですね。
大丈夫です。大丈夫なんです。ちゃんと出来ます。Xamarinは出来る子です。
というわけで今回はXamarin.iOSで広告を表示させてみましょう。

※ 事前準備として、AdMob+Firebaseのアカウントが必須です。

まずAdMobの管理画面でアプリと広告ユニットを登録しIDなどを取得します。
取得しなければいけないのは下記のリストです。
・アプリID(ca-app-pub-XXXXXXXXXXXXXXXX~NNNNNNNNNN)
・広告ユニットID(ca-app-pub-NNNNNNNNNNNNNNNN/NNNNNNNNNN)
・GoogleService-Info.plist(Firebaseから取得する)

アプリID・広告IDはAdMobの管理画面から取得してきてください。
GoogleService-Info.plistはこんな手順で取得します。(画像見づらくてすみません)

1. アプリの管理画面からFirebaseの結合手順表示ボタンをクリックします。
2. ダイアログが表示されるので、「こちら」をクリックします。
3.Firebaseのページが開くので、GoogleService-Info.plistをダウンロードします。

先程挙げたリストをもって、ここからXamarin Studioでの作業です。

ソリューションエクスプローラの「コンポーネント」を右クリックし、
「コンポーネントのを更に取得する…」を選択します。
AdMobで検索し、「Firebase AdMob for iOS」を追加します。
先程Firebaseからダウンロードした「GoogleService-Info.plist」をソリューションに追加します。
「GoogleService-Info.plist」を右クリックし、
ビルドアクションの設定をBundleResourceに変更します。(やらないとエラーになる)

AppDelegate.csにコードを追加します。
追加するのは、SDKのインポートとFinishedLaunchingでの初期化処理です。

using Google.MobileAds;
using Firebase.Analytics;

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
	App.Configure();
	// AdMobから取得したアプリIDを設定
	MobileAds.Configure("ca-app-pub-XXXXXXXXXXXXXXXX~NNNNNNNNNN");

	return true;
}

最後に、広告を表示するViewの設定をします。

上記のStoryBoardに紐づくViewControllerのクラスファイルのViewDidLoadにコードを追加します。

using Google.MobileAds;

public override void ViewDidLoad()
{
	base.ViewDidLoad();
	// AdMobで作成した広告のユニットIDを設定
	this.bannerView.AdUnitID = "ca-app-pub-NNNNNNNNNNNNNNNN/NNNNNNNNNN";
	this.bannerView.RootViewController = this;

	Request request = Request.GetDefaultRequest();
	// テスト用の端末IDをリストで設定
	request.TestDevices = new String[]{ Request.SimulatorId };

	this.bannerView.LoadRequest(request);
}

ではシミュレータを起動して実行してみましょう。

Error MT3001: Could not AOT the assembly '/Users/~/iOS/obj/iPhone/Debug/build-iphone8.1-10.1.1/mtouch-cache/Build/Firebase.Analytics.dll' (MT3001) (hoge.iOS)
Error MT3001: Could not AOT the assembly '/Users/~/iOS/obj/iPhone/Debug/build-iphone8.1-10.1.1/mtouch-cache/Build/Google.MobileAds.dll' (MT3001) (hoge.iOS)

んービルドエラーですね。なんででしょうね。
ドキュメントを良く読むと・・・?

> • App doesn’t compile when Incremental builds is enabled. (Bug [#43689][5])
なるほどインクリメンタルビルドほにゃららと書いてあります。

これを回避するにはプロジェクトのオプションから
iOS Buildの「インクリメンタルビルドを有効にする」のチェックを外してあげます。

なお開発中は本番の広告を表示しているとペナルティを受ける可能性があります。
そのため開発中は実機のデバイスをテストデバイスに登録してあげないといけません。
必ずアプリケーション出力ログを見てみましょう。下記のようなログはないでしょうか。

2016-11-24 02:58:33.538 AdMobTest.iOS[1733:1075253] <Google> To get test ads on this device, call: request.testDevices = @[ @"NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" ];

もしログがあった場合、コードを少し修正します。
テストデバイス登録しておけば、テスト用の広告しか表示されなくなります。

request.TestDevices = new String[] { Request.SimulatorId, "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" };

AdMob広告組み込みの作業は以上です。

[iOS] [Objective-C] UIをワーカースレッドから操作する

はい。突然ですがエラーが出ました。

Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now…

うんうんなるほど。。日本語でおk。

結論から言うとUIKitはスレッドセーフではないからです。
ゆえに、メインスレッド以外からUIを操作しようとするとこんなエラーが吐かれるんですね。

ではどうするか。
そんな迷える子羊iOSエンジニアのために
‘performSelectorOnMainThread’というメソッドが用意されています。
これはJava(Android)でいうところのhandlerのようなものですね。

実行例としてはこんな感じです。

[self performSelectorOnMainThread:@selector(実行したいメソッド) withObject:nil waitUntilDone:TRUE];