工作开发的app里有一项功能是邮箱验证。主要流程是用户输入一个邮箱地址,例如zlayne1989@163.com,这个邮箱地址会被传回给server,server往这个邮箱发送一封邮件,里面包含一个链接。用户可以使用app自带的webview打开网页版邮箱登录界面。登录邮箱后,用户需要点击邮件里的链接,之后webView会加载公司主站的网页并提示验证完成。整个逻辑并不复杂,但是在这里出了个小问题,那就是在显示公司主站的时候,显示为乱码。跟接口那边确认,那边说是已经指定了网页的编码形式为GBK,按理说不应该出现乱码的情况。后来将验证链接用浏览器打开依旧是乱码,查看浏览器的设置,发现编码方式指定为自动识别,网页被识别为UTF-8,而并没有将这个网页识别为GBK。后来发现是代理无服务器的问题。因为公司上外网都要走2.78的服务器,通过这种方式会出现乱码,而使用自己手机的3G却是正常的,所以这个问题就不需要修改了,不影响产品的使用。虽然是代理服务器的问题,但未找到根源之前,我尝试着使用UIWebView来解决编码问题,所以有了这篇博文。

1、UIWebView加载网页的3种方式:

-(void)loadRequest:(NSURLRequest*)request;-(void)loadHTMLString:(NSString*)stringbaseURL:(NSURL*)baseURL;-(void)loadData:(NSData*)dataMIMEType:(NSString*)MIMETypetextEncodingName:(NSString*)textEncodingNamebaseURL:(NSURL*)baseURL;

第一种最简单,只有一个NSURLRequest对象参数。第二种和第三种都是用来加载“本地”网页,这里说的“本地”是指需要用[NSString stringWithContentOfURL:url]等类似方式先从网络上加载完成网页数据,然后通过这俩函数将网页用webView显示出来。显示的乱码的情况是直接使用loadRequest加载网页。于是我尝试了用loadHTMLString来替换loadRequest。

2、使用loadHTMLString

在函数(UIWebViewDelegate)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType中截获用户在邮箱里面点击的那个验证链接:

NSURL*url=[requestURL];NSString*urlString=[urlabsoluteString];

由于公司的验证链接都是统一格式的(www.51jingying.com/mailtrace.php?.........),因此针对于这个项目,我采用了最傻瓜的方式来判断当前用户点击的链接是不是邮箱验证链接——在urlString里查找"mailtrace.php"。代码如下:

if(!reloaded&&[urlStringrangeOfString:@"/mailtrace.php"].length>0){reloaded=YES;self.webURL=url;[selfreLoadWebView];returnNO;}

因为在函数reLoadWebView中会使用LoadHTMLString,而这个函数也会调用上边的代理方法,因此定义个一个标志reloaded,保证只加载一次,否则会出现死循环。reLoadWebView如下:

-(void)reLoadWebView{[selfstartLoad];_errorLabel.text=@"";_errorLabel.userInteractionEnabled=NO;self.webContainer.userInteractionEnabled=NO;//GBK编码(0x0632)NSStringEncodinggbkEncoding=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);//GB18030(0x0631)NSStringEncodinggb18030Encoding=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGBK_95);NSMutableURLRequest*request=[[NSMutableURLRequestalloc]initWithURL:self.webURL];[(id)self.webContainer.delegateobjRetain];NSStringEncoding*usedEncoding=NULL;//将网页加载放到另一个线程里,已防止访问某些”特殊“的邮箱地址(如gmail)导致界面卡住dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{//带BOM头的如utf-8等这里会识别NSString*body=[NSStringstringWithContentsOfURL:self.webURLusedEncoding:usedEncodingerror:nil];//识别不到,按GBK编码再解码一次if(!body){body=[NSStringstringWithContentsOfURL:self.webURLencoding:gbkEncodingerror:nil];}//还是识别不到,按GB18030编码再解码一次.if(!body){body=[NSStringstringWithContentsOfURL:self.webURLencoding:gb18030Encodingerror:nil];}//显示if(body){[self.webContainerloadHTMLString:bodybaseURL:nil];}else{[self.webContainerloadRequest:request];}});}

其中webContainer是UIWebView实例。其实以上这些代码也可以直接用loadData来代替,loadData可以指定编码方式,也可以指定MIMEType,不过这样一来如果指定的参数有误就会加载不了网页了。

3、问题

使用上面的方法会带来两个问题:

(1)因为事先要使用stringWithContentOfURL预加载网页,而这个函数不会走UIWebViewDelegate方法:

-(void)webViewDidStartLoad:(UIWebView*)webView-(void)webViewDidFinishLoad:(UIWebView*)webView-(void)webView:(UIWebView*)webViewdidFailLoadWithError:(NSError*)error

所以我们无法监测网页的预加载情况:何时开始、何时结束、是否成功。例如,如果url为www.google.com,那么在调用了stringWithContentOfURL之后程序就可能很久都不会有任何反应(墙...),这也是为什么我使用dispatch_async的原因(界面会卡死)。

(2)若用上面的方法加载了网页A,而网页A中其他超链接,这些超链接又是不完整的,那么在A页面点击其他超链接则会出错。例如网页A中有

<ahref="/common/test.php">测试超链接<a/>

如果网页A是网址为“www.zlayne.com”,webView使用loadRequest加载的网页A,那么即使页面上的超链接如上所示,用户也可以跳转到正确页面。

但是!若webView是通过loadHTMLString加载的网页A,前面也说过了loadHTMLString是将网页预加载下来转换为string,那么这时候点击如上的超链接就会出错。因为实际访问的网址形如:

applewebdata://9cds-dsdf-96sdo-213pnm/common/test.php

解决这个问题的思路无非就是获得当前页面的域名,然后跟后边的拼接起来组成实际的网址链接没啥技术含量。


最后,无意中发现双击webView会使它变小,行为很怪,具体原因不明。