這一篇的內容對很多人來說大概沒什麼用,因為就算照著這篇提到的東西,寫了點程式出來,實際上也沒多少機會可以在使用者的裝置上應用,可是呢,只要討論 Cocoa,好像大部分人都興趣缺缺,談到 iPhone 卻又興致盎然。
※ iOS 的 WebKit Plug-in 基本上是一樣的
前一篇提到,我們可以直接在 Xcode 當中,透過 Xcode 本身所提供的專案範本,產生 WebKit Plug-In 專案,我們現在來做點好玩的事情。-我們把原本專案裡頭所有跟 Cocoa 相關的東西都拿掉,像是 Import Cocoa Framework 的地方,改成 Import UIKit,NSView 改成 UIView,NSRect 改成 CGRect,把 Base SDK 從 Mac OS X 改成 iPhone Simulator,在 iPhone 上面沒有定義 WebPlugInViewFactory,我們先拿掉,或是自己定義一個,裡頭只要宣告 + plugInViewWithArguments:
即可。
接著,把編出來的 Plug-In Bundle 丟到 iPhone Simulator 的 Internet Plug-In 目錄中,如果是 4.0 SDK,就是 “/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.0.sdk/System/Library/Internet Plug-Ins” 這個目錄。把 localhost 的 apache 打開,把前一篇提到的 HTML 內容,存到 local apache 的文件目錄裡,例如個人目錄下的 Sites 目錄。打開 iPhone Simulator,用 Mobile Safari 開啟我們的那份網頁瞧瞧。
好了,在 iPhone、iPod Touch、iPad 上面,寫一個瀏覽器 plug-in,就是這麼一回事。我沒有在實際的裝置上面測試過就是了-系統的 Internet Plug-Ins 基本上不允許寫入,您可以自行在 JB 過的裝置上試試看,而在 Cocoa 裡頭,每個應用程式可以使用自己 bundle 中的 WebKit Plugin,但 iOS 沒有這項設計。
總之,除了呼叫的 UI Framework 不同之外,Cocoa 與 iOS 上面的 WebKit plug-in,介面完全相同,在產生 plug-in instance 的時候,也會收到一樣的 arguments,拿到與 Cocoa 的 WebKit Plugin 一樣的參數,所以我們也就可以拿到 WebPlugInContainerKey,也就可以一路拿到 Plugin 所在的 WebFrame 與 WebView。
需要注意,這邊的 WebView 不是 UIWebView,而是實實在在,Interface 與 Cocoa 裡頭的 WebView 一樣的物件。
UIWebView 是一個精簡過-或這麼說,保留許多功能、隱藏許多 Interface 的- UI 元件,如果你試過把一個 UIWebView 有哪些 subview 倒出來看過,就可以知道,UIWebView 裡頭首先包含了一個 UIScrollView(在 3.x 與之前版本則是 UIScroller),scroll view 裡頭有一堆負責處理周圍陰影效果的 View,在這些處理陰影的 View 中間,才有一個 WebDocumentView,這個 WebDocumentView 本身是個 undocumented 物件,有一個 undocumented 的成員變數叫做 _webView,這個才是 plug-in 會看到的 WebView。
目前我們不太可能在 iPhone 上面寫個 Webkit plug-in,甚至來說,在大多數狀況下,也沒有需要非得寫個 plug-in 出來,而這年頭老實說也只剩下一家瀏覽器 plug-in,而這家公司因為這個 plug-in 與蘋果之間的風風雨雨,要寫也寫不完。倒是,去看一下 iPhone 的 WebKit 的 plug-in 架構,還是有些有趣的東西。
※ 縮放比例
iOS 裡頭目前有兩套蘋果自家的 WebKit plug-in,一套是 QuickTime,用來播放各種影音檔案,當然主要是 H.264 影片,另一個就是 Youtube plug-in;在模擬器裡頭,則只有 QuickTime。把 QuickTime plug-in 倒出來瞧瞧,可以看到幾個 Cocoa 上面沒有的 private API:
- (void)_webViewWillBeginZooming; - (void)_webViewDidZoomWithRelativeScale:(CGFloat)scale; - (void)_webViewScaleDidChange;
這幾個 method 處理的,是 Plug-in 的縮放比例。
比較桌面軟體與行動裝置的瀏覽器,雖然都有縮放比例的設計,但往往用途不太一樣,在桌面環境中,像是使用 Safari 這類的瀏覽器瀏覽網頁,需求往往是在網頁原始比例下,我們想要把文字放大些閱讀,不太會把網頁的顯示比例縮到比原始比例更小;但是在行動裝置上,因為螢幕比較小的關係,所以想要看到整個網頁的全貌,就勢必要縮小網頁的顯示比例,現在的網頁設計動輒就是 1024 pixel 寬,但是螢幕寬度就是 320 pixel,想要看到這個網頁的全貌,就必須要縮到原始大小的三成左右。
這點差別,也就決定了怎樣在 Cocoa 與 iOS 中使用 WebKit plug-in。在桌面應用程式中,使用 plug-in,最大的好處就是可以在網頁中使用 Native 的 UI 元件,像是在一個 400 pixel 寬的 plug-in view 中,放個 80 pixel 寬的按鈕,而如果使用者稍微調整網頁顯示比例,基本上都還是可以容納這些元件。
但是在行動裝置中,一個 400 pixel 的 Plug-in,在使用者在觸控螢幕上面兩指一掐,可能實際只有 40 pixel 大小。而在像 iPhone 這類裝置的觸控介面上,手指點到什麼地方,都不應該當成是 1×1 pixel 的點,而大概是 30×30 pixel 左右,在 iPhone 4 的 Retina Display 環境下,更要當做 60×60 處理。如此,我們把一個 UIButton 放進 Plug-in 中,隨隨便便都會比 Plug-in 來得更大。
使用 WebKit 的好處之一,就是可以在網頁中使用 Native UI,但是在行動裝置上看來就沒這種好處。相反地,Plug-in 畫面中的內容,應該要由傳入的縮放比例調整,實作時,大概還是要用 Quartz2D 這類的 API,自行根據縮放比例繪圖;而在 iPhone 上縮放網頁的時候,我們可以注意到,Web 內容並不是即時在 View 中動態重繪-那樣一定很慢,而是先把目前的內容整個拍成一張圖片,用 CoreAnimation 呈現圖片放大縮小的效果,直到手指放開才整個重繪一次,所以,當我們還在用手指 Pinch 縮放網頁時,畫面中的文字會比較模糊,重新 render 過之後,才會呈現清晰的文字。
因為 Plug-in 畫面在網頁中實際的尺寸可能非常小,這也就限制了 Plug-in 能夠做的事情-畢竟大小可能會被調整到約莫一根手指可以觸摸左右。想直接把桌面環境使用的 Plug-in,放在行動裝置使用,一定會有或多或少的問題。
我們可以看一下蘋果自己怎麼在這些行動裝置上面應用 Plug-in-在 iPad 上面,由於畫面比較大,所以在 QuickTime 影片的下方,還可以放置一系列播放按鈕與進度控制,但是在 iPhone 上,還沒有開始播放 QuickTime 影片之前,就只有畫面正中央一個大大的播放按鈕,按下去之後,就升起一個全螢幕的影片播放畫面,而實際上,放在 Plug-in 中的 UI 元件,就只有一個按鈕而已。
另外一個我懷疑是用 Plug-in 實作的,是 iPhone 的 Mail.app 裡頭處理附件的部份-如果一封信裡頭包含了一個比較大的附件檔案,Mail.app 並不會直接下載下來,而是在你點選了代表附件的圖示後,看到附件圖示裡頭有個風火輪轉個不停,這樣才完成下載,然後問你要在 Mail.app 裡頭瀏覽,還是用其他的應用程式開啟-像 GoodReader 可以開啟 PDF、iWork 可以處理各種 Office 檔案等。同樣的,附件圖示裡頭,也只有一個下載按鈕而已。