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