[OSX]MainMenu.xibのメニーにアクションを設定する
某所で、nibのメニューの項目をソースコードで設定する方法の説明があったので自分で試してみた。
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
NSMenuItem *menuItem = [mainMenu itemWithTitle:@"File"];
NSLog(@"%@", menuItem);
NSMenu *subMenu = [menuItem submenu];
menuItem = [subMenu itemWithTitle:@"Close"];
NSLog(@"%@", menuItem);
[menuItem setTarget:self];
[menuItem setAction:@selector(demo:)];
}
- (IBAction)demo:(id)sender
{
NSLog(@"%s", __func__);
}
nibで定義したメニーに対してという事で、NSApplicationのmainMenuメソッドでNSMenuのインスタンスを取得する。
これは、メニューの塊のようなので、itemWithTitle:メソッドでタイトルからNSMenuItemのインスタンスを取得する。
ここで取得できるのは、メニューバーに対する、TOPのFileやEditメニューの塊のようなので、さらに取得できたものに対して、ここでは"Close"メニューを取得する。
これに対して、setTarget:とsetAction:を行えば、File/Closeメニューを選択されると、ここで設定したメソッドが呼ばれるようになる。
2012-07-09 22:14:14.668 Demo[13089:403]
2012-07-09 22:14:14.671 Demo[13089:403]
2012-07-09 22:14:21.790 Demo[13089:403] -[AppDelegate demo:]
関連情報
Cocoa Event Handling Guide: Event Handling Basics
【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
[iOS]グラフ描画ライブラリ(4)
グラフ描画ライブラリSimpleChartを制作する際に参考にしたのが、iOSのテーブル・ビュー。
データ一式を渡して、ビューが頑張って表示するという方式もあると思うが、テーブル・ビューは、データそのものは、データ・ソースのプロトコルに対応したクラス側で管理し、テーブル・ビューに対して、件数や内容を返す方式となっている。
SimpleChartが参考にしているS7GraphViewも、基本的にはテーブル・ビューに似た方式だが、配列そのものを渡された表示している等、異なる部分もあるので、これをテーブル・ビュー的な方式にしてみた。
@class SimpleChartView;
@protocol SimpleChartViewDataSource
- (NSUInteger)numberOfPlotsInSimpleChartView:(SimpleChartView *)simpleChartView;
- (NSUInteger)numberOfXValuesInSimpleChartView:(SimpleChartView *)simpleChartView;
- (NSUInteger)simpleChartView:(SimpleChartView *)simpleChartView numberOfYValuesInPlot:(NSUInteger)plotIndex;
- (id)simpleChartView:(SimpleChartView *)simpleChartView XValueAtIndex:(NSUInteger)index;
- (NSNumber *)simpleChartView:(SimpleChartView *)simpleChartView YValueAtPlot:(NSUInteger)plotIndex value:(NSUInteger)valueIndex;
@optional
- (BOOL)simpleChartView:(SimpleChartView *)simpleChartView shouldFillPlot:(NSUInteger)plotIndex;
@end
@protocol SimpleChartViewDelegate
@optional
@end
@interface SimpleChartView : UIView
@property (nonatomic, weak) IBOutlet id dataSource;
@property (nonatomic, strong) IBOutlet NSFormatter *xValuesFormatter;
@property (nonatomic, strong) IBOutlet NSFormatter *yValuesFormatter;
@property (nonatomic, assign) BOOL drawAxisX;
@property (nonatomic, assign) BOOL drawAxisY;
@property (nonatomic, assign) BOOL drawGridX;
@property (nonatomic, assign) BOOL drawGridY;
@property (nonatomic, strong) UIColor *xValuesColor;
@property (nonatomic, strong) UIColor *yValuesColor;
@property (nonatomic, strong) UIColor *gridXColor;
@property (nonatomic, strong) UIColor *gridYColor;
@property (nonatomic, assign) BOOL drawInfo;
@property (nonatomic, copy) NSString *info;
@property (nonatomic, strong) UIColor *infoColor;
- (void)reloadData;
@end
配列データのアクセスで、いちいち、メソッドを呼び出すのは冗長だと思うが、どうだろか。あ、これは内部の話なので、利用側にはかんけいないか。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/SimpleChart - GitHub
関連情報
http://code.google.com/p/s7graphview/
S7GraphViewのサイト。残念ながら、閉鎖されたようだ。
【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
[OSX]beforeafter
以前のプロジェクトでnetstatのログ解析でお世話になっていたコマンドbeforeafter。特に不満はないのだが、著作権の扱いがよく分からない為、ほぼ、同じ動きをするPerlスクリプトを用意してみた。
#!/usr/bin/perl
use strict;
use warnings;
# netstat -s > netstat.before
# ...
# netstat -s > netstat.after
# beforeafter.pl netstat.before netstat.after
if ($#ARGV != 1) {
print 'Usage: beforeafter.pl before_file after_file', "\n";
exit 1;
}
my $filename1 = $ARGV[0];
my $filename2 = $ARGV[1];
open FH1, "<$filename1" or die "error: $filename1: $!\n";
open FH2, "<$filename2" or die "error :$filename2: $!\n";
my $c1 = '';
my $c2 = '';
my $d1 = 0;
my $d2 = 0;
my $separator1 = 0;
my $separator2 = 0;
while (not eof FH1) {
$c1 = getc FH1;
if ($c1 =~ /\s|\t|:|\(|\n/) {
print "$c1";
$separator1 = 1;
}
elsif ($c1 =~ /\D/) {
print "$c1";
$separator1 = 0;
}
else {
if ($separator1 == 0) {
print "$c1";
next;
}
my $n1 = $c1;
$d1 = $n1;
while (not eof FH1) {
$c1 = getc FH1;
if ($c1 =~ /\d/) {
$n1 = $c1;
$d1 = (10 * $d1) + $n1;
}
else {
last;
}
}
while (not eof FH2) {
$c2 = getc FH2;
if ($c2 =~ /\s|\t|:|\(|\n/) {
$separator2 = 1;
}
elsif ($c2 =~ /\D/) {
$separator2 = 0;
}
else {
if ($separator2 == 0) {
next;
}
my $n2 = $c2;
$d2 = $n2;
while (not eof FH2) {
$c2 = getc FH2;
if ($c2 =~ /\d/) {
$n2 = $c2;
$d2 = (10 * $d2) + $n2;
}
else {
last;
}
}
last;
}
}
my $delta = $d2 - $d1;
print "$delta";
if (not eof FH1) {
print "$c1";
}
}
}
close FH1;
close FH2;
# End Of File
使い方は以下のとおり。
$ netstat -s > netstat.before
...何らかの処理をする...
$ netstat -s > netstat.after
$ beforeafter.pl netstat.before netstat.after
関連情報
Annotated Output of netstat -s
Further Discussion of the Statistics
【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
[iOS]OpenGL ES(2)
描画で必要な状態やデータをオブジェクトという塊で扱っている。前回のコードで利用していたオブジェクトは以下のとおり。
- バッファオブジェクト
- その名のとおりメモリの塊。
- レンダバッファ
- 色データと深度データ、ステンシスデータがある。
- フレームバッファ
- 描画対象のバッファ。
OpenGL ESの状態は、レンダリングコンテキストと呼ばれるものに格納されている。これがあるため、他のアプリケーションから独立して描画する事が出来るのだが、そのために、上記のオブジェクトはバインドと呼ばれるコマンドで、コンテキストと関係づけて利用する。
iOSでは、EAGLContextクラスを通してレンダリングコンテキストを操作する事になる。
/* GLKViewControllerのサブクラス */
/* OpenGL ES 1.1コンテキストを作成 */
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
/* GLKitViewに設定 */
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormatNone;
/* 現在のコンテキストに設定 */
[EAGLContext setCurrentContext:self.context];
フレームバッファを1個生成して、それを現在のコンテキストに関連づける。
glGenFramebuffersOES(1, &_defaultFramebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, self.defaultFramebuffer);
レンダバッファを1個生成して、それを現在のコンテキストに関連づける。レンダバッファ領域をビューの大きさ分生成し、それをさっき生成したレンダバッファに色データとして設定する。
glGenRenderbuffersOES(1, &_colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, self.colorRenderbuffer);
glRenderbufferStorageOES(
GL_RENDERBUFFER_OES,
GL_RGBA8_OES,
self.view.bounds.size.width,
self.view.bounds.size.height);
glFramebufferRenderbufferOES(
GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES,
GL_RENDERBUFFER_OES,
self.colorRenderbuffer);
フレームバッファの状態に問題がないか確認する。
GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) ;
if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
DBGMSG(@"failed to make complete framebuffer object %x", status);
}
これで準備が整ったので、描画をとりかかる。
レンダバッファの消去。今回は色のレンダバッファのみだ。
glClear(GL_COLOR_BUFFER_BIT);
頂点配列とカラー配列を有効にする。
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
頂点配列を設定する。
GLfloat vertices[] = {
-0.5f, -0.5f,
0.5f, -0.5f,
0.0f, 0.5f,
};
glVertexPointer(2 , GL_FLOAT , 0 , vertices);
第一引数はxとy座標のデータなので2を指定。第二引数は浮動小数点を指定。
色配列を設定する。
GLubyte colors[] = {
255, 0, 0, 255,
255, 0, 0, 255,
255, 0, 0, 255,
};
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
第一引数はRGBAなので4を指定。第二引数はバイト単位を指定。
そして、三角形を描画。
glDrawArrays(GL_TRIANGLES, 0, 3);
ソースコード
GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/IRIS - GitHub
関連情報
OpenGL ES Programming Guide for iOS
OpenGLの神髄
【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
[iOS]OpenGL ES
避けてきた分野に3Dがある。でも、まったく避けていただけでは進歩がないので、理解できていないながらも、簡単な何かをやろうと思う。三角形を描画するだけに挑戦する。
プロジェクトにGLKit.frameworkとOpenGLES.frameworkを追加する。
GLKViewControllerのサブクラスを作成する。
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
@interface PrimitiveViewController : GLKViewController
@end
書籍を読みながら、理解していないながらもなんとか記述。
#import "PrimitiveViewController.h"
@interface PrimitiveViewController ()
@property (strong, nonatomic) EAGLContext *context;
@property (assign, nonatomic) GLuint defaultFramebuffer;
@property (assign, nonatomic) GLuint colorRenderbuffer;
@property (assign, nonatomic) GLuint depthRenderbuffer;
- (void)setupGL;
- (void)tearDownGL;
@end
@implementation PrimitiveViewController
@synthesize context = _context;
@synthesize defaultFramebuffer = _defaultFramebuffer;
@synthesize colorRenderbuffer = _colorRenderbuffer;
@synthesize depthRenderbuffer = _depthRenderbuffer;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.defaultFramebuffer = 0;
self.colorRenderbuffer = 0;
self.depthRenderbuffer = 0;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!self.context) {
NSLog(@"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[self setupGL];
}
- (void)viewDidUnload
{
[self tearDownGL];
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)setupGL
{
[EAGLContext setCurrentContext:self.context];
glGenFramebuffersOES(1, &_defaultFramebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, self.defaultFramebuffer);
glGenRenderbuffersOES(1, &_colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, self.colorRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES,
GL_RGBA8_OES,
self.view.bounds.size.width,
self.view.bounds.size.height);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES,
GL_RENDERBUFFER_OES,
self.colorRenderbuffer);
glGenRenderbuffersOES(1, &_depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, self.depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES,
GL_DEPTH_COMPONENT16_OES,
self.view.bounds.size.width,
self.view.bounds.size.height);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_DEPTH_ATTACHMENT_OES,
GL_RENDERBUFFER_OES,
self.depthRenderbuffer);
GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) ;
if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
DBGMSG(@"failed to make complete framebuffer object %x", status);
}
}
- (void)tearDownGL
{
[EAGLContext setCurrentContext:self.context];
if (self.defaultFramebuffer) {
glDeleteFramebuffersOES(1, &_defaultFramebuffer);
self.defaultFramebuffer = 0;
}
if (self.colorRenderbuffer) {
glDeleteRenderbuffersOES(1, &_colorRenderbuffer);
self.colorRenderbuffer = 0;
}
if (self.depthRenderbuffer) {
glDeleteRenderbuffersOES(1, &_depthRenderbuffer);
self.depthRenderbuffer = 0;
}
}
#pragma mark - GLKView and GLKViewController delegate methods
/*
- (void)update
{
}
*/
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClear(GL_COLOR_BUFFER_BIT);
/* 頂点の定義 */
GLfloat vertices[] = {
-0.5f, -0.5f,
0.5f, -0.5f,
0.0f, 0.5f,
};
/* カラー(RGBA)の定義 */
GLubyte colors[] = {
255, 0, 0, 255,
255, 0, 0, 255,
255, 0, 0, 255,
};
/* 頂点配列とカラー配列を有効 */
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
/* 頂点配列とカラー配列を設定 */
glVertexPointer(2 , GL_FLOAT , 0 , vertices);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
/* 描画 */
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
@end
実行。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/IRIS - GitHub
関連情報
OpenGL ES Programming Guide for iOS
OpenGLの神髄
【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
[iOS]SQLite
iOS SDKがCoreDataに対応してからsqliteを直接利用する機会は減ったと思われるが、やはり、知っていた方が便利なので挑戦する。
以前は、Apple DeveloperサイトでSQLiteBooksといるサンプル・コードが入手できたが、今はないようだ。ただ、著者は以前入手しておいたので、それを参考にした。良かった!
sqliteを使用する為には、『libsqlite3.0.dylib』をリンクし、sqlite3.hをインクルードする。
#import <sqlite3.h>
今回は、sqliteとのやり取りを隠す、Databaseクラスを用意した。以下がヘッダーだ。
#import <Foundation/Foundation.h>
#import <sqlite3.h>
@interface Database : NSObject
@property (nonatomic, assign) sqlite3 *database;
- (void)demo;
@end
内容は以下のとおり。初期化と破棄、そして、簡単なSQLの実行。
#import "Database.h"
@interface Database ()
@end
@implementation Database
@synthesize database = _database;
- (id)init
{
DBGMSG(@"%s", __func__);
self = [super init];
if (self) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"database.sql"];
if (sqlite3_open([path UTF8String], &_database) != SQLITE_OK) {
sqlite3_close(_database);
self.database = NULL;
DBGMSG(@"Failed to open database with message '%s'.", sqlite3_errmsg(_database));
}
}
return self;
}
- (void)dealloc
{
DBGMSG(@"%s", __func__);
if (sqlite3_close(_database) != SQLITE_OK) {
DBGMSG(@"Error: failed to close database with message '%s'.", sqlite3_errmsg(_database));
}
self.database = NULL;
/* [super dealloc]; */
}
- (void)demo
{
DBGMSG(@"%s", __func__);
if (NULL == self.database) return;
const char *sql = "CREATE TABLE demo ('id' INTEGER PRIMARY KEY, 'name' CHAR(32))";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(self.database, sql, -1, &statement, NULL) == SQLITE_OK) {
DBGMSG(@"[OK]sqlite3_prepare_v2(), srl: %s", sql);
while (sqlite3_step(statement) == SQLITE_ROW) {
DBGMSG(@"[OK]sqlite3_step()");
}
}
sqlite3_finalize(statement);
sql = "INSERT INTO demo(name) VALUES ('test')";
if (sqlite3_prepare_v2(self.database, sql, -1, &statement, NULL) == SQLITE_OK) {
DBGMSG(@"[OK]sqlite3_prepare_v2(), srl: %s", sql);
while (sqlite3_step(statement) == SQLITE_ROW) {
DBGMSG(@"[OK]sqlite3_step()");
}
}
sqlite3_finalize(statement);
sql = "SELECT id FROM demo";
if (sqlite3_prepare_v2(self.database, sql, -1, &statement, NULL) == SQLITE_OK) {
DBGMSG(@"[OK]sqlite3_prepare_v2(), srl: %s", sql);
while (sqlite3_step(statement) == SQLITE_ROW) {
DBGMSG(@"[OK]sqlite3_step()");
int primaryKey = sqlite3_column_int(statement, 0);
NSLog(@"primaryKey: %d", primaryKey);
}
}
sqlite3_finalize(statement);
}
@end
サンプルでは、demoメソッドを呼ぶボタンを用意して、動作確認した。上手くいったようだ。
2012-07-03 20:20:28.602 Database[7795:f803] -[Database init]
2012-07-03 20:20:31.391 Database[7795:f803] -[Database demo]
2012-07-03 20:20:31.393 Database[7795:f803] [OK]sqlite3_prepare_v2(), srl: CREATE TABLE demo ('id' INTEGER PRIMARY KEY, 'name' CHAR(32))
2012-07-03 20:20:31.461 Database[7795:f803] [OK]sqlite3_prepare_v2(), srl: INSERT INTO demo(name) VALUES ('test')
2012-07-03 20:20:31.464 Database[7795:f803] [OK]sqlite3_prepare_v2(), srl: SELECT id FROM demo
2012-07-03 20:20:31.464 Database[7795:f803] [OK]sqlite3_step()
2012-07-03 20:20:31.465 Database[7795:f803] primaryKey: 1
ソースコード
GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/Database - GitHub
関連情報
iPhone デベロッパーズ クックブック
【Cocoa練習帳】
http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)
[iOS]アラートのカスタマイズ(6)
『iPhone デベロッパーズ クックブック』で紹介されていた手順で、一定時間が経過すると自動で閉じるアラートの例だ。
タイマーから呼ばれる、アラートを閉じるメソッド。
- (void)performDismiss:(NSTimer *)theTimer
{
UIAlertView *alertView = [theTimer userInfo];
[alertView dismissWithClickedButtonIndex:0 animated:NO];
}
以前のiOSでは、アプリケーション側からアラートを閉じる状況は少なかった為か、『iPhone デベロッパーズ クックブック』では奇抜な方法という感じの説明だったが、アプリケーションがバックグラウンドに移行出来るようになって、バックグラウンドに遷移する際に、開いていたアラートを閉じるか、そのままにするかをアプリケーション側で判断する事になったので、この手順も普通の処理になったと思う。
アラート表示時にタイマーを仕込む。
- (IBAction)alertDismiss:(id)sender
{
UIAlertView *alertView = [[UIAlertView alloc] init];
alertView.delegate = self;
alertView.title = @"Alert Dismiss";
alertView.message = @"\n\ndismiss alert";
[NSTimer scheduledTimerWithTimeInterval:3.0f
target:self
selector:@selector(performDismiss:)
userInfo:alertView repeats:NO];
[alertView show];
}
自動で閉じる為、ボタンを用意しなかったが、ボタンがあることを前提としてレイアウトになるようで不格好となってしまった。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/WebApp - GitHub
関連情報
UIWebView Class Reference
UIWebViewDelegate Protocol Reference
iPhone デベロッパーズ クックブック
[iOS][Web]ネイティブWebアプリケーション(その4)
やはり、Cocoa勉強会は参加すると得る物がある。先日のCocoa勉強会で教えてもらった事だ。
UIWebViewのstringByEvaluatingJavaScriptFromString:を使った方法だと、HTMLコンテンツ側からは、UIWebViewDelegateのデリゲート・メソッドが呼ばれるタイミングでしかイベントを発生させる事ができない。そこで、任意のタイミングでイベントを発生させる方法に挑戦する。ヒントは、以前紹介したFacebook SDKで得られた情報だ。
イベント通知用のURLスキームを追加する。
サンプルでは、webapp-demoとした。
このURLスキームを受けるデリゲート・メソッドを追加する。
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
DBGMSG(@"%s, url(%@)", __func__, url);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"demo"
message:@"WebApp"
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[alert show];
return YES;
}
それでは、HTMLコンテンツに、このURLスキームを呼ぶコードを追加してみよう。
<a href="webapp://demo/debug?abc=123">call WebApp</a><br />
選択してみる。
アラートが表示された。
2012-07-02 09:26:59.438 WebApp[2366:f803] -[AppDelegate application:openURL:sourceApplication:annotation:], url(webapp-demo://WebApp.demo/debug?abc=123)
最初、URLスキームとしてWebAppを登録していたが、おそらく、なにかとぶつかったのか上手くいかなかった。使用するURLスキームの選択は重要なようだ。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/WebApp - GitHub
関連情報
UIWebView Class Reference
UIWebViewDelegate Protocol Reference
第53回Cocoa勉強会 関東
今回も貸し会議室の席は埋まりました。
飛び入りの発表があったので漏れていたらすいません。今回は、Cocoaで書いたデータ解析ツールとSandboxとPrivilege Separation、MPMusicPlayerControllerとAVPlayer、Xcode Tips、質問コーナと盛りだくさんでした。
[OSX][iOS]データ解析(2)
現在のXcodeはビルドした結果の出力先が隠されている為、コマンドライン・ツールをビルドして、操作するという流れが面倒になっている為、標準入力から値を得る部分をデバッグ用に内部変数から値を得る様に修正する。
#ifndef DEBUG
NSFileHandle *fhi = [NSFileHandle fileHandleWithStandardInput];
#endif
NSFileHandle *fho = [NSFileHandle fileHandleWithStandardOutput];
#ifndef DEBUG
NSData *datainput = [fhi readDataToEndOfFile];
NSString *str = [[NSString alloc] initWithData:datainput encoding:NSUTF8StringEncoding];
#else
NSString *str = @"12:23:45 start\n1,10\n2,13";
#endif
行単位に切り出せたら、Perlのchompのように末尾の改行文字を削ろう。
NSCharacterSet *charSet = [NSCharacterSet newlineCharacterSet];
line = [line stringByTrimmingCharactersInSet:charSet];
著者はよくやっているのだが、ログのある時刻の抜き出しをやってみよう。
regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d\\d:\\d\\d:\\d\\d)"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:line
options:0
range:NSMakeRange(0, line.length)];
if (0 < [match numberOfRanges]) {
NSString *tm = [line substringWithRange:[match rangeAtIndex:0]];
NSLog(@"time: %@", tm);
}
完成。
@autoreleasepool {
#ifndef DEBUG
NSFileHandle *fhi = [NSFileHandle fileHandleWithStandardInput];
#endif
NSFileHandle *fho = [NSFileHandle fileHandleWithStandardOutput];
#ifndef DEBUG
NSData *datainput = [fhi readDataToEndOfFile];
NSString *str = [[NSString alloc] initWithData:datainput encoding:NSUTF8StringEncoding];
#else
NSString *str = @"12:23:45 start\n1,10\n2,13";
#endif
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(.+)\n|(.+)"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSArray *array = [regex matchesInString:str
options:0
range:NSMakeRange(0, str.length)];
NSTextCheckingResult *matches;
for (matches in array) {
NSString *line = [str substringWithRange:[matches rangeAtIndex:0]];
#ifdef DEBUG
NSData *dataout = [[NSData alloc] initWithBytes:[line UTF8String]
length:[line lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
[fho writeData:dataout];
#endif
NSCharacterSet *charSet = [NSCharacterSet newlineCharacterSet];
line = [line stringByTrimmingCharactersInSet:charSet];
regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d\\d:\\d\\d:\\d\\d)"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:line
options:0
range:NSMakeRange(0, line.length)];
if (0 < [match numberOfRanges]) {
NSString *tm = [line substringWithRange:[match rangeAtIndex:0]];
NSLog(@"time: %@", tm);
}
}
}
ソースコード
GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/mac/analog - GitHub
関連情報
File System Programming Guide
NSRegularExpression Class Reference