Android WebView with load ProgressBar

It usually difficulty internet environment on mobile device. Sometimes angry for don't know the loading status. Maybe device be like angry bird fly on air. The feedback is very very important.

How to get WebView status Sample as below
webView = (WebView) findViewById(R.id.index_webview);
webView.loadUrl("YOUR_URL");
webView.setWebChromeClient(new WebChromeClient(){
public void onProgressChanged(WebView view, int progress) {
//Make the bar disappear after URL is loaded, and changes string to Loading...
if(newProgress <= 10){ //TODO something } // Return the app name after finish loading if(progress == 100){ //TODO something } } });

Reference
Android Developer
Android Developer - WebView

iOS UIWebView scrollview unrecognized

先前寫過一篇「iOS Webview scroll bounces」,能夠把webView的scrollView的反彈移除,在目前持有的手機版本iOS 5.1中相當的正常,但在iOS 4.3.3中卻會出現錯誤訊息如下:
[UIWebView scrollView]: unrecognized selector sent to instance

由於無法直接access到UIWebview 的scrollView,但scrollView確實是webView的subview,解決方式如下:
NSArray *webViewSubViews = [NSArray arrayWithArray:[webView subviews]];
UIScrollView *webScroller = (UIScrollView *)[webViewSubViews objectAtIndex:0];
webScroller.bounces = NO;

將WebView的ScrollView取出,再將其進行設定即可。

相關連結
iOS Developer Library
iOS Developer Library - WebView
iOS Webview scroll bounces

iOS Location service 定位服務

取得所在位置是行動裝置的一大特色,在地化、導航、語言、文化都跟這服務脫離不了關係,在iOS中使用這服務需要先import一個framework如下
CoreLocation.framework

加入framework後還必須加入CoreLocation的Delegate,以及因為下面範例中使用者如果將定位服務關閉彈出Alert詢問使用者是否將開啟定位服務,需要加入的Delegate如下(在.h檔中)
CLLocationManagerDelegate, UIAlertViewDelegate

加入後應該會看到錯誤,因為CoreLocation這framework還沒被import,必須在.h檔上方先import這行
#import <CoreLocation/CoreLocation.h>


使用的方式如下(.m檔)
//初始化LocationService
-(void) openLocationService{
//location
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
}

//給地址、經緯度後直接開啟Google map導航
-(void)navivationToTheAddress:(NSString *)address myLatitude:(double)latitude myLongitude:(double)longitude{
NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps?f=d&source=s_d&saddr=%f,%f&daddr=%@", latitude, longitude, address];
NSString *escaped = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:escaped]];
}

//location Delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
NSString *address = [self.myWebView stringByEvaluatingJavaScriptFromString:@"naviAddress"];
[self navivationToTheAddress:address myLatitude:manager.location.coordinate.latitude myLongitude:manager.location.coordinate.longitude];

[manager stopUpdatingLocation];
}

//錯誤發生時回報方式
-(void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
//NSLog(@"didFailWithError = %@", error);
NSString *errorString;
[manager stopUpdatingLocation];
NSLog(@"Error: %@",[error localizedDescription]);
switch([error code]) {
case kCLErrorDenied:
errorString = @"您已關閉定位服務無法順利導航,是否立即開啟服務?";
break;
case kCLErrorLocationUnknown:
errorString = @"無法順利取得您的位置";
break;
default:
errorString = @"無法順利取得您的位置";
break;
}

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Golf球場指南" message:errorString delegate:self cancelButtonTitle:@"收後啟用" otherButtonTitles:@"立即啟用", nil];
[alert show];
}

-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
NSLog(@"clickedButtonAtIndex buttonIndex = %d", buttonIndex);
if(buttonIndex == 1){
//點選"立即開啟"後打開設定中的定位服務開啟畫面
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=LOCATION_SERVICES"]];
}
}


相關連結
iOS Developer Library
iOS Developer Library - CoreLocation Framework

iOS WebView Javascript send value to native objective-c

iOS因為每次送審必須花上幾天的審核時間,所以很多需要經常變動的頁面常會使用web的方式來進行設計,但iOS因為WebView沒開放Nitro,所以速度上比起原生的差了不少,然而部分還是可以透過些小技巧來加速,畢竟如Google+ ,Facebook等為了常變更timeline也都會採用此方式。

原生的SDK就有提供Native(Objective-C)直接來呼叫Javascript的function或變數,也可以一並取得function、變數的值,方法很簡單,就下面這一行:
[webView stringByEvaluatingJavaScriptFromString:@"YOUR_FUNCTION_NAME"]

這方式相當的好用,甚至可以直接將Javascript需要另外執行的function等東西直接用這方式塞入html中,可以方變得改變webview所呈現的內容,但如果光靠這種方式還是不太夠的,畢竟這是單向的由Native(Objective-C)來監看webview的狀態,使用個timer在背景掃描也會造成系統的負載,畢竟在Mobile上能省電就省電,一個太耗效能的App沒多久就會被使用者發現,然後就被刪除,所以必須有方法來由Javascript用Event的概念直接將值傳回Native(Objective-C),原理很簡單,就是使用URL scheme的方式來傳,詳細的問題點下面會再說明,先直接來看作法:


首先Html Javascript這邊的準備如下:
sendToNative('哈哈哈哈哈哈');

function sendToNative(msg){
document.location = 'YOUR_SCHEME://myText='+encodeURIComponent(msg);
};

乍看之下不就是把當前的URL改掉,按照正常的WebView不就會直接的轉跳到其他頁面去,但是在webView的Delegate是可以避免掉這問題,方法如下

//init webview
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
NSString *path = [[NSBundle mainBundle] pathForResource:@"YOUR_FILE_NAME" ofType:@"html" inDirectory:@"YOUR_FOLDER_NAME"];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]];
[webView setDelegate:self];
[self.view addSubview:webView];;


//webView delegate
-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
//取得Scheme
NSString *scheme = [[[request URL] scheme] lowercaseString];

//如果scheme與自己定義的相同
if([scheme isEqualToString:@"YOUR_SCHEME"]) {
NSString *url = [NSString stringWithFormat:@"%@", [request URL]];
NSDictionary *myDic = [self urlToDictionaryWithURL:url scheme:scheme];

return NO;
}
return YES;
}

//將所有參數轉為Dictionary
-(NSMutableDictionary *) urlToDictionaryWithURL:(NSString *)url scheme:(NSString *)scheme{
NSArray *tempArray = [[url substringFromIndex:scheme.length+3] componentsSeparatedByString:@"&"] ;
NSMutableDictionary *myDic = [NSMutableDictionary new];

for (int i = 0; i < [tempArray count]; i++) { NSString *myString = [[tempArray objectAtIndex:i] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSArray *temp = [myString componentsSeparatedByString:@"="]; [myDic setValue:[[temp objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[temp objectAtIndex:0]]; } return myDic; }

webView的Delegate 當return為YES時會允許webview的url作他該做的動作,NO時則不會進行,但還是可以順利取得當時的url來運用,上面範例中的"urlToDictionaryWithURL"為將URL參數轉為Dictionary方便使用,URL參數格式如下,也就是GET的格式,使用"&"來做區隔。
YOUR_SCHEME://a=11111&b=22222&c=33333


但使用URL來傳值也是會有些限制,在字數、部分符號上都會造成問題,根據"LinkedIn for iPad: The Native/Web Messaging Bridge and WebSockets"這篇文章所提還是會建議使用URL方式來呼叫Native(Objective-C),再用"stringByEvaluatingJavaScriptFromString"來取得Javascript參數,可以避免URL的限制,文章中也提到使用WebSocket來進行傳送,是效率最高,但相同也會有些其他問題,最好的方式應該還是混合使用。



相關連結
LinkedIn for iPad: The Native/Web Messaging Bridge and WebSockets
iOS Developer Library
iOS Developer Library - WebView

iOS UIWebView video auto play

iOS使用WebView來作為MediaPlayer相當的方便,不但可以播放Youtube,也可以播放local的影音檔案,但在iOS使用html5的autoplay 功能卻會無法正常使用必須在Native(objective-c)這端加上這段
[webView setMediaPlaybackRequiresUserAction:NO];

而html這邊設定自動播放方式如下:
<video src="YOURFILEPATH" autoplay ></video>

光是使用video tag來播放還是會遇上些播放狀態的問題,由於autoplay會直接進行全螢幕播放,在Native SDK可以取得進入全螢幕、離開全螢幕事件,只要放在Init webview之後,使用Notification Center方式來傳送訊號
//進入全螢幕
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeStarted:) name:@"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil];
//離開全螢幕
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(youTubeFinished:) name:@"UIMoviePlayerControllerDidExitFullscreenNotification" object:nil];

而取得這些Notification的function如下
-(void)youTubeStarted:(NSNotification *)notification{
// your code here
NSLog(@"ENTER!!!");
}

-(void)youTubeFinished:(NSNotification *)notification{
// your code here
NSLog(@"EXIT!!!!!");
}


如果有需要使用javascript來控制影片的播放,如暫停、播放、播放完畢、目前播放秒數...等資訊也是相當簡單,以下為使用jQuery為例
//播放完畢
$("video").bind("ended", function() {
//end
});

//暫停
$('video').bind("pause",function(e){
//pause
});

//播放
$('video').bind("play",function(e){
//play
});

//目前播放時間(秒)
$('video').bind("timeupdate",function(e){
//timeupdate
});


其中播放完畢後預設為停在結束畫面並不會自動關閉播放模式,必須使用下面這行來進行離開全螢幕播放模式
$('video').get(0).webkitExitFullScreen();


如果要使用Javascript取得目前是否全螢幕瀏覽可使用下面方式判斷,回復為true為全螢幕,若為false為離開全螢幕
$('video').get(0).webkitDisplayingFullscreen;


相關連結
Opera Dev - Introduction to HTML5 video
iOS Developer Library
iOS Developer Library - WebView

iOS Webview scroll bounces

iOS使用Webview在上下滑到底的同時通常還可多滑出一段,也就是與UIScrollView一樣的反彈,來告知使用者已經滑到底,我覺得是個相當好的回饋,但如果自己已經有做好另外的回饋,也是可以將這個效果關閉,只需要在UIWebView加上下面這行就會關閉。

webView.scrollView.bounces = NO;

相關連結
iOS Developer Library
iOS Developer Library - WebView
iOS UIWebView scrollview unrecognized

iOS Web App hide button bar

iOS使用Safari瀏覽同時下方有一個按鈕列相當的佔版面,但如果你的服務預設就已經是全螢幕,這時候就會覺得這很討厭,使用方式如前一篇所寫"Add to Home Screen"方式,加到桌面,然後記得在head中加入下面這行

<meta name="apple-mobile-web-app-capable" content="yes" />

相關連結
iOS Mobile web app add to home screen icon

iOS Mobile web app add to home screen icon

iOS開發WebApp是常見的事,尤其iOS內建的Safari有個特殊功能是"Add to Home Screen",加入後的icon其實就跟常見的App沒兩樣,可以參考下圖說明:

1.打開Safari,會發現下方的按鈕列正中將有個Export的icon


2.按下去後會談出一堆按鈕,其中由上數下來第三個即為"Add to Home Screen"


3.點擊後會讓你輸入名稱,也就是之後的App名稱


4.加入完成,在畫面中就會多出個App icon,點下去後就是直接開啟這頁面的快速鍵。



當然到這裡有人會想問幹嘛不寫個App就好了,幹嘛這麼麻煩?Web App與Native app的優缺點在這就不作說明,隨便Google一下應該就會有很多大師給您開示,會想用這的一大原因是iOS的Safari有Nitro加速,比起Native的WebView快上很多很多,也是facebook一直被幹焦效能不好的原因,其實用Mobile Web版本的Facebook速度整個快超多的,就是因為Nitro,不過本篇重點是該如何設定icon,這邊就趕快進入主題。


你的html的head加入以下這一段再記得把icon放到路徑中,在加入時就會出現,但是會有光澤反射
<link rel="apple-touch-icon" sizes="57x57" href="ICONNAME.png">

拿掉光澤反射方法如下在"rel"改為apple-touch-icon-precomposed
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="ICONNAME.png" />

sizes就是使用ios預設尺寸,記得依照不同Device作設置
iPhone = 57x57(pixels)
iPhone Retina = 114x114(pixels)
iPad = 72x72(pixels)
iPad Retina = 144x144(pixels)

只要都放對就會有漂亮的icon出現啦,再依照需求看是否需要反光的光澤。

Android Webview disable link click highlight

Android平常開啟時常會有討厭的預設的邊框,相當的顯眼,對於使用者來說這是個不錯的回饋,但對於已經有設定好另外的回饋來說這就變得有點多餘,只需要在css中加上以下一行即可消除預設的框框。

-webkit-tap-highlight-color: rgba(0, 0, 0, 0);


Android Webview hide scrollbar

scrollbar很重要,可以讓使用者清楚的知道現在的位置,但是在不該出現的時候跑出來就很討厭,下面這行可以把非必要時的scrollbar隱藏起來。

webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);


Android webView get javascript web value

用法很簡單,功能上也比起iOS所能提供得支援強大,廢話不多說往下看。

JS呼叫Android

JS
function androidResponse() {
window.YOURNAME.FUNCTIONNAME("I am being sent to Android.");
}

Android
final class IJavascriptHandler {
IJavascriptHandler() {}
public void FUNCTIONNAME(String text) {
// this is called from JS with passed value
Toast t = Toast.makeText(getApplicationContext(), text, 2000);
t.show();
}
}


WebView記得要去設定一下
webView.addJavascriptInterface(new IJavascriptHandler(), "YOURNAME");




Android呼叫JS

JS
funcrion androidResponse(string){
alert(string)
}

Android
webView.loadUrl("javascript:androidResponse();");

webView記得去把JS enable
webview.getSettings().setJavaScriptEnabled(true);
或用
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);



update 2012-08-16
熊熊發現到Android Developer官方就有篇教學
http://developer.android.com/guide/webapps/webview.html

相關連結
Android Developer
Android Developer - WebView

Javascript back previous page/ next page

常用到,但是也常常忘,記錄一下

上一頁
history.go(-1)

上一頁在超連結中
<a href="javascript:history.go(-1)">Back</a>

下一頁
history.go(1)

下一頁在超連結中
<a href="javascript:history.go(1)">Forward</a>

Android WebView Enable Javascript

Android webView預設Javascript是不允許使用的,以下方法為開啟Javascript:
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);


相關資訊
Android Developer

iOS class without ARC compiler

相信很多人在網路上看過很多很棒的framework, class,但礙於iOS5後很多專案使用ARC,加上很多後進的開發者一開始就使用ARC,以致想用些以前好用的framework會無法正常使用,以下的方法為使用ARC的專案,將部分檔案使用非ARC的方式來compiler

其實只需要加上這一行如下
-fno-objc-arc

放置的位置可參考下圖


相關連結
iOS Developer Library

iOS NSUserDefaults save and load

iOS app中常常會需要作些儲存設定,這邊先寫一種最簡單的NSUserDefaults,如果有稍微多一點資料需要儲存建議使用plist,真的有超大量要儲存建議使用coreData或sqlite。


儲存
NSString *saveSetting = @"save value";
[[NSUserDefaults standardUserDefaults] setObject:saveSetting forKey:@"YOURKEY"];


讀取
NSString *loadSetting = [[NSUserDefaults standardUserDefaults] stringForKey:@"YOURKEY"];



相關連結
iOS Developer Library
iOS Developer Library - NSUserDefault

jQueryMobile listView add/delete row

jQueryMobile是個很好用的mobile web framework,尤其在做prototype更是方便!ListView是Mobile上相當常用的呈現方式,當然必須常常來做些增減內容,以下說明增加的方式:

關鍵就是下面這行
$('#listView').append(output).listview('refresh');

output就直接把字串組合進去即可,例如使用ul、il的架構來說:
<li><h3>title</h3></li>


如果有需要刪除某一行可以使用下面這方式(感覺有點笨,不過目前還找不到更好的方式,有人有找到記得告訴我:))
$('#listView').empty().append(output).listview('refresh');


相關連結
jQuery Mobile - http://jquerymobile.com/
jQuery Mobile Demo - http://jquerymobile.com/demos/