Cocoa Break Logo Top Soft Develop  脱力空間 Logo
Apple Web Badge
made by mi

概要 翻訳 ソース リンク がらくた

概要 Examples ADC Samples 3rd Parties etc CBOriginals

Fortune

以下は、Mac OS X 10.4.11 上の Xcode 2.5 で説明しています。BlankWidgetで解説したことは繰り返しませんので、そちらを参照してください。

目次:
1 Read Me.rtf の日本語訳
2 ウィジェット
3 プラグイン
4 ウィジェットのファイル構成
5 Fortune.html
6 Fortune.css
7 Fortune.js
8 まとめ

1 Read Me.rtf の日本語訳

ウェジェットプラグインを使う進んだウィジェットです。Cocoa と JavaScript の間でデータを渡すことを可能にする、Coroa と JavaScript を橋渡しするウィジェットプラグインを含んでいます。

注意:最初に /SimplePlugin/ で見つかる SimplePlugin をコンパイルし、結果のファイルである SimplePlugin.widgetplugin を Fortune ウィジェットパッケージのルートに置きます。それから、パッケージがウィジェットとして有効になるように、.wdgt 拡張子を追加します。

2 ウィジェット

上の Read Me.rtf の説明どおりにウィジェットを作り、それからインストールすると以下のようになります。背景画像は「フォーチューンクッキー(おみくじ入りクッキー)」を意識したものだと思われます。

中央の部分には「Click here to obtain a fortune.(おみくじを得るには、ここをクリックしてください。)」とあります。クリックすれば、ランダムでおみくじが表示されます。

上の例では、真ん中の文字列が「you need not worry about your fortune.(自身の運命について心配する必要はない。)」という風に変わります。この新しい文字列はプラグインを使って、Objective-C クラスから取得されたものです。もちろん、JavaScript にも文字列配列と乱数があるので、同様の結果を作り出すことは簡単です。ここでは、プラグイン使用の例として、Objective-C クラスから文字列を取得しています。

3 プラグイン

ウィジェット本体について説明する前に、プラグインについて説明します。プラグインがどのように動作するのかを先に説明したほうが、それを JavaScript から呼び出した時の動作が理解しやすいと思われるからです。

Xcode 2.5 では、Dashboard プラグインというようなプロジェクトテンプレートはありません。「Cocoa Bundle」を新規プロジェクトとして作成し、それを修正することになります。

このサンプル内に「SimplePlugin」というフォルダがありますが、このなかに「SimplePlugin.xcodeproj」というプロジェクトファイルがあります。これがプラグインプロジェクトです。これを Xcode で開きます。

3.1 ファイル構成

SimplePlugin.xcodeproj でのファイル構成は以下のようになっています。

ここで、MyPluginClass.h と .m がプラグインを定義しているファイルになります。WebKit フレームワークとリンクされている点に注意してください。

3.2 ヘッダとグローバル変数

まず MyPluginClass.h を見てみます。これは以下のようになっています。

MyPluginClass.h
#import #import @interface MyPluginClass : NSObject { short sayingCount; } @end

インスタンス変数が宣言されているだけです。このインスタンス変数は、出力したおみくじの番号を保存しておくためのものです。

ヘッダとは別に、MyPluginClass.m の最初に変数が定義されています。これも見てみましょう。

MyPluginClass.m 冒頭
/*********************************************/ // 引用文の数と引用文自体。 // これらは下でランダムに選ばれ、Fortune ウィジェットに返される /*********************************************/ enum { NUM_QUOTES = 8 }; static NSString *quotes[NUM_QUOTES] = { @"You will be awarded some great honor.", @"You are soon going to change your present line of work.", @"You will have gold pieces by the bushel.", @"You will be fortunate in the opportunities presented to you.", @"Someone is speaking well of you.", @"Be direct, usually one can accomplish more that way.", @"You need not worry about your future.", @"Generosity and perfection are your everlasting goals.", };

定数 NUM_QUOTES は、引用文の数で、つぎの quotes[] 配列が引用文自体を定義しています。

3.3 初期化

つぎに初期化を行うメソッドを見てみます。『Dashboard プログラミングトピック』の「ウィジェットプラグインの作成」にあるように、これは必ず実装しなければならないメソッドです。プラグインが読み込まれたときに Dashboard がこのメソッドを実行します。

MyPluginClass.m > initWithWebView:
// このメソッドは、ウィジェットのウェブビューが最初に初期化され、 //ウィジェットプラグインが最初に読み込まれる時に呼び出される -(id)initWithWebView:(WebView*)w { //NSLog(@"Entering -initWithWebView:%@", w); self = [super init]; srand(time(NULL)); return self; }

まず、コメントアウトされていますが、デバッグ用のログ出力があります。デバッグ時や動作を調べたい時には、コメントでなくします。次にスーパークラスの初期化をし、引用文をランダムに選ぶ時に使う乱数の初期化を行っています。

time は C 言語標準ライブラリの time.h で宣言されている関数で、現在時刻を time_t 値(10.4.11 では long)として返します。srand も標準ライブラリ関数で stdlib.h で宣言されているもので、同じライブラリ関数 rand() を使うときに返される乱数を初期化します。ここで引数には unsigned の種をとり、それを使って初期化を行います。種を変えれば、違う乱数が生成されます。

ちなみに、C 言語標準ライブラリの乱数は生成が速いですが、筆者の学習以後に大幅に改装されないないかぎり、出てくる乱数はあまりランダムではありません。ここでは単に引用文を選ぶだけなので、この乱数で十分ですが、ランダムであることが重要な場合(シミュレーションや統計)には、使わないように気をつけてください。逆に適当に速く乱数がほしい場合は C 言語ライブラリの乱数を使います。

さて、初期化と対応して、割り当て解除メソッドも見てみましょう。

MyPluginClass.m > dealloc
-(void)dealloc { [super dealloc]; }

このクラスでは、インスタンス変数としてオブジェクトを使用したり、保持しているものがないので、スーパークラスの実装を呼んでいるだけです。

3.4 仲介オブジェクト

『Dashboard プログラミングトピック』の「ウィジェットプラグインの作成」にあるように、WebScripting 簡易プロトコルとは別に、JavaScript と Objective-C 言語プラグインとの橋渡しをするオブジェクトをバインドする必要があります。

MyPluginClass.m > windowScriptObjectAvailable:
// このメソッドは、Obj-C と JavaScritp の間を橋渡しするために使うオブジェクトを与える // JavaScript 側でそれを参照する名前を与えるには setValue:forKey: を使う -(void)windowScriptObjectAvailable:(WebScriptObject*)wso { //NSLog(@"windowScriptObjectAvailable"); [wso setValue:self forKey:@"FortunePlugin"]; }

ここでは、プログラミングトピックで最小限含めたほうがいいとされている1行があるだけです。これにより、JavaScript 内で「FortunePlugin」という名前で、このオブジェクトが参照できるようになります。

3.5 WebScripting 簡易プロトコル

プラグインを実際に使うときには、当然なことながら、プラグイン独自の何らかの作業をするメソッドを含んでいるはずです。それらのメソッドを JavaScript 側から便利に呼び出せるようにするため、WebScripting 簡易プロトコルを実装します。これを実装しなくても、上の橋渡しオブジェクト内でキーをかいしてメソッドを利用できますが、デフォルトの名前になります。使われる名前を自分で設定したい場合、以下のメソッドを実装することになります。

MyPluginClass.m > +webScriptNameForSelector:
// このメソッドは JavaScript 内に橋渡しされる時に // 通常バラバラになるメソッドに対して親しみやすい名前を提供できる +(NSString*)webScriptNameForSelector:(SEL)aSel { NSString *retval = nil; //NSLog(@"webScriptNameForSelector"); if (aSel == @selector(getFortune)) { retval = @"getFortune"; } else if (aSel == @selector(logMessage:)) { retval = @"logMessage"; } else { NSLog(@"\tunknown selector"); } return retval; }

クラスメソッドであることに注意してください。渡されたセレクタに対して、JavaScript 内で使える名前を返します。ここでは、getFortunelogMessage: の場合、それらを返しています。このクラスの場合、メソッド名が単純なので、じつはこれを実装しなくても、たいした違いはなかったりします。くわしいことは、WebScripting リファレンスのこのメソッドの説明を見ればわかりますが、getFortune のデフォルト名はそのまま getFortune で、logMessage: の場合は logMessage_ となります。

この WebScripting プロトコルには、メソッドセレクタではなく、属性を呼ぶためのキーを変更したりするメソッドや、特定のメソッドを JavaScript から隠したりするためのメソッドなども宣言されています。

3.6 プラグイン固有のメソッド

プラグイン特有の作業を行うメソッドは、getFortunelogMessage: です。

MyPluginClass.m > getFortune
// ウィジェットにおみくじを返す - (NSString *) getFortune { short temp; temp = rand() % NUM_QUOTES; if(temp == sayingCount) { temp = (temp + 1) % NUM_QUOTES; } sayingCount = temp; return quotes[sayingCount]; }

まず最初に乱数を取得して、それを引用文の数で割っています。つぎにインスタンス変数とその乱数を比較します。同じなら、直前と同じものを返すので、別のものを返すようにしています。それから返す予定の番号を保存しておき、配列から引用文を返しています。

MyPluginClass.m > logMessage:
// JavaScript から渡されたメッセージをコンソールに送る // これは JavaScript 文字列から NSString* への変換を実演する // 実際には、JavaScript から alert() を呼び出すだけでいいだろう // これにより結局 Dashboard がコンソールにそれを送る - (void) logMessage:(NSString *)str { NSLog(@"JavaScript says: %@", str); }

単に NSLog でコンソールにログ出力しているだけです。コメントにもあるとおり、このメソッドは必要ではありません。JavaScript の文字列が Objective-C メソッドに NSString* として変換して渡されていることを確認するためのものです。

さて、これでプラグインの説明はおしまいです。非常に単純な構造になっているのがわかったと思います。これをビルドすれば、SimplePlugin.widgetplugin が作られます。Read Me.rtf にあるように、これをウィジェットパッケージのルートに置くことになります。また、ウィジェット自体の Info.plist に、Plugin キーとしてこれが指定されていることに注意してください。

4 ウィジェットのファイル構成

さて、プラグインをビルドしてコピーした後のウィジェットのファイル構成は以下のようになります。

Default.png は、ウィジェットの文字を除く背景が含まれている画像です。Icon.png は、アイコンです。主要 HTML ファイルは、Fortune.html です。CSS ファイルは Fortune.css です。スクリプトファイルは、Fortune.js です。

このサンプルには Apple クラスが含まれていませんが、アニメーションなどの Apple クラスが使われています。このため、v10.4.3 より前だと動作しないので気をつけてください。

5 Fortune.html

それでは、主要 HTML ファイルを見てみます。

Fortune.html
<html> <head> <!-- このウィジェットの CSS ファイル --> <style type="text/css"> @import "Fortune.css"; </style> <!-- このウィジェットの JavaScript --> <script type='text/javascript' src='Fortune.js' charset='utf-8'/> </head> <body onclick="next()"> <!-- このウィジェットのどこでもクリックすれば次のおみくじに --> <img src="Default.png"> <!-- おみくじクッキーと紙の画像 --> <div id="quote">Click here to obtain a fortune.</div> <!-- 基本の場所とりテキスト --> </body> </html>

コメントにあるように、body 要素に onclick ハンドラが設定されていることに注意してください。単純なので、あとはわかると思います。

6 Fortune.css

スタイルシートは、特別な設定は何もありません。気になる方は、サンプル内のファイルを見てください。

7 Fortune.js

このサンプルの中心となるのがこのファイルです。ここでプラグインのメソッドを使用しています。プラグインの読み込みや初期化は、Info.plist に指定されていたので、ウィジェット読み込み時に、ウィジェットを表示する際のウェブビューの初期化の一環として実行されることに気をつけてください。HTML や JavaScript ファイル時で初期化をする必要はありません。プラグイン自体で読み込まれた時の初期化メソッド等を実装するだけです。

7.1 グローバル変数

このファイル内では関数外コードがなく、変数が 1 つだけ関数外で宣言されています。これはさまざまな関数で使われるアニメーションオブジェクトを保存しておくためのものです。

Fortune.js > グローバル変数
var animation = {duration:0, starttime:0, to:1.0, now:1.0, from:0.0, firstElement:null, timer:null};

7.2 クリックハンドラ

このウィジェットにはよくある onload ハンドラがなく、かわりに onclick ハンドラが設定されていました。そのため、単にウィジェットが読み込まれても、JavaScript が実行されるわけではありません。特別な初期化は行われません。ウィジェットのどこかがクリックされたとき、初めて next() が呼び出されることになります。

Fortune.js > next()
// 現在のおみくじから次への遷移を実行する function next() { hideContent(); // 現在のおみくじをフェードアウト setTimeout("swap();",500); // 新しいおみくじと切替 setTimeout("showContent();",550); // 新しいおみくじをフェードイン }

単に他の関数を呼び出しているだけです。それらの関数を見ていきます。まず hideContent() です。

Fortune.js > hideContent()
function hideContent() { if (animation.timer != null)// アニメーション実行中なら停止 { clearInterval (animation.timer); animation.timer = null; } // 文字の不透明度を 0 にするアニメーションを実行 var starttime = (new Date).getTime() - 13; animation.duration = 500; animation.starttime = starttime; animation.firstElement = document.getElementById ('quote'); animation.timer = setInterval ("animate();", 13); animation.from = animation.now; animation.to = 0.0; animate(); }

アニメーションについては他のサンプルで説明していますので、細かい所は省きます。後の showContent() がファイル内では先になっていてコメントがくわしいので、気になる方はそちらを見てください。id が「quote」である要素の不透明度を 0 にするようなアニメーションを実行します。すなわち、表示されている文字列がじょじょに見えなくなります。

つぎに、swap() です。

Fortune.js > swap()
// 現在のおみくじを新しいものと交換 // 新しいおみくじを得るために Obj-C/JavaScript ウィジェットプラグインを使用 function swap() { if (FortunePlugin) { // プラグインがあるかチェック var line = FortunePlugin.getFortune(); // ウィジェットプラグインからおみくじを取得 document.getElementById("quote").innerHTML = line; // 新しいおみくじを入れる } else { // プラグインがなければアラート alert("Widget plugin not loaded."); } }

プラグインがあるかチェックして、それからプラグインから引用文を取得します。プラグインのメソッドであるものの、JavaScript 関数のように普通に使われていることに注意してください。Objective-C メソッドの利用はごく簡単です。

「quote」要素内に新しいおみくじが入れられたので、次はそれをフェードインして表示するだけです。

Fortune.js > showContent()
function showContent() { if (animation.timer != null) // 値がそのままの場合、アニメーションタイマー値をリセット { clearInterval (animation.timer); animation.timer = null; } var starttime = (new Date).getTime() - 13; // 1 フレーム戻って設定 animation.duration = 500; // ms 単位のアニメーション時間 animation.starttime = starttime; // 開始時を指定 animation.firstElement = document.getElementById ('quote'); // フェードする要素を指定 animation.timer = setInterval ("animate();", 13); // アニメーション関数を設定 animation.from = animation.now; // 開始する不透明度(必ずしも 0 でない) animation.to = 1.0; // 最終的な不透明度 animate(); //アニメーションを開始 }

簡単なので、くわしい説明はしません。これで next() の内容がわかったと思います。各フレームごとのアニメーションを実行するメソッドも定義されていますが、他サンプル等と似たものなので、ここでは説明しません。

8 まとめ

プラグインを示すサンプルなので、本体自体はとても単純です。ウィジェットからのプラグイン使用も、またプラグインの作成も非常に簡単なものであることがわかったと思います。

プラグインには、logMessage: というメソッドが定義されていましたが、残念ながら、このファイル内では使われていません。実験してみたい方は、適当な部分にログ出力を入れて、ためしてみるといいでしょう。


管理人:神吉 秀典 E-mail:puer@ba.wakwak.com