Born Neet
IB不使用&オープンソースなiPadアプリ(テキストエディタ)を作る 第2回:SplitView
- 2010-08-15 (Sun)
- Edhita
第2回です。
今回はSplitViewを作ります。iPadアプリなUIといえばこれですね。
※ ちなみに、IBを使えば新規プロジェクトでSplitVIew basedを選ぶだけで終わりです。
作るのは2ペインの単純なアプリ。
左ペインで選んだ文字列を、右ペインに表示するだけです。
(Portraitモードの時は1ペインにして、代わりにPopViewを使用します)
まだテキストエディタとしての機能はありません。
それでは早速始めます。
[参考文献]
今回の参考サイトは以下のとおり。
参考書籍は第1回をご参照下さい。
iPadで追加されたUISplitViewを試してみる « CLSmooth BLOG…
RootViewController
まずはleft paneとなるRootViewControllerを作ります。
と言っても特別なことはしておらず、何の変哲もないTableViewControllerです。
RootViewController.h
ヘッダファイルはこんな感じです。
#import
@class DetailViewController;
@interface RootViewController : UITableViewController {
DetailViewController *detailViewController;
}
//@property (nonatomic, retain) IBOutlet DetailViewController *detailViewController;
@property (nonatomic, retain) DetailViewController *detailViewController;
@end
RootViewController.m
お次は実装ファイル。
#import "RootViewController.h"
#import "DetailViewController.h"
@implementation RootViewController
@synthesize detailViewController;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
}
/*
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
*/
/*
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
*/
/*
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
*/
/*
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
*/
// Ensure that the view controller supports rotation and that the split view can therefore show in both portrait and landscape.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"CellIdentifier";
// Dequeue or create a cell of the appropriate type.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
}
// Configure the cell.
cell.textLabel.text = [NSString stringWithFormat:@"Row %d", indexPath.row];
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
/*
When a row is selected, set the detail view controller's detail item to the item associated with the selected row.
*/
detailViewController.detailItem = [NSString stringWithFormat:@"Row %d", indexPath.row];
}
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc {
[detailViewController release];
[super dealloc];
}
@end
もうお気づきかもしれませんが、IBでSplitVIew basedを選択した時に自動作成されるファイルを流用しています。
プロな方はどうか知りませんが、少なくとも僕はこんな内容を一から書くスキルも元気もありません。
というわけで、太字部分以外はそのままです。
(つまりヘッダファイルのプロパティからIBOutletを削っただけ)
DetailViewController
次にright paneとなる、DetailVIewControllerです。
見た目は単なるToolbar付きViewControllerですが、
UISplitViewControllerのDelegteとなっており、
画面が回転した際、ToolbarにPopover用のボタンを追加・削除する機能を持ちます。
DetailViewController.h
まずはヘッダファイル。rootview同様、IBOutletを消すだけです。
#import
@interface DetailViewController : UIViewController {
UIPopoverController *popoverController;
UIToolbar *toolbar;
id detailItem;
UILabel *detailDescriptionLabel;
}
//@property (nonatomic, retain) IBOutlet UIToolbar *toolbar;
@property (nonatomic, retain) UIToolbar *toolbar;
@property (nonatomic, retain) id detailItem;
//@property (nonatomic, retain) IBOutlet UILabel *detailDescriptionLabel;
@property (nonatomic, retain) UILabel *detailDescriptionLabel;
@end
DetailViewController.m
次に実装。長いですが、最後にinitメソッドを追加して、
IBの代わりにtoolbar、labelを作成しているだけです。
#import "DetailViewController.h"
#import "RootViewController.h"
@interface DetailViewController ()
@property (nonatomic, retain) UIPopoverController *popoverController;
- (void)configureView;
@end
@implementation DetailViewController
@synthesize toolbar, popoverController, detailItem, detailDescriptionLabel;
#pragma mark -
#pragma mark Managing the detail item
/*
When setting the detail item, update the view and dismiss the popover controller if it's showing.
*/
- (void)setDetailItem:(id)newDetailItem {
if (detailItem != newDetailItem) {
[detailItem release];
detailItem = [newDetailItem retain];
// Update the view.
[self configureView];
}
if (popoverController != nil) {
[popoverController dismissPopoverAnimated:YES];
}
}
- (void)configureView {
// Update the user interface for the detail item.
detailDescriptionLabel.text = [detailItem description];
}
#pragma mark -
#pragma mark Split view support
- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc {
barButtonItem.title = @"Root List";
NSMutableArray *items = [[toolbar items] mutableCopy];
[items insertObject:barButtonItem atIndex:0];
[toolbar setItems:items animated:YES];
[items release];
self.popoverController = pc;
}
// Called when the view is shown again in the split view, invalidating the button and popover controller.
- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
NSMutableArray *items = [[toolbar items] mutableCopy];
[items removeObjectAtIndex:0];
[toolbar setItems:items animated:YES];
[items release];
self.popoverController = nil;
}
#pragma mark -
#pragma mark Rotation support
// Ensure that the view controller supports rotation and that the split view can therefore show in both portrait and landscape.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#pragma mark -
#pragma mark View lifecycle
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
/*
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
*/
/*
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
*/
/*
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
*/
/*
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
*/
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.popoverController = nil;
}
#pragma mark -
#pragma mark Memory management
/*
- (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)dealloc {
[popoverController release];
[toolbar release];
[detailItem release];
[detailDescriptionLabel release];
[super dealloc];
}
- (id)init {
if (self = [super init]) {
self.view.backgroundColor = [UIColor whiteColor];
toolbar = [[UIToolbar alloc] init];
[toolbar sizeToFit];
[self.view addSubview:toolbar];
// これやっとかないとpopview出すボタンが追加できない([toolbar items]がnullになるから)
[toolbar setItems: [NSArray array]];
// heightはdefaultのfontsizeに合わせて17
detailDescriptionLabel = [[UILabel alloc] initWithFrame: CGRectMake(0, self.view.bounds.size.height * 0.5, self.view.bounds.size.width, 17)];
// 常に上下中央
detailDescriptionLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
detailDescriptionLabel.textAlignment = UITextAlignmentCenter;
detailDescriptionLabel.text = @"Detail view content goes here";
[self.view addSubview:detailDescriptionLabel];
}
return self;
}
@end
AppDelegate
最後にAppDelegate。
実はこいつが一番修正が多かったりする。
(太字が前回からの修正点)
AppDelegate.h
rootとdetailのヘッダファイルをimport。
#import
#import "RootViewController.h"
#import "DetailViewController.h"
@interface EdhitaAppDelegate : NSObject {
UIWindow *window;
}
//@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
AppDelegate.m
前回はwindowにlabelを突っ込んでたけど、
今回はrootとdetailをもとにSplitViewを作成。
※ rootはNavigationControllerのrootとして設定
#import "EdhitaAppDelegate.h"
@implementation EdhitaAppDelegate
// @synthesize window;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
RootViewController *rootViewController = [[RootViewController alloc] init];
// tableview単体じゃ仕方ないのでnavviewでwrap
UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
DetailViewController *detailViewController = [[DetailViewController alloc] init];
rootViewController.detailViewController = detailViewController;
UISplitViewController *splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = [NSArray arrayWithObjects:navigationController, detailViewController, nil];
splitViewController.delegate = detailViewController;
[window addSubview:splitViewController.view];
[rootViewController release];
[navigationController release];
[detailViewController release];
[window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
/*
Restart any tasks that were paused (or not yet started) while the application was inactive.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
*/
}
#pragma mark -
#pragma mark Memory management
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
/*
Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
*/
}
- (void)dealloc {
[window release];
[super dealloc];
}
@end
完成
[まとめ]
第2回目の今回は単純なSplitViewアプリを作ってみました。
ようやくIBでSplitViewプロジェクトを作成したものに追いついたわけですね。
次回以降いよいよテキストエディタの機能を追加していきます。
ご期待下さい。
[コード]
今回のコードは「Edhita2.zip」に入っています。
GitHubのdownloadページからダウンロードして下さい。
Downloads for tnantoka's Edhita - GitHub
IB不使用&オープンソースなiPadアプリ(テキストエディタ)を作る 第1回:プロジェクトの作成
- 2010-08-08 (Sun)
- Edhita
3月にv1.0、7月にv1.5(.1)を公開したjsanyですが、
たくさんの方に使っていただいているようで、嬉しい限りです。
ただ、何もわかってない頃に作り始めたこともあり、こいつの内部実装はもうぐちゃぐちゃです。
その結果、1.5で重大なバグを出してしまい、すぐ1.5.1を出すハメになりました。
このままいくといずれ破綻しそうなので、
オープンソースなアプリで腕を磨いて、改めてObjective-Cを勉強しなおしたいと思います。
題材はテキストエディタ、名前はまんまですが「edhita」です。
中身を深く理解する為、IBは使用しません。
また、ソースコードは全てgithubで公開します。
このアプリ作成で得たノウハウをjsanyにFeedbackしていこうという魂胆です。
※ ホントはjsanyをオープンソースにしたいんですが、
今のコードは恥ずかしすぎてとても晒せないので。
(ただでさえ高くないプログラマとしての評判がガタ落ちしちゃいます)
では、始めます。
初回はIB不使用なiPadアプリプロジェクトを作成するところまでやります。
[参考書籍]
と、その前に参考にした書籍を紹介しておきます。
他に参考にした情報があれば、それぞれの回で記載します。
2010/09/09 追加
[IB不使用なプロジェクトの作成]
前置きが長くなりました。
IBを使わないプロジェクトは、以下の手順で作成できます。
簡単ですね。
- Xcodeの新規プロジェクトから、「Window-based Appication」、Productは「iPad」を選択。
- MainWindow.xibファイルを削除する。
(参照を削除じゃなく、一緒にゴミ箱に入れる)
- 「プロジェクト名-info.plist」の「Main nib file base name」を項目ごと削除。
- main.mのmain()を修正。
(plistに指定したnib fileから自動で取得されていたものをコードで指定)int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // int retVal = UIApplicationMain(argc, argv, nil, nil); int retVal = UIApplicationMain(argc, argv, nil, @"EdhitaAppDelegate"); [pool release]; return retVal; }
- AppDelegateの修正。
(自動で作成されていたUIWindowインスタンスを作成し、viewとlabelを追加。)// EdhitaAppDelegate.m // @synthesize window; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; UIView *view = [[UIView alloc] initWithFrame: [window frame]]; [window addSubview:view]; UILabel *label = [[UILabel alloc] initWithFrame: [window frame]]; label.text = @"Hello, iPad!"; label.textAlignment = UITextAlignmentCenter; label.font = [[UIFont alloc] fontWithSize: 50]; [view addSubview:label]; [window makeKeyAndVisible]; return YES; }
// EdhitaAppDelegate.h //@property (nonatomic, retain) IBOutlet UIWindow *window;
- ビルドと実行。
[まとめ]
今回の作業はこれで終了です。
俗に言うHello, world!ですね。
次回以降、テキストエディタとしての機能を追加していきます。
宜しくお付き合い下さい。
突っ込みもお待ちしています。
[コード]
今回のコードは「Edhita1.zip」に入っています。
GitHubのdownloadページからダウンロードして下さい。
Downloads for tnantoka's Edhita - GitHub
[おまけ:目次]
こんな感じで進めたい。
- 第1回:プロジェクトの作成
- 第2回:SplitView
- 第3回:ファイル操作とNavigationController
- 第4回:Syntax Highlighting
- 第5回:Accessory View
- 第6回:広告
- 第7回:FTP
- 第?回:未定
JavaScriptでタイピングゲーム(英語長文暗記用)
- 2010-08-07 (Sat)
- JavaScript
いつまでも「英語苦手」とも言ってられないので、
やる気を出してプレゼンでも暗記するかと思って、
任意の長文をタイピングするようなWebサービスを探したけど、
イマイチ見つからなかったので作ってみた。
ながらプログラミングなので、
コードはひどいけど、一応動いているのでよしとする。
(といってもテスト全然してないけど。)
タイピング試作版
使い方は至って簡単。
初めて開いた時に表示されるtextareaに英文を入力してregisterします。
※ テストの時はこの英文を使いました。
すると、localStorageに英文を保存して、タイピングができるようになります。
問題を変えるときは、右上の問題登録から再度同じ操作を行ないます。
と、まぁ最低限の機能は備えてるかな、と思います。
自分で使ってる中で不満があれば、機能追加するかもしれません。
以上、久々の投稿にしては質が低いなー。
- Comments: 0
- TrackBack (Close):
JavaScript Anywhere 1.5リリース!
- 2010-07-10 (Sat)
- iPhone/iPad/IPod Touch
iPadに(暫定)対応したJavaScript Anywhereのアップデート版がリリースされました。
6月末リリースを目指していたので、約1週間遅れですが。
今回のバージョンでは、皆様からいただいた要望を元に、
以下の機能を盛り込みました。
是非、使ってみてください。(もちろん価格は無料のままです。)
- 複数プロジェクトのサポート
- Landscapeモードに対応
- 各種設定
- テキスト・背景色
- メール送信時の形式(添付ファイル or 本文)
- 画面回転ロック
- Webからファイルをダウンロード
ただ、付け焼刃の対応をした為、コードがもうぐちゃぐちゃです。
このままでは動作の安定にも悪影響を与えかねないということで、
改めてObjective-Cの勉強をしています。
次のリリースでは書き直したコードによる安定・高速版をお届けできる予定です。
「打倒有料ライバルアプリ」で頑張っていきますので、
今後ともjsanyを宜しくお願いします。
jQTouchとHTML5のlocalStorageでwikiみたいなの作った(w3c widget化も)
- 2010-06-22 (Tue)
- iPhone/iPad/IPod Touch
約一ヶ月ぶりのエントリ…完全に暑さに負けてますね^^
というわけで今日は日曜出勤の振替休日を利用して、
最近Ext.jsに合流したりで話題のjQTouchを触ってみました。
そしてできあがったのが、このwikiもどきです。
wiki5
説明
触っていただければ使い方はなんとなくわかると思います。
ただ、Mobile Safari以外では一切動きません。
(iOS4上では動作未確認です。)
また、タップが効かない、ボタンがアクティブのままになる、
画面が真っ黒になる等、全体的に挙動が安定していません。
(はい、テスト不足です。)
どうにもならなくなったときは「wiki5/index.html?clear」にアクセスすれば、
初期化されます。
あと、ついでですがw3c widgetにもしてみました。
cloud readerで動作することを軽く確認しています。
wiki5
中身の解説は暑いのでやめておきます(!)が、
ソースコードはgithubに置いてますので、ご自由にどうぞ。
tnantoka's wiki5 at master - GitHub
感想
今回、jQTouchのドキュメントを一切読まずに進めてしまったので、
予想以上に苦労しました。
やっぱり無茶はダメですね。
あと、iPhone Webアプリはデバッグが大変だな、と。
MacのSafariで動いても、シミュレータでは全然動かないとかも多々。
小さいアプリこそ実は、IBでちょちょいと作ってネイティブアプリにした方が
楽だったりするのかも。
使用ライブラリ
- jQTouch — jQuery plugin for mobile web development
説明不要。 - Showdown - Markdown in JavaScript
Wiki記法の代わりにMarkdownを採用しました。 - JSON in JavaScript
Mobile SafariでJSON.parse/stringifyする為に。
(現在の実装はlocalStorageに文字列しか格納できないので。仕様上はany data。)
参考サイト
jQTouch – 画面遷移の前にデータ処理 | STUDIO Bloom 大阪・北摂地域を中心としたWebサイト・システム制作
他、多数。
以上です。
あーブログの書き方をどんどん忘れていくー
- Comments: 0
- TrackBack (Close):
- Search
-
Loading
- Feeds
- Links
- スポンサードリンク