非ドキュメンティドアプリケーションの、Spotlight対応について発表してきました。
前回のように緊張でガチガチになる事はありませんでしたが、用意した台本の通りには話せませんでした。
そして、Keynoteのスキルが足りなすぎる印象です。
非ドキュメンティドアプリケーションの、Spotlight対応について発表してきました。
前回のように緊張でガチガチになる事はありませんでしたが、用意した台本の通りには話せませんでした。
そして、Keynoteのスキルが足りなすぎる印象です。
UITableViewをタップすると、行がハイライト表示されます。背景は青く、文字は白く。
これはタップを検出したときに、UITableViewCellのsetHighlighted:animated:が呼び出された時の応答です。
ここに、若干の謎があります。
通常UITableViewCellはサブビューとして、UIImageViewやUILabelを持っています。この二つのクラスは、setHighlighted:というメソッドを持っているので、それが呼び出されているのでしょう。
しかし、UIViewならどんなクラスであってもサブビューとして追加できる事を考えると、これは妙です。
サブビューのクラスが上記二種類かどうか判定しているような、残念な実装かもしれせん。
少なくても、UIHighlightableなどというプロトコルも、見当たりません。
これは、独自のUIViewのサブクラスを用意した場合に、ハイライトに応答する方法が無いという事です。
解決方法
力技的には、UITableViewCellのsetHighlighted:animated:をオーバーライドしてしまう事を考えると思いますが、もっと良い方法が見つかりました。
実はUITableViewCellはサブビューをハイライトしようとした時は、該当するUIViewがsetHighlighted:を実装しているかどうか調べます。
実装していれば呼び出すという事です。
つまり、プロトコルのオプショナルメソッドや、非形式プロトコルと同様な扱いになっています。
以下は「MyView」と文字を表示するだけのUIViewのサブクラスですが、UITableViewCellに追加するとちゃんとハイライトします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | @interface MyView : UIView { BOOL highlighted; } @property (nonatomic, getter=isHighlighted) BOOL highlighted; @end @implementation MyView - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor clearColor]; } return self; } - (void)drawRect:(CGRect)rect { if (highlighted) { [[UIColor whiteColor] set]; } else { [[UIColor blackColor] set]; } [@"MyView" drawInRect:self.bounds withFont:[UIFont systemFontOfSize:[UIFont systemFontSize]]]; } @synthesize highlighted; - (void)setHighlighted:(BOOL)aHighlighted { if (highlighted != aHighlighted) { highlighted = aHighlighted; [self setNeedsDisplay]; } } @end |
最近のフレッツ光対応ルータには、フレッツ・スクエア ネクストに対応した物があります。
フレッツ・スクエアは専用のPPPセッションを張る必要あり、フレッツ・スクエアv6はクライアントPCに専用ツールを入れる必要があったのですが、フレッツ・スクエア ネクストは特に何もしなくても接続できるという、大変気持ち悪い^h^h^h^h^h便利な代物です。
試しにウチからアクセスしてみたところ、初回のアクセスにかなり時間が経過した後に繋がり、その後なぜかLAN内のサーバに繋がらなくなるという面白展開になりました。
この時は、LANのプライマリDNSサーバへのflets-east.jpの問い合わせがタイムアウトして、セカンダリDNSになっていたフレッツ光対応ルータが応答して繋がり、その後ローカルマシンはプライマリDNSが死んだと誤解して、LAN内のホスト名を引けなくなっている、という顛末でした。
少し実験してみた所、フレッツ・スクエア ネクストは下記のような流れで接続している事が判りました。
つまりLANにDNSサーバがある場合、flets-east.jpの問い合わせがあった時に、フレッツ光対応ルータへフォワードすれば良いのです。
あと、僕らのような人種にありがちなミスは、ローカルマシンやブラウザでIPv6を無効に設定してしまう事です(^_^;;
ウチはDebian lennyでBIND 9を動かしていますが、以下のような設定を入れたらフレッツ・スクエア ネクストに問題なく繋がるようになりました。
vi /etc/bind/named.conf.local
+zone "flets-east.jp" {
+ type forward;
+ forward only;
+ forwarders {
+ <フレッツ光対応ルータのIPアドレス>
+ };
+};前回のエントリで紹介したDebian lennyにおいてのDarwin Calendar Serverの構築ですが、iCalのアカウントの委任タブの編集ボタンから他のユーザにカレンダーを公開しようとしても、ユーザ名が見つからないという残念な制限がありました。
が、この設定を行えるクライアント向けツールに気がつきました。今頃(^_^;;
CalDAVClientLibraryです。
インストール
ツールはスクリプトでSubversionリポジトリで公開されています。
ターミナルを開いて、下記のコマンドで取得します。
svn checkout http://svn.macosforge.org/repository/calendarserver/CalDAVClientLibrary/trunk CalDAVClientLibrary
委任設定
cd CalDAVClientLibrary ./runshell.py --server=http://<サーバのホスト名>:8008 --user=<公開する側のユーザID> Password:<公開する側のユーザのパスワード> / > proxies -i Proxy > add -r New principal [q - quit]: /principals/users/<公開する相手のユーザID> Proxy > quit / > quit
シェルモードにはヘルプもあるので、先に読むと良いです。
CalDAVClientLibraryにはrunadmin.pyというコマンドもあって、こちらはサーバー上でaccounts.xmlの編集に使えます。
ドキュメント不足で手書き設定が判りにくいので、このツールで編集した方が良いと思います。
コマンドの使い方はヘルプですぐ判りますし、値の指定は対話形式なので問題ないと思います。
連休前に会社のリポジトリサーバをDebian lennyで組むというミッションがあったのですが、Debianの出来の良さが気に入ったので自宅のサーバも換装してみました。
怪しげなリポジトリの追加やコンパイル作業無しで必要なサービスが揃ったのは初めてで、Debianのパッケージの充実に感動している所です。
せっかくなので、今まで先送りしていて欲しかったCalDAVサーバを構築してみる事にしました。
lennyのリポジトリを確認した所、Darwin Calendar Serverが見つかりました。
DAViCalの方は、squeeze以降にしか無いようです。
先に使用感について書いておきますが、概ね良好です。
MacのiCalとiPhoneから、問題なく読み書きできています。
自宅サーバを持っている人で、MobileMeやGoogle Calendarにスケジュールを預けたくないけど、OTA同期したい人にはお勧めできます。
一点だけ問題があり、iCalからカレンダーを他のユーザに委任する事ができません。
Calendar Serverのメーリングリストを追ったところ、アカウントをLDAPで管理するように構成しなければ、この機能が動作しないという事でした。
自宅サーバに家族のアカウントを収容していて、相互にカレンダーの公開を考えている場合は面倒かもしれません。
僕は、LDAPは構成しませんでした。面倒すぎ(^_^;;
インストール
aptitude install calendarserver
ディスクの拡張属性を有効化
Calendar Serverは、情報の一部のファイルの拡張属性に収容します。
/var/spool/caldavを含むディスクを、user_xattr付でマウントするようにしてください(ext3の場合)。
下記は僕の所の例ですが、ファイルシステムでオプションが違うと思うので、調べてください。
vi /etc/fstab -/dev/md0 / ext3 errors=remount-ro 0 1 +/dev/md0 / ext3 errors=remount-ro,user_xattr 0 1
ルートディスクの場合はアンマウントできないので、再起動かもしれません。
設定ファイルを用意
インストール直後は設定ファイル不足で起動しないようになっています。
サンプルをコピーしてから始めると楽です。
cp /usr/share/doc/calendarserver/examples/accounts.xml /etc/caldavd cp /usr/share/doc/calendarserver/examples/sudoers.plist /etc/caldavd
ネットワーク設定
デフォルトだとlocalhostにバインドされているので、すべてのIPアドレスに応答するように設定します。
またSSLを有効にする場合は、証明書も指定します。
vi /etc/caldavd/caldavd.plist - <!-- <key>SSLPort</key> <integer>8443</integer> - --> <key>BindAddresses</key> - <array><string>localhost</string></array> + <array><string></string></array> <key>SSLCertificate</key> - <string>/etc/ssl/certs/ssl-cert-snakeoil.pem</string> + <string>/etc/ssl/certs/<証明書></string> <key>SSLPrivateKey</key> - <string>/etc/ssl/private/ssl-cert-snakeoil.key</string> + <string>/etc/ssl/private/<秘密鍵></string>
ユーザを追加
ユーザ情報はXMLファイルに書き込みます。
パスワードも生ですし、PAMを使ってLinuxのアカウントで認証する事もできません。
LDAPを使って構成すると幸せになれるみたいですが……
vi /etc/caldavd/accounts.xml + <user> + <uid>user1</uid> + <password>pass1</password> + <name>User 1</name> + <cuaddr>user1@foo.com</cuaddr> + </user> + <user> + <uid>user2</uid> + <password>pass2</password> + <name>User 2</name> + <cuaddr>user2@bar.com</cuaddr> + </user>
試していませんが、会議室予約のような共有カレンダーはLDAP無しでも設定可能であるようです。
accounts.xmlの下の方にそれっぽいサンプルがあるので、お試しください。
自動起動を設定
デフォルトで起動が禁止されています。
vi /etc/default/calendarserver -#start_calendarserver=yes +start_calendarserver=yes
後は起動するだけです。
/etc/init.d/apache2 calendarserver
クライアントからの接続
iCalやiPhoneからの接続は簡単です。
ホスト名とユーザ名とパスワードを入れれば、SSLの有無やカレンダーのパスを自動的に検出してくれます。
Windowsからの接続は試していませんが、Mozilla Sunbirdなら繋がると思います。たぶん(^_^;;
ニーズが解りにくいかもしれませんが、LAN内で通信するアプリケーションを実装する時に、通信プロトコルとしてHTTPを選択するケースが該当します。
ランダムなハイポートにどうやって接続するんだと疑問に思うかもしれませんが、ポート番号はZeroconfなどでブロードキャストします。
利点としてはIANAにポート番号を登録したり、衝突覚悟で使う必要が無いという事をあげておきます。
C#でWebサーバを実装する場合、真っ先に思いつくのはSystem.Net.HttpListenerクラスだと思いますが、このクラスは以下の理由で今回の目的には使えません。
そこで、アプリケーションに埋め込み可能なHTTPサーバの実装を探してみたんですが、C# WebServerという物がありました。
すべてC#で書かれていて、Apacheライセンスです。
試したところ、ポート番号0を指定すると例外を投げます。また、リスン開始後のポート番号を確認する方法もありません。
そこで、パッチを作成しました。
ポート番号に0を指定するとランダムなハイポートを割り当てます。
また、割り当てたポート番号を読み出せるプロパティを追加しました。
パッチはプロジェクトにポストしました。
マージしてくれると、メンテが楽なんですが……。

なぜなら、項目を何件でも表示できるから。
iPhone OS 2.xまでのUIActionSheetは、項目の件数が増えると画面上にはみ出してしまって、ボタンが押せなくなってしまう限界があり、件数に限界がありました。
iPhone OS 3.0のUIActionSheetはこの部分が改良されて、はみ出る時は自動的にリスト表示に切り替わります。
iPhone開発をやるようになってから、ずっと気になっていた事があります。下記のコードが、コンパイラを通らないのです。
1 2 3 4 5 6 7 8 | int main() { int n; switch (n) { case 0: int x = 1; break; } } |
int x = 1;の所で、「error: expected expression before ‘int’」となります。
気がついた当時は、gccのObjective-Cコンパイラに、変数スコープルールのバグがあるのではないかと考えて、次のような回避コードを書いていました。
1 2 3 4 5 6 7 8 9 10 | int main() { int n; switch (n) { case 0: { int x = 1; } break; } } |
所がある日、次のようなコードがコンパイラを通る事に気がつきました。一瞬、目の錯覚かと。
1 2 3 4 5 6 7 8 9 | int main() { int n; switch (n) { case 0: ; int x = 1; break; } } |
念のため、最初のコードをC++コンパイラに通してビルドできる事を確認、さらにCコンパイラを試したら、エラーになりました。つまりObjective-Cではなく、C言語レベルで引っかかっていたのです(例はintですが、見つけた時はNSStringか何かでした)。
ここまできてやっと、caseラベルが変数宣言文をエラーにしていると気がつきました。次のコードは、Cコンパイラでエラーになります。
1 2 3 4 | int main() { test: int x = 1; } |
納得いかない仕様ですが、C99とC++の変数宣言には非互換があるという事になります。JISを当たってみましたが、それらしき制約を見つけられないです…
MailMeの変なキャラのアニメーションの実装について発表してきました。
発表後のQAで、Core Animationを誤用していた事が発覚。フレームアニメをやりたい時は、contentsプロパティを書き換えれば良かったんですよ。なるみさん、ありがとうございます。
UIAlertViewの表示中に端末を回転すると、いったんアラートが消えて回転後に再表示されます。
単に見せ方がこうなっているだけだと考えていたのですが、再表示のタイミングで次のようなイベントが発生しています。
つまり、一度アラートは閉じています。
ここで問題は、cancelButtonIndexに大して何らかの処理を書いてしまっている場合です。
回転をサポートする場合はもちろんですが、cancelButtonIndexは処理無しにしておくべきだと思われます。