2010年9月3日 星期五

iOS 開發教學 - 使用 UIWebView 之 Javascript 呼叫 Objective C & 實做簡單的 Web Server?

幾個月前學習使用 UIWebView 時,就是作一個簡單得 Browser 出來,提供輸入 URL 以及上下頁等等基本功能,然後在這樣得過程中,在切換上下頁時,又想要更新 URL 欄位資訊,此時就是替 UIViewController 加上 UIWebViewDelegate 並使用 - (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType 解決。


已經有一陣子跟同事討論有沒有可能用 Javascript 去呼叫 Objective-C 做的 function 呢?雖然一直反反覆覆地回他,但今天認真看一下,找到這篇 Calling Objective-C from JavaScript in an iPhone UIWebView ,原來,他用到的技巧就是之前學作一個簡單 Browser 會用到的,去偵測點到的 hyperlink!因此,換個方式以 Javascript 使用 window.location = 'call?HelloWorld'; 時,透過 UIViewController + UIWebViewDelegate 攔截到 url 後,就可以透過 pattern matching 來決定啟用自己內定的 Objective-C function 啊!這麼簡單就解決了,果真學東西要融會貫通才有用!

下一步的思考:那我可不可以寫個簡單的 web server 擺在 iPhone native app 咧?這個答案是有可能的!並且可以考慮使用 Ajax !

現在從 Javascript 可以呼叫 Objective-C 後,而 Objetive-C 本身就可以呼叫 Javascript 啦,那就只需要從 Javascript 呼叫 Objective-C 時,準備好 callback function,之後在 Objective-C 處理完後,可以透過控制 UIWebView 去呼叫 Javascript 囉!

關於 Ajax Query 部份:

經過測試,使用 Ajax 並不能被偵測出來,只能用那種真的有在換頁的效果的,如 <a href="http://www.google.com.tw">Google</a> 這種方式,或是原先的 window.location 囉!

程式碼:

@ UIWebViewController.h (直接建立一個 UIViewController 並新增以下資料即可)

#import <UIKit/UIKit.h>
@interface UIWebViewController : UIViewController<UIWebViewDelegate> {
}
@end

@ UIWebViewController.m (直接建立一個 UIViewController 並新增以下資料即可)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
        NSString *target = [[request URL] absoluteString];
        NSRange check = [target rangeOfString:@"MyCGI?"];
        //
        //  用來判斷是否有進入這個 function
        //
        NSLog(@"Catch:%@",target);
        if( check.location != NSNotFound )
        {
                //
               
// 這邊就是自己的 CGI 要做的事,包含處理 request 以及呼叫 Javascript callback function!
               
//
                [webView stringByEvaluatingJavaScriptFromString:
                        [NSString stringWithFormat:@"report('[MyCGI Report] %@');", [target substringFromIndex:check.location]]];

                return NO;
        }
        return YES;
}

- (void)viewDidLoad {
    [super viewDidLoad];

        UIWebView *myWebView = [[UIWebView alloc] init];
        [myWebView setFrame:self.view.frame];
        [self.view addSubview:myWebView];

        [myWebView
                loadData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"]]
                MIMEType:@"text/html"
                textEncodingName:nil
                baseURL:nil];
        [myWebView setDelegate:self];
        [myWebView release];

}

@ YourAppDelegate

#import "UIWebViewController.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.
        UIWebViewController *webViewCtrl = [[UIWebViewController alloc] init];
        [window addSubview:webViewCtrl.view];
        //
        // 在此偷懶不 release 他(不然無法正確偵測)!正規寫法應該要在 header 宣告,並且在 dealloc 進行 release
        //

    [window makeKeyAndVisible];

        return YES;
}

@ index.html (別忘了擺這個檔案進去 app 囉)

<html>
        <body>
                <script language="Javascript">
                        function report( s , clear )
                        {
                                var report;
                                if( ( report  = document.getElementById( 'report' ) ) )
                                {
                                        while( clear && report.hasChildNodes() && report.childNodes.length >= 1 )
                                                report.removeChild( report.firstChild );
                                        if ( s )
                                                report.innerHTML = s + '<br/>';
                                }
                        }
                        function doQuery( target )
                        {
                                report( null , true );
                                try
                                {
                                        var ajReq = new XMLHttpRequest();
                                        ajReq.open( 'GET' , target , true );
                                        ajReq.send(null);
                                        report( 'Query: ' + target );
                                }
                                catch(err)
                                {
                                        report( '[Error] ' + err , true );
                                }
                        }
                        function doLQuery( target )
                        {
                                report( null , true );
                                report( 'LQuery: ' + target );
                                window.location = target;
                        }
                </script>
                <div id="report"></div>
                <button onclick="doQuery('http://www.google.com.tw');">Ajax Query - Google</button><br />
                <button onclick="doQuery('http://localhost/MyCGI?HelloWorld');">Ajax Query - MyCGI</button><br />
                <button onclick="doLQuery('http://www.google.com.tw');">location Query - Google</button><br />
                <button onclick="doLQuery('http://localhost/MyCGI?HelloWorld');">location Query - MyCGI</button><br />
                <a href="http://www.google.com.tw" target="_blank">Google.com</a>
                <a href="http://localhost/MyCGI?HelloWorld" target="_blank">MyCGI</a>
        </body>
</html>

參考資料:


沒有留言:

張貼留言