yoppa.org


Blog

iOSのGUIをopenFrameworksのプロジェクトに追加する

ニセコ滞在中のBlog強化月間として、まずは備忘録的なものから。

openFramewroksでiPhone / iPadアプリをつくるのは、Processing的な文法に馴染んでいる人であれば、とても敷居が低くすぐにグラフィクスやサウンドを駆使したアプリが作成できる。ただ、その一方で、機能的なGUIを作ろうとすると意外に面倒だったりする。利用する側にとっても、独自のユーザインタフェイスが導入されているのは、よほど上手にデザインされていないと、使い勝手の悪いものになってしまいがち。

実はopenFrameworksで作成したiOSアプリのプロジェクトには、iOSのGUIをそのまま利用することが可能となっている。インタラクティブなアニメーションやサウンドなどopenFrameworksが得意な部分はそのまま利用して、GUIパートはiOSのネイティブなSDKで作成するという分業をすることで、とても簡単にiOSのアプリらしいプログラムを作成可能だ。openFrameworksのプロジェクトにiOSのGUIを追加する流れをまとめてみた。

プロジェクトの設定

まずは新規にプロジェクトを作成する。これは通常通りemptyExampleを複製して利用すれば良い。必要に応じてプロジェクト名や実行ファイル名を修正しておく。

次にここにiOSのGUIを作成するための、各種ファイルをプロジェクトに追加する。このやり方がoFプロジェクトへのiOS GUIの追加の肝要な部分かもしれない。GUIを追加するには、GUIのパーツの配置などを記録したXibファイルとその挙動を定義したクラスが必要となる。これらのファイルを追加するには、まずプロジェクトのsrcフォルダの中にグループを追加しておく。例えば「gui」という名前にしておくとわかり易い。

XcodeScreenSnapz001.jpg

次にここにGUIのファイル一式を追加する。先程のguiフォルダを右クリックして「New File」を選択する。追加するファイルの種類を選択する画面が表示されるので、ここで「UIViewController subclass」を選択する。ここが一番のポイント。

XcodeScreenSnapz002.jpg
XcodeScreenSnapz003.jpg

次にクラスの名前を指定するダイアログが表示される。ここで任意のクラス名を入力する。この例では「MyGuiView」にしている。この際に「With XIB for user interface」のオプションを忘れずにチェックしておく。最後に設定したクラスを保存する画面になるので「クラス名.mm」で指定する。openFrameworksではC++とObjective-Cを共存させるので、拡張子を「.mm」にするように注意する。この例では「MyGuiView.mm」とした。完了するとguiグループには「MyGuiView.h」「MyGuiView.mm」「MyGuiView.xib」の3つのファイルが追加されているはずだ。

XcodeScreenSnapz004.jpg

MyGuiViewクラスの編集

次にこの生成されたMyGuiViewクラスを編集していく。まずヘッダーファイル、MyGuiView.hから。

MyGuiView.h

#include "testApp.h"

@interface MyGuiView : UIViewController {
  testApp *myApp;
}
@end

変更したのは以下の部分。

1行目:testApp.hをインクルードしている。これによって、oFのメインのクラスであるtestAppクラスを参照している。

2行目:MyGuiViewクラスをUIViewControllerクラスを継承したクラスとして定義する。

4行目:testAppクラスへのポインタをプロパティとして用意してる。これにより、MyGuiViewクラスからoFのメインのクラス、testAppへ参照できるようにする。

次に実装ファイルであるMyGuiView.mmのほうをみていく

MyGuiView.mm

#import "MyGuiView.h"

@implementation MyGuiView

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  if (self) {
    // Custom initialization
  }
  return self;
}

- (void)didReceiveMemoryWarning
{
  // Releases the view if it doesn't have a superview.
  [super didReceiveMemoryWarning];
    
  // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidLoad
{
  [super viewDidLoad];
  // Do any additional setup after loading the view from its nib.
  myApp = (testApp*)ofGetAppPtr();
}

- (void)viewDidUnload
{
  [super viewDidUnload];
  // Release any retained subviews of the main view.
  // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
  // Return YES for supported orientations
  return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

@end

ここで変更してる部分は1行だけ。

26行目:用意したtestAppクラスへのポインタへ、ofGetAppPtr()メソッドを使用して参照を取得している。

testAppにMyGuiViewを追加

MyGuiViewクラスの準備はできたので、早速このGUIをtestAppクラスに読み込んで画面に表示してみたい。testApp.mmを以下のように編集する。

#include "testApp.h"
#include "MyGuiView.h"

MyGuiView * myGuiViewController;

//--------------------------------------------------------------
void testApp::setup(){	
  // register touch events
  ofRegisterTouchEvents(this);
	
  // initialize the accelerometer
  ofxAccelerometer.setup();
	
  //iPhoneAlerts will be sent to this.
  ofxiPhoneAlerts.addListener(this);
	
  //Add Gui setup
  myGuiViewController = [[MyGuiView alloc] initWithNibName:@"MyGuiView" bundle:nil];
  [ofxiPhoneGetUIWindow() addSubview:myGuiViewController.view];
}

// 以下省略

変更した点は、以下の部分。

2行目:ヘッダファイル、MyGuiView.hをインクルード。

4行目:MyGuiViewクラスへの参照のためのポインタを用意している。

18・19行目:MyGuiViewクラスのためのメモリを確保した上で、インスタンス化。インスタンス化したMyGuiViewを画面に追加している。

この状態でビルドをすると、空のiOSのGUIのためのViewが追加された状態でビルドが成功するはずだ。これでGUIを読みこむ準備はできたので、あとは実際のインタフェイスをInterfaceBuilderで作成していく。

Xibファイルの編集

では簡単なサンプルとして、スライダーを操作すると背景色の明度が変化するという簡単なインタフェイスを作成してみたい。

MyGuiView.xibファイルを開いてGUIの編集画面を表示する。ここで、画面の中央にObjectのリストからSliderを一つ選択して画面に配置する。またGUI画面の背景を透明にしたいので、Viewの設定のbackgroundをClearColorに設定する。

xcodeedit.png
XcodeScreenSnapz007.jpg

testAppクラスの準備

では、このSliderの値で背景色が変更できるように、testApp側を変更したい。具体的には、testApp.hに背景色を格納するためのofColorのインスタンス、bgColorを用意している。testApp.mmでは、update()メソッドの中で、設定した背景色bgColorで色を塗るように変更している。

testApp.mm

#include "testApp.h"
#include "MyGuiView.h" //add

MyGuiView * myGuiViewController; //add

//--------------------------------------------------------------
void testApp::setup(){	
  // register touch events
  ofRegisterTouchEvents(this);
	
  // initialize the accelerometer
  ofxAccelerometer.setup();
	
  //iPhoneAlerts will be sent to this.
  ofxiPhoneAlerts.addListener(this);
	
  //If you want a landscape oreintation 
  //iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

  //Initalize background color
  bgColor.r = 127;
  bgColor.g = 127;
  bgColor.b = 127;
    
  //Add Gui setup
  myGuiViewController	= [[MyGuiView alloc] initWithNibName:@"MyGuiView" bundle:nil];
  [ofxiPhoneGetUIWindow() addSubview:myGuiViewController.view];
}

//--------------------------------------------------------------
void testApp::update(){
  //Set background color
  ofBackground(bgColor.r, bgColor.g, bgColor.b);
}

//以下省略

MyGuiViewにアクションを追加

スライドを操作すると、testAppのプロパティbgColorを変更できるように、MyGuiViewクラスに変更を加える。具体的には、changeBackgroundColor()というアクションを新規に定義して、スライダの値をtestAppのbgColorに反映するようにしてみたい。

MyGuiView.h

#include "testApp.h"

@interface MyGuiView : UIViewController {
  testApp *myApp;
}

-(IBAction)changeBackgroundColor:(id)sender;

@end

MyGuiView.mm

#import "MyGuiView.h"

@implementation MyGuiView

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  if (self) {
    // Custom initialization
  }
  return self;
}

- (void)didReceiveMemoryWarning
{
  // Releases the view if it doesn't have a superview.
  [super didReceiveMemoryWarning];
    
  // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidLoad
{
  [super viewDidLoad];
  // Do any additional setup after loading the view from its nib.
  myApp = (testApp*)ofGetAppPtr();
}

- (void)viewDidUnload
{
  [super viewDidUnload];
  // Release any retained subviews of the main view.
  // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
  // Return YES for supported orientations
  return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (IBAction)changeBackgroundColor:(id)sender {
  UISlider * slider = sender;
  myApp->bgColor.r = [slider value] * 255;
  myApp->bgColor.g = [slider value] * 255;
  myApp->bgColor.b = [slider value] * 255;
}

@end

GUIパーツとアクションの接続

最後にXibファイルを、作成したchangeBackgroundColor()アクションに追加する。Xibファイルを開いてGUIを表示して、配置したSliderを右クリックする。するとSliderから送出可能なイベントの一覧が表示されるので、その中からValueChangedを選択し、Controlキーを押しながらドラッグして、FirstResponderに接続する。すると、FirstResponder側、つまりMyGuiViewクラスで定義されているアクションの一覧が表示されるので、そこからchangeBackgroundColorアクションを探して接続する。

XcodeScreenSnapz008.jpg
XcodeScreenSnapz010.jpg

完成!!

ビルドすると、スライダを操作すると背景色の明度を変化させることのできる簡単なアプリが作成されているはずだ。このサンプルはとてもシンプルなものだが、iOSのUIの機能をそのままつかえるので、ボタンやセレクタ、チェックボックスなど便利なパーツがそのままopenFrameworksで使用可能となる。また、Viewの中にMapKitの書類を貼りこむことで、地図を表示してプログラムを加えるといった応用も可能だ。

iOSシミュレータScreenSnapz001.png

サンプルファイルのダウンロード

完成したサンプルのファイル一式はこちらからダウンロードしてください。

yuta — 20 February 2013 14:39
いつも参考にさせていただいています。 唐突な質問で失礼致しますが、 openFrameworksとGUIの連携で、 GUI側からoF側に myApp->bgColor.b = [slider value] * 255; このような形式でやり取りしていますが、 数字ではなく文字列をやり取りする方法はあるのでしょうか。 文字列を渡すと、0x665541ddのような感じで oF側が文字列として認識できないようなのです。 ご教授いただければ嬉しいです。