cocoa*life
Lokka で Google Analytics のトラッキングコードを簡単に挿入するための Helper Plugin を書いてみた
- 2010-10-11 (Mon)
- Ruby
Lokka は komagata さんが開発されている、Ruby で書かれた CMS です(旧名 Pyhä)。
打倒 WordPress を目指しているそうです。
komagata’s lokka at master – GitHub
今後使用しようと思っていて、 Plugin も書けるということで、ちょっと試してみました。
簡単に Google Analytics のトラッキングコードが挿入できる Plugin です。
$LOKKA_ROOT/plugin フォルダに hello というサンプルがあるので、それを見ながら書いてみます。
komagata さんのエントリによれば、 Sinatra Extensions のサブセットだということ。
Pyhaの大体の仕組み – komagata [p0t]
Sinatra: Writing Extensions
$LOKKA_ROOT/plugin/ に google_analytics/lib/google_analytics.rb というファイルを作りました。
そして以下を記述。
module Lokka
module Helpers
def analytics(uid)
haml(<<-script, {})
%script{ :type => "text/javascript" }
:cdata
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '#{uid}']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
script
end
end
end
モジュール名をどうしたらいいのかわからなかったので、 $LOKKA_ROOT/lib/lokka/helpers.rb を参考に Lokka::Helpers に入れてしまいました。
Rails の感覚だと後は、サーバを再起動すれば動きそうですが、このままでは動きません。
おかしいなぁと思って、プロジェクトを grep してみると、 init.rb にこんな行が。
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'plugin', 'hello', 'lib'))
これは同じように入れないといけないんだろうかと、こう書いてみる。
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'plugin', 'google_analytics', 'lib'))
そしてもう一カ所、 $LOKKA_ROOT/lib/lokka.rb を見てみると
require 'lokka/hello'
なんて行があるので、同様に
require 'lokka/google_analytics'
と書いたところ、無事に動きました。
- Comments: 4
- Trackbacks: 1
Redmine を nginx + Unicorn で動かしてみる
- 2010-10-05 (Tue)
- Ruby on Rails
今後のために、一度 Webサーバである nginx と Railsサーバ?の Unicorn を使って Redmine を動作させてみることにしました。
すでに Redmine は Apache + Passenger で動いています。
環境
- Ubuntu Server x86_64 10.04
Rack のアンインストール
このエントリを書いている時点では Redmine は Rails 2.3.5 で作成されていて、 Rails 2.3.5 は Rack 1.0.1 を使用します。
もし Rack の1.2.1とかがインストールされている場合にはアンインストールしましょう。
でないと、こんなエラーが出て起動しません。
これにだいぶんはまりました。
I, [2010-10-05T14:04:25.155622 #26791] INFO -- : unlinking existing socket=/tmp/unicorn.sock I, [2010-10-05T14:04:25.155914 #26791] INFO -- : listening on addr=/tmp/unicorn.sock fd=3 I, [2010-10-05T14:04:25.156344 #26791] INFO -- : Refreshing Gem list Missing the Rails 2.3.5 gem. Please `gem install -v=2.3.5 rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.
Unicorn 0.96 doesn’t play nice with Rails 2.3.5
インストール
$ sudo apt-get install nginx $ sudo gem install unicorn
Unicorn の設定
設定のほとんどは nginx + Unicorn を試してみた – milk1000cc からのコピペです。
ここで設定するソケットを後で使用します。
$ vi $RAILS_ROOT/config/unicorn.rb
# ワーカーの数
worker_processes 2
# ソケット
listen '/tmp/unicorn.sock'
# ログ
stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
# ダウンタイムなくす
preload_app true
before_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
old_pid = "#{ server.config[:pid] }.oldbin"
unless old_pid == server.pid
begin
# SIGTTOU だと worker_processes が多いときおかしい気がする
Process.kill :QUIT, File.read(old_pid).to_i
rescue Errno::ENOENT, Errno::ESRCH
end
end
end
after_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
設定ができたら Unicorn を起動します。
$ unicorn_rails -c config/unicorn.rb -E production -D
nginx の設定
http にきたものはすべて https に転送するようにしているので、設定は以下のようになります。
upstream unicorn の部分で設定するものは、先ほどの unicorn.rb で設定したソケットです。
最下行から2行目の proxy_pass で設定する部分は、最上位の upstream hoge の hoge になります。
$ vi /etc/nginx/sites-available/redmine
upstream unicorn {
server unix:/tmp/unicorn.sock;
}
server {
listen 80;
server_name example.com;
rewrite ^/(.*) https://example.com/$1 permanent;
}
server {
listen 443;
server_name example.com;
root /var/www/redmine/public;
error_log /var/www/redmine/log/error.log;
ssl on;
ssl_certificate /etc/nginx/ssl/apache.pem;
ssl_certificate_key /etc/nginx/ssl/apache.pem;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
location / {
if (-f $request_filename) { break; }
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://unicorn;
}
}
サーバのスタート
$ sudo ln -s /etc/nginx/sites-available/redmine /etc/nginx/sites-enabled/redmine $ sudo /etc/init.d/nginx start
参考文献
- Comments: 0
- Trackbacks: 1
devise で Basic 認証をする
- 2010-09-27 (Mon)
- Ruby on Rails
devise は高機能な Rack ベースの認証システムだそうです。
plataformatec’s devise at master – GitHub
よくあるログイン画面を表示して認証する方式もありますし(これはなんというのだろう?)、いろいろな認証方式を使用することができるようです。
今回、お試しサイトを作る必要があって Basic 認証を使用することにしました。
その方法をググってもいまいちわからなかったので、ここに記載しておくことにします。
しかしながらこれで使い方があっているのかどうか、いまいち自身がないので間違っている場合には、ご指摘をお願いします。
devise では通常以下のようなコードで認証を必須にできます。
class ArticlesController < ApplicationController before_filter :authenticate_user! end
以下のページによると HTTP を使用した認証にはこんな風に書いてあります。
How to: use http authentication – devise – GitHub
def http_authenticate
authenticate_or_request_with_http_digest do |user_name, password|
user_name == "foo" && password == "bar"
end
warden.custom_failure! if performed?
end
今回はパスワードは固定ではないので、えいやとこれだけにしてみました。
class ArticlesController < ApplicationController before_filter :authenticate_user! def http_authenticate end end
思った通りに Basic 認証で認証することができるようになりました。
- Comments: 0
- Trackbacks: 0
Capistrano with Bundler and RVM
- 2010-09-25 (Sat)
- Ruby
最近、新しい Rails サーバを立ち上げる必要が出てきて、一から Capistrano によって deploy をする必要に迫られました。
Capistrano についてはいろいろなところで良質な記事があるので、それ自体を使うのは難しくはないと思うのですが、 RVM や Bundler を使用した環境で使用する場合に結構はまってしまったので、記録しておこうと思います。
サーバ側で RVM を使用する場合
RVM: Ruby Version Manager – RVM with Crapistrano を参照し、Ruby 1.9.2 を使うのであれば、以下の3行を deploy.rb に書き加えます。
$:.unshift(File.expand_path('./lib', ENV['rvm_path']))
require "rvm/capistrano"
set :rvm_ruby_string, '1.9.2'
set :rvm_type, :user
deploy 時に Bundler で Gem を更新する場合
deploy 時に bundle install で Gem をインストールしてほしいと思います。
大前提として、 Bundler を最新版にしておくことが重要です。
自分の場合、古いバージョンが入っていて上手く動きませんでした。
config/deploy.rb に
require "bundler/capistrano"
と書いておけば、 deploy 時に標準では shared/bundle フォルダに bundle install をしてくれます。
- Comments: 0
- Trackbacks: 0
FreeBSD 8.1にavahiをインストールした
- 2010-07-30 (Fri)
- UN*X
FreeBSDでBonjour (Zero Configuration Networking)を使用したかったのですが、インストールしようとするといろいろと無駄なものが入るので、なるべく削った状態でインストールしたいというのが積年の思いでした。
avahi 0.6.25まではどうしてもコンパイルがうまくいかなかったのですが、0.6.27になって(もしかしたら0.6.26でもいいのかも?)無事インストールができました。
$ wget http://avahi.org/download/avahi-0.6.27.tar.gz $ tar zxf avahi-0.6.27.tar.gz $ cd avahi-0.6.27 $ ./configure --disable-glib --disable-gobject --disable-qt3 --disable-qt4 --disable-gtk --disable-dbus --disable-python --disable-mono --disable-autoipd --disable-gdbm --disable-gtk3 --disable-monodoc $ gmake $ sudo gmake install
gmakeを使わないと
sed -e 's,@sbindir\@,/usr/local/sbin,g' > avahi-daemon.sh
というところで止まってしまいます。
あと行うべきことは2点。
- /etc/rc.conf に avahi_daemon_enable=”YES” を記述する。
- user/group に avahi を追加する。そうしないと
Failed to find user 'avahi'.
と怒られて起動ができません。
参考文献
- Comments: 0
- Trackbacks: 0
ZFS(raidz2 + spare)での修復の実際
- 2010-07-03 (Sat)
- UN*X
家で使用している、ファイルサーバはFreeBSD 8.0とZFSを使用して構築したものです。
先日、接続すると上手く反応してくれなくなっていたので、調べてみたら一つのディスクがエラーを出している様子。
環境は以下のようになります。
da0: Seagate ST3500410AS
da1 – da7: HGST HDP725050GLA360
新しいHDD: HGST HDS721010CLA332
ログは確かこんな感じ(消してしまったので適当に細工)。
# zpool status
pool: pool state: DEGRADED
status: One or more devices has experienced an unrecoverable error. An
attempt was made to correct the error. Applications are unaffected.
action: Determine if the device needs to be replaced, and clear the errors
using 'zpool clear' or replace the device with 'zpool replace'.
see: http://www.sun.com/msg/ZFS-8000-9P
scrub: none requested
config:
NAME STATE READ WRITE CKSUM
pool DEGRADED 0 0 0
raidz2 DEGRADED 0 0 0
da1 ONLINE 0 0 0 81.1M resilvered
da2 ONLINE 0 0 0 81.1M resilvered
da3 DEGRADED 209 449K 0
da4 ONLINE 0 0 0 81.1M resilvered
da5 ONLINE 0 0 0 81.1M resilvered
da6 ONLINE 0 0 0 81.1M resilvered
da7 ONLINE 0 0 0 81.1M resilvered
spares
da0 AVAIL
da3の調子が悪いみたいです。
こんなときにと用意していた、ホットスペアは勝手に使用してくれない様なので、手動でreplaceします。
# zpool replace da3 da0
# zpool status
pool: pool state: DEGRADED
status: One or more devices has experienced an unrecoverable error. An
attempt was made to correct the error. Applications are unaffected.
action: Determine if the device needs to be replaced, and clear the errors
using 'zpool clear' or replace the device with 'zpool replace'.
see: http://www.sun.com/msg/ZFS-8000-9P
scrub: resilver completed after 2h14m with 0 errors on Thu Jul 1 03:24:59 2010
config:
NAME STATE READ WRITE CKSUM
pool DEGRADED 0 0 0
raidz2 DEGRADED 0 0 0
da1 ONLINE 0 0 0 81.1M resilvered
da2 ONLINE 0 0 0 81.1M resilvered
spare DEGRADED 0 0 0
da3 OFFLINE 209 449K 0
da0 ONLINE 0 0 0 152G resilvered
da4 ONLINE 0 0 0 81.1M resilvered
da5 ONLINE 0 0 0 81.1M resilvered
da6 ONLINE 0 0 0 81.1M resilvered
da7 ONLINE 0 0 0 81.1M resilvered
spares
da0 INUSE currently in use
このままだとずっとdegradedという嫌な表示が出たままなのでda3を切り離します。
# zpool detach pool da3
いったんマシンの電源を落とし、新しいHDDを入れて起動すると
# zpool status
NAME STATE READ WRITE CKSUM
pool UNAVAIL 0 0 0 insufficient replicas
raidz2 UNAVAIL 0 0 0 insufficient replicas
da1 ONLINE 0 0 0
da2 ONLINE 0 0 0
da0 ONLINE 0 0 0
da4 FAULTED 0 0 0 corrupted data
da5 FAULTED 0 0 0 corrupted data
da6 FAULTED 0 0 0 corrupted data
da7 UNAVAIL 0 0 0 corrupted data
となってしまい、使用できない状態。
どうなっているのかを調べてみると、da3 => da7になり、da4以降が一つずつ前にずれている状態。
先でda3はdetachされているので、da4だったda3が見えていないと。
HDDのScan Orderは変えられないし、どうしたものかと悩んで調べていたら、ただ単純にexportしてimportすれば良いだけということがわかりました。
# zpool export pool # zpool import pool
# zpool status
pool: pool
state: ONLINE
scrub: none requested
config:
NAME STATE READ WRITE CKSUM
pool ONLINE 0 0 0
raidz2 ONLINE 0 0 0
da1 ONLINE 0 0 0
da2 ONLINE 0 0 0
da0 ONLINE 0 0 0
da3 ONLINE 0 0 0
da4 ONLINE 0 0 0
da5 ONLINE 0 0 0
da6 ONLINE 0 0 0
めでたく戻ってる!
最後は、spareはspareに戻しました。
# zpool replace pool da0 da7
# zpool status
pool: pool
state: ONLINE
status: One or more devices is currently being resilvered. The pool will
continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
scrub: resilver in progress for 0h47m, 40.25% done, 1h10m to go
config:
NAME STATE READ WRITE CKSUM
pool ONLINE 0 0 0
raidz2 ONLINE 0 0 0
da1 ONLINE 0 0 0 31.1M resilvered
da2 ONLINE 0 0 0 31.1M resilvered
replacing ONLINE 0 0 0
da0 ONLINE 0 0 0
da7 ONLINE 0 0 0 61.4G resilvered
da3 ONLINE 0 0 0 31.0M resilvered
da4 ONLINE 0 0 0 31.0M resilvered
da5 ONLINE 0 0 0 31.0M resilvered
da6 ONLINE 0 0 0 31.0M resilvered
# zpool add pool spare da0
# zpool status
pool: pool
state: ONLINE
scrub: resilver completed after 2h21m with 0 errors on Sat Jul 3 07:07:19 2010
config:
NAME STATE READ WRITE CKSUM
pool ONLINE 0 0 0
raidz2 ONLINE 0 0 0
da1 ONLINE 0 0 0 79.7M resilvered
da2 ONLINE 0 0 0 79.6M resilvered
da7 ONLINE 0 0 0 152G resilvered
da3 ONLINE 0 0 0 79.6M resilvered
da4 ONLINE 0 0 0 79.6M resilvered
da5 ONLINE 0 0 0 79.6M resilvered
da6 ONLINE 0 0 0 79.6M resilvered
spares
da0 AVAIL
errors: No known data errors
らくちんらくちんです。
参考文献
- Comments: 0
- Trackbacks: 1
Google Data APIs Objective-C Client LibraryをiOS SDKで使用するための準備
- 2010-06-18 (Fri)
- Cocoa
以下の文章は、Google Data APIs Objective-C Client LibraryをiOS SDKで使用するための準備 | アクトインディ技術部隊報告書と同じものです。
今回はGoogle Data APIs Objective-C Client LibraryをiOS SDKで使用するための準備について書きたいと思います。
Google Data APIs Objective-C Client LibraryはGoogleの様々なサービスをObjective-Cから使うことができるライブラリです。
Google Data APIs Objective-C Client Library
あらかじめGData.xcodeprojを、ライブラリを使用したいプロジェクトの「ファイルとグループ」欄に追加しておきましょう。
ライブラリをコンパイルする
コンパイルオプションを指定してコンパイルします。
今回はPicasaサービスとそれに付随してOAuthを使用したので、以下のオプションを指定しました。
-DGDATA_INCLUDE_PHOTOS_SERVICE=1 -DGDATA_INCLUDE_OAUTH=1
iPhone Simulator版とiPhone OS版をReleaseでコンパイルします。
lipoでFat Binaryにする
cd build lipo -create Release-iphoneos/libGDataTouchStaticLib.a Release-iphonesimulator/libGDataTouchStaticLib.a -output libGDataTouchStaticLib.a
コンパイルしたライブラリをXcodeに追加する
- Xcodeの「ターゲット」でアプリを指定し右クリックし「情報を見る」を選択。
- 「一般」 – 「リンク済みライブラリ」の下の「+」をクリック。
- 「その他を追加…」をクリックし、さきほどコンパイルしたlibGDataTouchStaticLib.aを選択。
コンパイルするための設定を追加する
- プロジェクトを右クリックし、「情報を見る」を選択。
- 「ビルド」を選ぶ。
- 「ヘッダ検索パス」に/usr/include/libxml2を追加。
- 「ユーザヘッダ検索パス」にライブラリのフォルダを追加(このとき「再帰的」にチェックを入れる)。
- 「常にユーザパスを検索」にチェックを入れる。
- 「他のリンカフラグ」に -all_load -ObjC -lxml2 を指定。
参考文献
- BuildingTheLibrary – gdata-objectivec-client – Adding the Google Data API Objective-C Client Library to a Project – Project Hosting on Google Code
- gdata-objectivec-clientを一部手動でstatic library化 – Undecidable Narrator – 曖昧な語り手
- Google Data API クライアントライブラリのビルド – Awaresoft
- [iPhone] cocos2dを静的ライブラリ化して使う : boreal-kiss.com
- Comments: 0
- Trackbacks: 0
O’Reillyの電子書籍iPhoneアプリから.epubファイルを自作する
O’Reillyでは低価格で電子書籍のiPhoneアプリケーションを配布しています。
このように600円とか700円とかのものが非常に多いです。
こんな低価格でも十分太っ腹なのに、さらにすごいのは、アプリを解凍してあげれば中にEPUBフォーマットで作成されたフォルダがまんま入っていて、自由にMacからでも見ることが出来る点です。
当のO’Reillyのサイトにもやり方が書いてあります。
Ebook Bundles – Getting The Most
- iTunesで購入した本を選択し右クリックし、「Finder」を選択。
- そのアプリのファイル(〜.ipa)を探す(多分すでにフォーカスが当たっている)。
- そのアプリのファイルを別のところにコピーし、拡張子をzipにリネーム。
- zipを解凍し、中に入っているPayload/bookというフォルダがEPUBフォーマットになっている。
- さらにその中のOEBPSフォルダを開けばxhtmlがあるので、それを表示させればMacでも表示が出来る。
せっかくEPUBフォーマットになっているのですから、ここはiPadのiBooksで表示させたい。
ということで、.epubファイルを作成します。
- bookフォルダ以下のMETA-INF, mimetype, OEBPSの3つのフォルダを選択する。
- 右クリックし「3項目を圧縮」を選択。
- 出来たzipファイルの拡張子を.epubに変更する。
あとはiTunesにドロップしてあげましょう。
まだiPadを持っていないので、実際にiBooksに転送して見ることは出来ませんが、今から非常に楽しみです。
P.S.
Mac/iPhoneアプリもあるStanzaでも転送することが出来、表示することが出来ました。1
参考文献
- もともとのO’ReillyのiPhoneアプリのバックエンドはStanzaですから当たり前といえば当たり前ですが・・・。 [↩]
- Comments: 0
- Trackbacks: 5
Objective-Cでメンバ変数に動的にアクセスする方法
- 2010-04-25 (Sun)
- Cocoa
以下の文章はObjective-Cでメンバ変数に動的にアクセスする方法 | アクトインディ技術部隊報告書と同様のものです。
今回、複数回使い回したいViewがあったのですが、一方で様々なところで使われるものではありませんでした。
こんなときにわざわざクラスを作るのはどうも重い感じがしてしまいます。
そこでUIViewのサブクラスを作らずに、メソッドで作ることにしました。
- UIViewController
- 今回作成したいView
- 今回作成したいView
というように複数個必要です。
このViewの中身はUILabelが2つだけ。
さらに出来ることなら、作成するViewの中身であるUILabelを、このViewを持っているViewControllerのメンバ変数からアクセスしたい。
以前はTagを割り当ててアクセスしていたのですが、意外と面倒だったのでそれ以外の方法がないかどうか調べてみました。
実際には以下のようなコードを使用することで出来ました。
#include <objc/runtime.h>
@implementation HogeViewController
- (UIView *)generateView:(NSString *)titleLabelName contentLabelName:(NSString *)contentLabelName
{
UIView *resultView;
UILabel *titleLabel, *contentLabel;
// 便宜上CGRectZeroを使います。
resultView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[resultView addSubview:titleLabel];
[titleLabel release];
object_setInstanceVariable(self, [titleLabelName UTF8String], titleLabel);
contentLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[resultView addSubview:contentLabel];
[contentLabel release];
object_setInstanceVariable(self, [contentLabelName UTF8String], contentLabel);
return resultView;
}
@end
titleLabelName, contentLabelNameにはそれぞれに割り当てたいメンバ変数の名称を指定します。
こんな感じにobject_setInstanceVariable関数を使用してあげれば、割り当てたいメンバ変数に割り当てることが出来ます。
P.S.
といいつつも、実は様々なところで使われる必要がわかり、まじめにUIViewのサブクラスを作成したため、上記のコードはお蔵入りになりました。
参考文献
- Comments: 0
- Trackbacks: 0
UITableViewにcellを動的に追加/削除する2つの方法。
- 2010-04-14 (Wed)
- Cocoa
以下の文章はUITableViewにcellを動的に追加/削除する2つの方法。 | アクトインディ技術部隊報告書と同様のものです。
UITableViewにcellを動的に追加/削除するということについて書こうと思います。
特にこれまでUITableViewのbeginUpdatesとendUpdatesという二つのメソッドを、いったいどんな場合に使用したらいいのか明確になっていませんでした。
ここら辺を重点的に書きたいと思います。
以下のAppleの公式ドキュメントにかなり詳しく書かれています。
Table View Programming Guide for iPhone OS: Inserting and Deleting Rows and Sections
cellを一つずつ追加/削除していく場合
cellを一つずつ追加/削除していく場合には、beginUpdates/endUpdatesの出番はありません。
追加/削除したいcellのindexPathを作成してinsert(or delete)RowsAtIndexPaths:withRowAnimation:メソッドを呼ぶだけ。
単純です。
ただし注意点はinsertRowsAtIndexPaths:withRowAnimation:メソッドを呼ぶと、UITableViewDataSourceのtableView:cellForRowAtIndexPath:メソッドが呼ばれるということ。
すなわち、cellの中身をNSArray等で管理している場合にはあらかじめNSArrayの内容を変更しておかなければなりません。
cocoa*life – UITableViewCellを削除ボタンを使って削除する
複数のcellをいっぺんに追加/削除する場合
ここまではbeginUpdatesやendUpdatesを使用する必要はありません。
では、どういうときに使用するべきなのか?
ポイントはbeginUpdates/endUpdatesに関することが書かれているセクションのタイトルがBatch Insertion and Deletion of Rows and Sectionsであるということ。
batchというのは英語で束という意味なので、「いっぺん」に複数のcellを処理したいときに使うということになります。
つまり、このメソッドを使用するかしないかの選択基準の違いは、一つずつかいっぺんかということになります。
もちろん複数のcellを追加/削除したい場合であっても、一つずつ追加/削除することができます。
beginUpdatesとendUpdatesで追加/削除のメソッド群を挟みます。
endUpdatesを呼び出したあと、tableView:cellForRowAtIndexPath:メソッドが呼ばれ、追加と削除の結果が表示されます。
beginUpdatesとendUpdatesで挟まれた追加/削除の挙動は、公式ドキュメントに以下のように書かれています。
-
The code calls the deleteRowsAtIndexPaths:withRowAnimation: method after it calls insertRowsAtIndexPaths:withRowAnimation:. However, this is not the order in which UITableView completes the operations. It defers any insertions of rows or sections until after it has handled the deletions of rows or sections. This happens regardless of ordering of the insertion and deletion method calls.
Deletions within an animation block specify which rows and sections in the original table should be removed; insertions specify which rows and sections should be added to the resulting table.
- 引用元: Table View Programming Guide for iPhone OS: Inserting and Deleting Rows and Sections(強調は引用者による)
この文章をまとめてみました。
(前提条件としてbeginUpdates/endUpdatesで囲まれている中では)
- deleteRowsAtIndexPaths:withRowAnimation:メソッドをinsertRowsAtIndexPaths:withRowAnimation:メソッドの前に呼ぶことは必須ではない(not the order)。
- 実際のcellの追加は削除のあとに行われる。上記メソッドの呼び出し順序は影響を及ぼさない(regardless of ordering of the insertion and deletion)。
- 削除時のindexPathにはbeginUpdates呼び出し前、元々の(original)テーブルに対するものを指定し、追加時は削除後の結果となる(resulting)テーブルに対するものを指定する。
このような仕組みにすることで、以下のページに書かれているように1つのcellを削除するごとにindexPathを調整することが必要なく、削除を直感的に行うことが出来ます。
テーブルのセルの削除や追加にbeginUpdates/endUpdatesは必要か(2) – iPhoneアプリ開発まっしぐら★ – iPhoneアプリ開発グループ
ということで、beginUpdates/endUpdatesメソッドはcellをいっぺんに追加/削除したい場合に使えばよい!ということになります。
参考文献
- Comments: 0
- Trackbacks: 0


