このサイトは自作なので、Wordpressみたいなエディタ強化がイマイチです。
tinymceからシンプルエディタをJSで切替る際、どうしてもHTMLタグが挿入されちゃう(>_<)
とりあえずtinymceは一旦停止して、Wordpressに付属しているquicktags.jsを使ってみました。 インストールされている場所は、
wordpress/wp-includes/js/quicktags.js
これを自分のサイトにコピーしてHTMLヘッダーで読み込み
HTMLタグのツールバーは以下のようにすれば表示される。
コンテンツをダウンロードしてファイルに保存 これはよくまとまっているサイトが、NSURLDownloadはCocoaフレームワークのものでUIKitにはないようです。
よって、NSURLDownloadをラッパークラスとして使うようです。
iPhone SDKレシピ2:NSURLConnectionを使ってファイルをダウンロードする
- (void)connection:(NSURLConnection *)i_connection didReceiveResponse:(NSURLResponse *)i_response;
- (void)connection:(NSURLConnection *)i_connection didReceiveData:(NSData *)i_data;
- (void)connectionDidFinishLoading:(NSURLConnection *)i_connection;
- (void)connection:(NSURLConnection *)i_connection didFailWithError:(NSError *)i_error;
CoreDataを使うまでもなく手っ取り早く設定を保存したい場合は、SettingsBundleを利用すると便利かも。
(1) 新規ファイル > ResourceでSettings Bundleを選択し、Settings.bundledで保存
(2) Settings.bundle > Root.plistを開く
(3) PreferenceSpecifiersを開き、各項目の設定 item0:グループ item1:ユーザ名 item2:パスワード として作成した。
(4) Settingsを読み込む
※viewDidAppearで設定した
- (void)viewDidAppear:(BOOL)animated {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];;
[super viewDidAppear:animated];
}
(5) アプリを起動して、設定を確認してみる。
これだけ。 スライダーや階層型で設定する事も可能です。 AndroidもXMLで設定する感じで素晴らしいと思ったけど、Xcodeも素晴らしいよ。
えっと、iPhone参考本でついにQuartzの章にはいりましたが、いきなり話を脱線してQuartz Composerについて。
Xcodeをインストールすると「Quartz Composer」てのがもれなく付属してます。 ただ書籍も持ってないしチンプンカンプンなので、ネットで検索してみた。
Quartz Composerによるビジュアルフロープログラミング
「Quartz Composer」はiPhoneはもとより、JavaScriptに対応しているみたいです。 ・Snow Leopard対応!初めてのQuartz Composer入門 ・Quartz Composer×JavaScriptでRSSリーダーを作ろう
つまりは、Adobeのオーサリングツールみたくアニメーション作成できるわけっすね。
そしてアニメーションに限らず、OSCを使ったMIDIやWifi通信、ビデオカメラとか色んなライブラリが使えるようです。
ライブラリのカテゴリだけでこんなに!
こんな感じでパッチをつないで、ノンコーディングで作成していきます。
QuickTimeで書き出してみました >>サンプル動画
Windowsだとコーデックの関係か?デフォでみれない
みたいなので、キャプチャーを
Quartz Composerのマニュアルは例のごとくAppleから(苦) Apple Developer Quartz Composer
・どこでもセーブ ・永続化が必要ならNSCoding Protocolを実装する の記事ように、NSKeyedArchiverとNSKeyedUnarchiverでデータをシリアライズしてファイル保存できる。
ポイントはNSCodingプロトコルを実装する事。 手軽なデータ保存アプリにはいいかも。 また履歴の保存とか面倒なデーター群をシリアライズして1つのフィールドに格納するのに便利かな?
逆にデータの一部分だけ取り出したいとか、検索が絡んでくると大変な気もする。。。 実際に使ってないので、あくまでも机上の空想だがw
CoreDataの基礎 その1|CoreDataの基礎 その2|CoreDataの基礎 その3|CoreDataの基礎 その4
実際にデータを保存してみる。
結論から言ってハマりました。。。
まず、.xcdatamodelでのEntityの設定で以下のようにしました。
・Entity名:User ・アトリビュート キー:no int16 氏名:name String
で、ですね、「SELECT * FROM User WHERE no = 2;」みたいなことをやろうと、
NSPredicate *pred = [NSPredicate predicateWithFormat:@"(no = %d)", i];
を実行してもまったくもって抽出できない! 文字列のフォーマットが駄目なのか?と何度も検証したが何度やってもうまくいかないorz で、判明したのが、
アトリビュート名で「no」を使うと抽出できなかった・・・
SQLiteの予約語なのか?Core Dataで利用できないのか?わからないが、アトリビュート名を 「no」→「id」に変更したらうまくいきました。 ※idはObjective-Cの予約語なのでこれも命名的によろしくないとは思いますが・・・
駄目駄目なかなり汚いコードですが・・・まぁ、調査と言う事で。
- (void)applicationDidEnterBackground:(UIApplication *)application {
Core_Data_persistenceAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSError *error;
for (int i = 1; i <= 4; i++) {
NSString *fieldName = [NSString stringWithFormat:@"line%d", i];
UITextField *textField = [self valueForKey:fieldName];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"User"
inManagedObjectContext:context];
[request setEntity:entityDescription];
NSPredicate *pred = [NSPredicate predicateWithFormat:@"(id = %d)", i];
[request setPredicate:pred];
NSManagedObject *user = nil;
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil) {
NSLog(@"Error!");
}
if ([objects count] > 0) {
//UPDATE
user = [objects objectAtIndex:0];
} else {
//INSERT
user = [NSEntityDescription insertNewObjectForEntityForName:@"User"
inManagedObjectContext:context];
}
[user setValue:[NSNumber numberWithInt:i] forKey:@"id"];
[user setValue:textField.text forKey:@"name"];
[request release];
}
[context save:&error];
}
- (void)viewDidLoad {
NSLog(@"viewDidLoad");
Core_Data_persistenceAppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"User"
inManagedObjectContext:context];
[request setEntity:entityDescription];
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
[request release];
if (objects == nil) {
NSLog(@"Error!");
}
for (NSManagedObject *oneObject in objects) {
NSNumber *rid = [oneObject valueForKey:@"id"];
NSString *name = [oneObject valueForKey:@"name"];
UITextField *textField = [self valueForKey:fieldName];
textField.text = name;
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:app
];
[super viewDidLoad];
}
(1) applicationDidEnterBackgroundが呼び出される (2) ApplicateionDelegateからNSManagedObjectContextを取得 (3) NSEntityDescription entityForNameでUser Entityを取得 (4) データベースリクエストである (5) NSFetchRequest setPredicateでクエリーを設定
SELECT * FROM User WHERE id = %d
(6) (NSArray *) NSManagedObjectContext executeFetchRequest:NSFetchRequestでクエリー実行 (7) 既にデータがある場合はNSArrayの最初のデータ(NSManagedObject)取得 (8) データがない場合はNSEntityDescription insertNewObjectForEntityForNameでNSManagedObjectを新規作成 (9)setValueで各値を設定し、NSManagedObjectContext saveで保存
(1) viewDidLoadが呼び出される (2) ApplicateionDelegateからNSManagedObjectContextを取得 (3) NSEntityDescription entityForNameでUser Entityを取得 (4) NSFetchRequest setPredicateでクエリーを設定 (5) (NSArray *) NSManagedObjectContext executeFetchRequest:NSFetchRequestでクエリー実行 (6) NSArrayをループして、valueForKeyでデータ設定
また、viewDidLoadでapplicationDidEnterBackgroundのObserverを設定しています。 ただ、Applicationの遷移でデータを制御するのは危険かと思うのであくまでも一例で・・・
iOS4になってマルチタスクになりましたが、起動・終了・サスペンドの処理の仕様が変更になっています。
参考本でアプリ終了時(iPhoneのホームボタンで終了時)にデータ保存するサンプルがあるんですけど、その通りコーディングして実行しても、データが保存されません。
もっと詳しく言うと、
(1) viewDidLoadが呼ばれ、NSNotificationCenterのObserverdでapplicationWillTerminateを登録
(2) iPhoneのホームボタンをクリック(アプリ終了ではない)
(3) applicationWillTerminateは呼ばれない
※参考本ではapplicationWillTerminateで保存処理を書いている
(4) データが保存されない
つうわけで、NSLogでUIApplicationのDelegateメソッドを追ってみました。 ■アプリケーション起動時
(1) didFinishLaunchingWithOptions (2) UIViewController loadVIew (3) UIViewController viewDidLoad (4) UIViewController viewWillAppear (5) UIViewController viewDidAppear (6) applicationDidBicomActive
■ホームボタンクリック
(1) applicationWillResignActive (2) applicationDidEnterBackground
■再度ホームボタンクリック
(1) applicationWillEnterForeground (2) applicationDidBecomeActive
となります。
つまりマルチタスクのiOS4では自動でapplicationWillTerminateが呼ばれない! のでapplicationWillResignActiveかapplicationDidEnterBackgroundに保存処理を書くか・・・。まぁユーザに明示的に保存させるのが必須ってことですね。 でもさ、でもさ、XcodeでCore Dataプロジェクト作成すると
デフォルトでapplicationWillTerminateにCore Dataの処理が記述されてる(??)
どっちなのApple?・・・まぁ、マルチタスクじゃない方を優先してるんだろうけどw
以前にも書きましたが、やはりマルチタスクは鬼門なのでiOS4 アプリケーションの状態遷移を参考にしてアプリを作っていった方が良いです。
ちなみに、iPhoneのホームボタンをダブルクリックするとバックグランドで待機しているアプリ一覧が表示されます。
これは気づかなかったw
てか、全てのアプリが待機状態になっています。
マルチタスクってのはこういうことだったのか・・・真のマルチタスク(?)じゃないのね。
検索してたら、Objective-Cのメモリ管理について記載があったのでメモ書きというか、コピーw AppleDevelopper メモリ管理
<
p class="code">新しいオブジェクトに動的にメモリを割り当てる 新たに割り当てられたメモリを適切な値に初期化する 2 つのステップを完了するまで、オブジェクトは完全には機能しません。 各ステップを実行するのは別々のメソッドですが、通常は 1 行のコードに記述します。
id anObject = [[Rectangle alloc] init];
NSObject では 2 つの主要なメソッド、allocとallocWithZone:を定義しています。
+ (id)alloc; + (id)allocWithZone:(NSZone *)zone;
メソッドが引数を持たない場合、メソッド名はそれらの 4 文字のみ、つまりinitになります。 引数を持つ場合、「init」プレフィックスの後に引数のラベルが続きます。
たとえば、NSView は initWithFrame: メソッドで初期化することができます。 init...メソッドは新たに割り当てられたレシーバ以外のオブジェクトを返すかnil を返すことがあるため、プログラムでは alloc または allocWithZone: が返す値だけでなく、初期化メソッドが返す値を使用することが重要です。
次のコードは init が返す値を無視しているため、非常に危険(らしい)です。
id anObject = [SomeClass alloc];
[anObject init];
[anObject someOtherMessage];
この代わりに、オブジェクトを安全に初期化するには、割り当ておよび初期化メッセージを 1 行のコードに結合する必要があります。
id anObject = [[SomeClass alloc] init];
[anObject someOtherMessage];
Photosにスライドショーとサムネイル切替をつけてみた。
スライドショーは、jQueryライブラリであるCrossSlide を利用。 ■HTMLヘッダー
■JavaScript(汎用化)
var photos = new Array();
//初期化
function slideInit() {
$('#display').crossSlide({ sleep: 3, fade: 2 }, photos);
}
//スライド開始
function startSlide() {
$("#photo").hide();
$("#display").fadeIn("slow");
$("#display").crossSlideRestart();
}
//スライド停止
function stopSlide() {
$("#display").crossSlideStop();
$("#display").fadeOut("slow");
}
//photos配列インデックスから写真表示
function showPhoto(index) {
stopSlide();
var photo = photos[index];
$("#photo").fadeOut("slow");
$("#photo").attr("src", photo);
$("#photo").fadeIn("slow");
}
「var photos」とグローバルにしてるのが、ちょっと危ないけどw
■JavaScript(実行) ※PHPで動的に写真配列をJavaScript出力
$(function() {
loadPhotos();
slideInit();
});
function loadPhotos() {
photos.push({src:'http://colinux/yoo_blog/images/photos/22.jpg', dir:'up'});
photos.push({src:'http://colinux/yoo_blog/images/photos/21.jpg', dir:'down'});
photos.push({src:'http://colinux/yoo_blog/images/photos/20.jpg', dir:'up'});
photos.push({src:'http://colinux/yoo_blog/images/photos/9.jpg', dir:'down'});
}
データ生成処理は、Ajaxで非同期通信してデータで処理するのもありかも。
■HTML(データ生成&実行) ※PHPで写真配列をアウトプット
スライドショーは、予め写真を先読みしておいて(このサイトではPHP)画像配列を設定しています。
$(id).crossSlide(オプション、画像配列)
これで指定したidのエレメントにスライドショーが表示される。 画像取得は、JSONで取得した方がJS側で応用がききそうですが、まぁ時間があれば。スライドショー停止はcrossSlideStop()、リスタートはcrossSlideRestart()を直接呼ぶだけ。 ※詳しくは、CrossSlide のページを参照
手動切替は、jQuery標準のfadeIn、fadeOutを利用。 写真を先読みして配列にしておいて、indexで切り替えるだけです。
$(id).attr("src", photos[index]);
SQLite3を直接使うには、フレームワーク「libsqlite3.bylib」の追加が必要。
Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.0.sdk/usr/lib/
フレームワークを追加したら.hにsqlite3をimportしておく。
#import "/usr/include/sqlite3.h"
.mに実装する。 ■データベース作成
if (sqlite3_open([[self dataFilePath] UTF8String], &database) != SQLITE_OK) {
sqlite3_close(database);
NSAssert(0, @"Faild to open database");
}
char *errorMsg;
NSString *createSQL = @"CREATE TABLE IF NOT EXISTS FIELDS (row INTEGER PRIMARY KEY, field_data TEXT);";
if (sqlite3_exec(database, [createSQL UTF8String], NULL, NULL, &errorMsg)) {
sqlite3_close(database);
NSAssert(0, @"Error creating table: %s", errorMsg);
}
NSString *query = @"SELECT row, field_data FROM FIELDS ORDER BY row";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &stmt, NULL) == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
int row = sqlite3_column_int(stmt, 0);
char *rowData = (char *)sqlite3_column_text(stmt, 1);
NSString *fieldName = [[NSString alloc]
initWithFormat:@"field%d", row];
NSString *fieldValue = [[NSString alloc] initWithUTF8String:rowData];
UITextField *field = [self valueForKey:fieldName];
field.text = fieldValue;
[fieldName release];
[fieldValue release];
}
sqlite3_finalize(stmt);
}
■データ挿入/更新
-(void)applicationWillTerminate:(NSNotification *)notification {
for (int i = 1; i <= 4; i++) {
NSString *fileName = [[NSString alloc]
initWithFormat:@"field%d", i];
UITextField *field = [self valueForKey:fileName];
[fileName release];
char *errorMsg;
char *update = "INSERT OR REPLACE INTO FIELDS (ROW, FIELD_DATA) VALUES(?, ?);";
sqlite3_stmt *stmt;
if (sqlite3_prepare(database, update, -1, &stmt, nil) == SQLITE_OK) {
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_text(stmt, 2, [field.text UTF8String], -1, NULL);
}
if (sqlite3_step(stmt) != SQLITE_DONE) {
NSAssert1(0, @"Error updating table: %s", errorMsg);
sqlite3_close(database);
}
}
}
SQLite3で利用できるメソッドやプロパティー等は、sqlite3.hを参考にすればよい。 基本的にデータベース作成、SQL実行なので、SQLがわかればそんな難しいことはないと思う。 ※sqlite3に関わる構文上で、option + クリック
ソース