Progress Bar をつけてみる

WebView にウェブサイトを読み込むときに、ブラウザでよく見かける Progress Bar をつける方法をメモってみる。といっても、Objective-C での方法を木下さんの HMDT で見つけたので、基本的にはそれを RubyCocoa で動くように手直ししたもの。これが正しいかわからないけど、とりあえず動いている状態で一度メモる。

WebView 以外で Progress Bar を使う場合のメモは、Progress Bar を使ってみるにある。

まずは、Interface Builder で次のようなパーツを配置する。0、of、0、done とあるのは、それぞれ独立した Label (NSTextField)。これは、一つの Label にして、表示するテキストの方で調整せいてもいいのでお好みで。

これらは順に、totalLoadedText、ofText、totalToLoadText、doneText、webProgressBar という outlet にしてある。

右側のバーは、NSProgress Indicator。

ここでは、Size を Small にしてあるけど、これはお好みで。

ここの設定は、Maximum を 1.0 にしておく。あとは、Indeterminate のチェックは外しておく。Display When Stopped はチェックを入れておく(入っていなくてもだいじょうぶかも)。

ここまで設定したら、ページを読み込んでいるとき以外は表示させたくないので、最後に属性の Hidden にチェックを入れておく(そう出なければ外しておく)。

あとは、WebView の delegate メソッドを使うので、delegate を AppController というか、スクリプトを書くサブクラスのインスタンスに結びつける。

基本的には、ウェブページを読み込むと、request が delegate に送られるので、それを捕まえて数えていく。これが、読み込まれるべき resource の数になる。そして、読み込まれた resource と読み込みに失敗した resource を数えて、それにあわせてプログレスバーを更新していく。

まず、webView_didStartProvisionalLoadForFrame(webView,webFrame) という delegate メソッドで、ページの読み込みが始まったことがわかるので、そこで数値をリセットし、カウンターとプログレスバーを表示する。

@reCount - 読み込む resource の数

@reCompCount - 読み込まれた resource の数

@reFailCount - 読み込みに失敗した resource の数

あとは、setHidden(false) で outlet を表示するようにして、2つの数字のところは 0 にする。これは、結構前に書いたスクリプトなので面倒なことをしてるけど、Object Controller で一括管理するとすっきりすると思う。それに関しては、Object Controller を使ってみるにメモしてあるので、そちらを参照してください。あと、ここでは、webFrame が WebView の main frame であるかどうか確認している。

プログレスバーの値は Double 型で指定するんだけど、Ruby なので float で 0.0 とする。それを setDoubleValue(double) で設定する。

def webView_didStartProvisionalLoadForFrame(webView,webFrame)

if webFrame.to_s == webView.mainFrame.to_s

@reCount = 0

@reCompCount = 0

@reFailCount = 0

@totalLoadedText.setHidden(false)

@totalToLoadText.setHidden(false)

@totalLoadedText.setIntegerValue(0)

@totalToLoadText.setIntegerValue(0)

@webProgressBar.setHidden(false)

@webProgressBar.setDoubleValue(0.0)

@ofText.setHidden(false)

@doneText.setHidden(false)

end

end

さて、初期設定ができたら、次は、resource がいくつ読み込まれるのかを webView_identifierForInitialRequest_fromDataSource(webView,request,datasource) で捕まえて、@reCount を増やしていく。

def webView_identifierForInitialRequest_fromDataSource(webView,request,datasource)

@reCount += 1

end

それから、resource が読み込まれたときを webView_resource_didFinishLoadingFromDataSource(webView,resource,datasourse) と読み込みに失敗したときを webView_resource_didFailLoadingWithError_fromDataSource(webView,resourse,error,datasource) で捕まえて、それぞれの数を数えていく。その際に、プログレスバーとカウンターをアップデートしていく。それには別に updateProgressBar(sender) とでも名前を付けてメソッドを作る。

def webView_resource_didFinishLoadingFromDataSource(webView,resource,datasourse)

@reCompCount += 1

self.updateProgressBar(nil)

end

def webView_resource_didFailLoadingWithError_fromDataSource(webView,resourse,error,datasource)

@reFailCount += 1

self.updateProgressBar(nil)

end

これを受ける updateProgressBar(sender) では、@reCount が 0 ならプログレスバーも 0 に、そうでなければプログレスバーを進めていくようにする。Interface Builder で最大値を 1.0 に設定したので、それにあわせて設定する値を計算する。読み込まれた数は、@reCompCount から失敗した @reFailCount を引いたもので、それを @reCount で割ると 1.0 までの間でどれくらい進んだかが計算できる。

def updateProgressBar(sender)

if @reCount == 0

@webProgressBar.setDoubleValue(0.0)

else

totalProgress = @reCompCount - @reFailCount

@totalLoadedText.setIntegerValue(totalProgress)

@totalToLoadText.setIntegerValue(@reCount)

totalCount = totalProgress/@reCount.to_f

@webProgressBar.setDoubleValue(totalCount)

end

end

最後に、webView_didFinishLoadForFrame(sender,frame) でページの読み込み終了の情報を受け取り、frame が main frame ならプログレスバーとカウンターを隠している。これをそのまま残したい場合は、setHidden(true) を指定しない。

def webView_didFinishLoadForFrame(sender,frame)

if sender.mainFrame == frame

@totalLoadedText.setHidden(true)

@totalToLoadText.setHidden(true)

@webProgressBar.setHidden(true)

@ofText.setHidden(true)

@doneText.setHidden(true)

@webFrame.setEditable(true)

end

end

これで、delegate を送る WebView に対するプログレスバーができた(はず)。最後に、ここまでのスクリプトをまとめてみた。

def webView_didStartProvisionalLoadForFrame(webView,webFrame)

if webFrame.to_s == webView.mainFrame.to_s

@reCount = 0

@reCompCount = 0

@reFailCount = 0

@totalLoadedText.setHidden(false)

@totalToLoadText.setHidden(false)

@totalLoadedText.setIntegerValue(0)

@totalToLoadText.setIntegerValue(0)

@webProgressBar.setHidden(false)

@ofText.setHidden(false)

@doneText.setHidden(false)

@webFrame.setEditable(false)

end

end

def webView_identifierForInitialRequest_fromDataSource(webView,request,datasource)

@reCount += 1

end

def webView_resource_didFinishLoadingFromDataSource(webView,resource,datasourse)

@reCompCount += 1

self.updateProgressBar(nil)

end

def webView_resource_didFailLoadingWithError_fromDataSource(webView,resourse,error,datasource)

@reFailCount += 1

self.updateProgressBar(nil)

end

def updateProgressBar(sender)

if @reCount == 0

@webProgressBar.setDoubleValue(0.0)

else

totalProgress = @reCompCount - @reFailCount

@totalLoadedText.setIntegerValue(totalProgress)

@totalToLoadText.setIntegerValue(@reCount)

totalCount = totalProgress/@reCount.to_f

@webProgressBar.setDoubleValue(totalCount)

end

end

def webView_didFinishLoadForFrame(sender,frame)

if sender.mainFrame == frame

@totalLoadedText.setHidden(true)

@totalToLoadText.setHidden(true)

@webProgressBar.setHidden(true)

@ofText.setHidden(true)

@doneText.setHidden(true)

@webFrame.setEditable(true)

@appInfoObjCtl.content['webPageLoaded'] = 1

self.addWebHistory(sender)

end

end