Clouder::Blogger

UITableViewのreorder時のドラッグ開始/終了のイベントを取得する

UITableViewの並び替えでセルを「ドラッグしたとき」と「ドロップしたとき」のイベントを取る必要があって調べてみたのですが、公式にはそのようなイベントハンドラがないようです(プライベートAPIにはあるとかないとか)。

そこで調べてみたら、UITableViewCellにUILongPressGuestureRecognizerを仕込んで、reorderのコントロールを掴んだ時と離した時をUITableViewにdelegateしてやるとそれっぽいことができるというのを知ったのでやってみました。

参考にしたのは、StackOverFlowの「How to get notified of UITableViewCell move start and end」という記事

先にやり方をまとめ

  • UITableViewCellのサブクラスを作って、UILongPressGuestureRecognizerをreorderのコントロールに仕込む
  • 上で仕込んだイベントが発生したら、UITableViewにその状態をdelegateする
  • UITableView側でその状態を保持するインスタンス変数を用意して、そいつに状態を保持させて、ドラッグ中なのかどうかを判定する

実装

実装は以下のようになっています。最終的にはMyTableViewControllerのisDraggingTableViewCellのYES/NOでドラッグ中かどうかが判定できます。

MyTableViewController.h

#import <UIKit/UIKit.h>

// MyTableViewCellDelegate のプロトコルを設定
@interface MyTableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource, MyTableViewCellDelegate>

@end

MyTableViewController.m

@interface MyTableViewController ()

// ドラッグ状態保持用のインスタンス変数を定義
@property (nonatomic, assign, getter = isDraggingTableViewCell) BOOL draggingTableViewCell;

@end

@implementation

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // ...

    if (cell == nil) {
        cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.delegate = self; // delegateを設定
    }

    // ...
}

// reorderさせるためにこのdelegateメソッドを定義
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
    // do anything
}

#pragma mark - MyTableViewCellDelegate

// ドラッグ状態を取得するためのdelegateメソッドを定義
- (void)tableViewCell:(MyTableViewCell *)cell dragging:(BOOL)dragging {
    // 渡ってきたドラッグ状態を保持する
    _draggingTableViewCell = dragging;
}

@end

MyTableViewCell.h

#import "MyTableViewCell.h"

@class MyTableViewCell;

@protocol MyTableViewCellDelegate <NSObject>

// delegateメソッドの定義
- (void)tableViewCell:(MyTableViewCell *)cell dragging:(BOOL)dragging;

@end

@interface MyTableViewCell : UITableViewCell

@property (unsafe_unretained) id<MyTableViewCellDelegate> delegate;

@end

MyTableViewCell.m

#import "MyTableViewCell.h"

@implementation MyTableViewCell

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    [super setEditing:editing animated:animated];

    if (editing) {
        // subviewsの中からreorderのコントロールをみつけて、UILongPressGestureRecognizerを設定
        for (UIView * view in self.subviews) {
            NSString *viewClassName = NSStringFromClass([view class]);
            if ([viewClassName isEqualToString:@"UITableViewCellReorderControl"]) {                
                if (view.gestureRecognizers.count == 0) {
                    UILongPressGestureRecognizer *gesture =
                        [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
                    gesture.cancelsTouchesInView = NO;
                    gesture.minimumPressDuration = 0.15;
                    [view addGestureRecognizer:gesture];
                }
            }
        }
    }
}

// ドラッグ状態をdelegateに送信
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        [self.delegate tableViewCell:self dragging:YES];
    } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
        [self.delegate tableViewCell:self dragging:NO];
    }
}

@end

Article Data Management on Octopress

Octopressは記事の管理がちょっとめんどくさい。

基本的にOctopressのインストールされたサーバにしか記事のマスターデータがないので、更新するときにはそのサーバにログインしなければならない。最終的にgithubで一元管理してるにもかかわらず。。。

これについてはどうするのが良いのだろうか。

マスターデータ自体をgitで管理するか、 もしくはDropboxに置くという手もありそう。

Migrated to Octopress

年々ブログを書かなくなってきたので、何か変化を加えて書くモチベーションを上げようと思いブログのシステムを変えてみました。

今回導入したのはOctopressという最近(?)流行っているブログのシステムです。

Octopress

Octopressとは

OctopressはRubyで書かれたブログ管理フレームワークで、GitHub Pagesを使って記事を管理することができ、jekyllという静的サイトジェネレーターを使って作られています。

Movable TypeのようなWebのインターフェイスはなく、記事はmarkdownなどの形式で書いたファイルをgithubにpushすることで更新するというものです。

なので、ちょっと非エンジニアの人には使いづらいかもしれませんが、個人的にはとても楽に更新できそうだなぁと感じました。

MovableTypeからの移行

今回これが1番面倒だった…。

いままでMovable Typeで書いた記事はやっぱり移行したかったので、Movable Typeからoctopressへの記事の移行の方法をいろいろ調べてみました。

移行方法はいくつかみつけたのですが、今回はjekyllにおまけ程度ではあるのですが移行用プログラムが同梱されていることがわかったのでそれを使うことに決めました。

とはいえ、いろいろと修正が必要だったので、ゴリゴリ修正をしてやっとこさ移行してやりました。

この辺についてはまた後日まとめたいと思います。

ということで、今後はもうちょっと更新したいなと思ってる所存です。

プレゼンをmarkdownで書いたらええやん

markdown2impressってのを書きました。

https://github.com/yoshiki/markdown2impress

これはmarkdownで書いた文章をimpress.jsに対応したHTMLに変換するものです。impress.jsっていうのは、CSS3をつかって文字とかをぎゅんぎゅん動かすことのできるプレゼンフレームワークです(prezi.comにインスパイアされて作ったらしい)。

impress.jsを使ってプレゼンをゼロから作ってもいいんですが、HTMLをいちいち書くのめんどかったのでmarkdown形式で書いたものをそれ用に変換してくれたらいいなと思って書いてみました。基本的にはmarkdownで書いた文章の1つのセクションが1つのスライドになるようになっていて、githubのリポジトリのREADME.mdをこのプログラムで変換するとプレゼンに変換されます。

使い方は簡単で、
% markdown2impress.pl README.md
とやるだけでOK。

こうするとカレントディレクトリにindex.html、js/impress.js、css/impress.cssの3つのファイルが生成されます。

もしCSSを変えたければcss/impress.cssを直接編集してやればいいです。ちなみに生成されるcss/impress.cssは元のimpress.jsに含まれるcssをちょっとだけ加工してあります。

markdown2impressは、なにも指定せずにmarkdownを変換してやるとスライドの位置(縦横幅や位置)を勝手に計算してくれるのですが、以下のようにセクション内にHTMLのコメント形式でimpress.jsが理解できる data-x や data-y などのアトリビュートを書くことでスライドの位置を変更することもできます。
<!-- data-x="1000" -->
もちろん data-xやdata-y以外のdata-z、data-rotate、data-scaleなども使えます。

ちなみにコマンドラインのオプションとして以下が指定できるようになっています。
  • –width
    スライドの横幅(default: 1200)
  • –height
    スライドの縦幅(default: 800)
  • –column
    スライドの横の数(default: 5)
  • –outputdir
    出力ディレクトリ(default: current dir)

興味があったら使ってみてください。

※生成されるimpress.jsが最新ではない場合があるので、その場合は本家のjsとcssの最新版をコピってくれれば動くはずです。

TCPセッションモニタ「miruo」が便利だった

なんかネットワークの問題を調べていたらtcpのリトライが発生している模様。
でもイメージトークしててもしょうがないので本当にリトライが発生しているのかtcpdumpで調べてみたんだけど、なにせ情報量が多過ぎる。

そんでtcpdumpの情報の解析方法を調べているうちにmiruoってのを見つけて、使ってみたら便利だったのでご紹介。

miruoはDSAS開発者の部屋の「高負荷サイトのボトルネックを見つけるには」で使い方とかも含めて紹介されています。

ダウンロードは↓からどうぞ。
TCPセッションモニタ「miruo」

これは便利だ。

Notify My Androidでim.kayac.comの通知を受け取る方法

先日メインで使ってるDoCoMoのフィーチャーフォンをGalaxy Nexusに機種変したのですが、それまでiPhoneで受け取っていたim.kayac.comの通知をAndroidでも受け取りたくなったのでどういう方法があるのか調べてみました。

im.kayac.comはGoogle Talk(Jabber)でも通知ができるので、やるとしたらその方向しかないんだろうなぁと思いつつググってみると案の定Google Talkのアプリで通知を受け取るっていう記事がいくつかみつかりました。

でも、なんか「素のGoogle Talkで通知を確認するのはなんかクールじゃない!」

と思っちゃったので、それ以外の方法ないのかといろいろ探してみたらみつかりました。
(みつかったというかアプリを利用して足りない部分は自分で作ったんですが…)

簡単に方法を説明しちゃうと、自前でXMPPのクライアントを立てて、im.kayac.comからの通知を「Notify My Android」っていうNotify専用のアプリに流すという方法です。

簡単に書くとこんな感じ。
[im.kayac.com] -> [XMPP client] -> [Notify My Android]


ちなみにNotify My Androidは有料です。
有料だけど、今回この実装をやってみたら買う価値はあるかなと。
缶ビール2本我慢すれば買える値段ですし(※お酒はあまりのみませんが!)。

ってことで手順を説明します。

  • AndroidマーケットでNotify My Androidを購入し、Notify My Androidのサイトでアカウント登録とAPI Keyの発行を済ませておく。
  • 以下のスクリプトを使って、im.kayac.comのGoogle Talkのアクティベーションします。
    実行にはAnyEvent::XMPPのインストールと、pitを使ってgtalkって名前で設定(usernameとpassword)を追加しておく必要があります。

    実行は引数にim.kayac.comで発行されたアクティベーションコードを渡すだけ。
    % ./register.pl アクティベーションコード
    session ready at ./register.pl line 27.
    connected at ./register.pl line 31.message from api@im.kayac.com/123456: Your jabber account has been activated! at ./register.pl line 36.
  • あとは以下のスクリプトを起動するだけです。
    実行にはWebService::NotifyMyAndroidのインストールと、pitを使ってnma(Notify My Androidの略)って名前で設定(apikey)を追加しておく必要があります。

    実行は普通に以下な感じ。
    % ./server.pl &
    ちなみに、
    request to https://nma.usk.bz/publicapi/verify?apikey=YOUR_API_KEY failed at ./server.pl line 19
    とかでる人はLWP::Protocol::httpsが入ってないからだと思います。
これでサーバさえちゃんと動いてたら、Notify My Androidに通知が来るはずです。 興味ある方がいたらやってみてください。

Plack on SL4A in Yokohama.pm #8

Yokohama.pm #8で「Plack on SL4A」というタイトルでLTしてきました。

実はこの話は元ネタがあって、去年のperl.krのAdventカレンダーで紹介されていたものを実践してみたものになっています。

本当は、これを元にAmon2などのフレームワークを動かしたかったのですが、XS依存の問題があってそのXSモジュールをインストールしようと頑張ったのですが、どうしてもインストールできなかったため今回のPlackのインストールまでの話になってしまいました。

もうちょっとだけSL4AでXSを動かせるか試してみたいと思っていて、進捗があったらここでお知らせしたいと思います。

ちなみにSL4AのXSのmakeできない問題は、すでにSL4AのIssueに上っていて、このIssueの最後の方のポストでgithubのPerlのブランチを切って書き換えたやつでビルドできたという話が出ているのですが、ARMのクロスコンパイル環境を用意してやってみたもののうまくいなかいんですよねぇ…。

Android開発にも役立つEmacsの補完プラグイン - Ajc-java-complete

最近Androidの開発をしていまして、例にもれずEclipseが体に合わないため(というかEmacsが好きなため?)、Emacsで開発をしています。

しかし、いままでJavaで本格的に開発したことなかったのでEmacsにおけるJavaの開発環境がまったく整備されていないので、EmacsでもうちょっとJavaの開発がしやすくする便利なモードないのかなと調べてみました。

いろいろ調べてみたら、JDEE − Java Development Environment for Emacsってのがあったのですがなんかごてごてしていて、そんなモリモリの機能いらないんだよなぁと思っていたら「ajc-java-complete」っていうのをみつけました。

ajc-java-completeは名前の通りauto-completeやyasnippetと連携して補完することをメインにしたものになっていて、今も開発が継続しているようでなかなかよさげ。ということでさっそくインストールしてみました。

  • auto-complete のインストール

    http://cx4a.org/software/auto-complete/ からダウンロードしてきて、この辺を参考にインストール。

    .emacsに以下のような設定をしておくとよいでしょう。
    (require 'auto-complete)
    (global-auto-complete-mode t)
    (define-key ac-complete-mode-map "\C-n" 'ac-next)
    (define-key ac-complete-mode-map "\C-p" 'ac-previous)

  • yasnippet のインストール

    http://code.google.com/p/yasnippet/ からダウンロードしてきて、この辺を参考にインストール。

    .emacsに以下のような設定をしておくとよいでしょう($PATH_TO_SNIPPETはsnippetをインストールしたディレクトリね)。
    (require 'yasnippet-bundle)
    (yas/initialize)
    (yas/load-directory "$PATH_TO_SNIPPET/snippets")

  • ajc-java-complete のインストール

    https://github.com/jixiuf/ajc-java-completeからcloneしてきて、この辺を参考にajc-java-complete自体をインストール。

    なお、ajc-java-completeは、auto-completeに付属するpopup.elにパッチをあてないといけないらしく、ajc-java-completeのパッケージに付随するpopup.elを、すでにインストールしたauto-completeのpopup.elと入れ替える必要があります。

    あと、Javaのクラスを補完させるためにタグファイルを生成する必要があります。そのためのJavaのプログラムが添付されているので、それを以下のようにjavacでコンパイルしてから実行する必要があります。
    % javac Tags.java
    % env CLASSPATH=$PATH_TO_ANDROID_HOME/platforms/android-11/android.jar:/System/Library/Frameworks/JavaVM.framework/Classes/classes.jar:. java Tags

    この例ではAndroid開発もしたいのでAndroidのSDKのパッケージも含めています。
    (なお、classes.jarのパスはMac OS X用なので適宜自分の環境に合わせて変えてください)

    んで、最後に.emacsに以下のように書きます。
    (require 'ajc-java-complete-config)
    (add-hook 'java-mode-hook 'ajc-java-complete-mode)

以上でインストールが完了しました。

あとはEmacsを起動してFoo.javaとかやってみると補完がきくようになります。

補完以外の機能で言うと、「C-c i」でインポートしていないものを自動でインポートして(ファイルの上部に「import com.example.foo;」って挿入して)くれたり、クラス名のところにカーソルをもってきて「C-c m」とやるとそのクラスをインポートしてくれたりするものがあります。(ただし、なぜかextendsの後に書いたクラスはインポートしてくれず…)

ちなみに、さきほど補完用にタグファイルを生成したのですが、あれがけっこうな容量になっていて、それを起動時に読み込むため、初回のみですがちょっと時間がかかるようです。これ直したいとajc-java-completeをいまいじってるところです。なにかいい案があったらまたお知らせします。(っていうかpull requestしたい)

それともう1つ、自分はMac OS XのEmacsでインポート機能を使うとダイアログがウィンドウで出てうざい(のとEmacs自体が固まったりする)ので以下のパッチをあててます。
--- old/ajc-java-complete.el	2011-07-08 00:00:28.000000000 +0900
+++ new/ajc-java-complete.el	2011-07-08 00:00:12.000000000 +0900
@@ -938,7 +938,8 @@
 before that it will use y-or-n-p ask user to confirm "
   (let ((import-class-buffer "*ajc-import-java-class*")
         (import-class-window) (user-confirmed-class-items-list)
-        (java-buffer (current-buffer))(java-window))
+        (java-buffer (current-buffer))(java-window)
+        (last-nonmenu-event t))
     (setq case-fold-search nil)
     (if (and import-class-items-list (> (length import-class-items-list) 0))
         (progn

GNU Parallel いいかもね

GNU Parallelがすごすぎて生きるのがつらいを見て使ってみました。

そもそも複数のサーバのaccess_logをtail -fで一括で見たかったからです。

GNU Parallelからダウンロードして
% ./configure
% make
% make install
でインストールは終了。簡単!

Perlで書かれてるんでコンパイルとか必要ないっす。

インストールしたら目的のサーバ群に対して tail -f /path/to/access_log を発行するだけ。
% parallel -u ssh {} 'tail -f /path/to/access_log' :::: /path/to/server_list.txt
こうするとserver_list.txtに列挙されてるサーバに対してtail -f /path/to/access_logが実行されます。

-u っていうオプションを付けないと出力がバッファリングされちゃうので、tail -f するときは -u を付けないとだめです。

ちょっと引数にクセがあるけど慣れですね。
マニュアルは man parallel で見られます。

他にもこういうのあるけど、Perlで書かれてて使い易かったのでしばらく使ってみようと思います。