WebView

 

 

简介

  • 什么是UIWebView
    UIWebView是iOS内置的浏览器控件
    系统自带的Safari浏览器就是通过UIWebView实现的

  • UIWebView不但能加载远程的网页资源,还能加载绝大部分的常见文件
    html\htm
    pdf、doc、ppt、txt
    mp4
    … …

网络请求失败解决办法
1, 点击工程, 就会有右边的信息,

2, 点击TARGETS下的红线内的那个文件, 右侧最上栏会有一行单词, 找到 Info 点击就会显示 Key Type Value 这些以及下面的字段

3, 鼠标放在任意一个字段上面会显示 + - 号, 点击 + 号, 添加 NSAppTransportSecurity, 再点击后面的类型, 设置为Dictionary

4, 点击 NSAppTransportSecurity 前面的三角 △ , 三角符号朝下后, 之后在点击右侧的 + 号, 并添加NSAllowsArbitraryLoads(自己简单理解的意思是:允许任意的加载), 类型设置为Boolean, 后面点击为 YES, 就可以了.添加完后, 就可以运行, 可以请求到网络了

UIWebView常用方法

// 加载资源
- (void)loadRequest:(NSURLRequest *)request;
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
// 后退
- (IBAction)back:(id)sender {
[self.webView goBack];
}
// 前进
- (IBAction)forward:(id)sender {
[self.webView goForward];
}
// 刷新,重新加载当前页面
- (IBAction)refresh:(id)sender {
[self.webView reload];
}

webView代理方法

#pragma mark - <UIWebViewDelegate>
// 加载完成时调用
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSLog(@"%s", __func__);
// 设置能不能前进和后退
self.backItem.enabled = webView.canGoBack;
self.forward.enabled = webView.canGoForward;
}
// 开始请求时调用
- (void)webViewDidStartLoad:(UIWebView *)webView
{
NSLog(@"%s", __func__);
}
// 加载失败时调用
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(@"%s", __func__);

self.backItem.enabled = webView.canGoBack;
self.forward.enabled = webView.canGoForward;
}

添加控件

  • 宽高固定,不能设置,固定44,屏幕宽度

  • 导航,标签,五角星(unknown),固定距离,弹簧
  • UIBarButtonItem不是继承自UIview,它只是一个普通的对象,继承自nsobject

  • 最终系统会根据item中的文字自动生成button控件,但是开发的时候,面向的是对象.

  • 还有一个比较关键的方法:ShouldStartLoadWithRequest: 这个方法是有返回值的,Bool类型,一般有返回值的方法就是要控制控制器做一些事情,返回yes / no 告诉控制器应该做什么,不该做什么

  • 这个方法是,每当WebView即将发送请求之前(还没有发出去),都会调用这个方法,返回yes,就是允许加载,返回no就是不允许加载
  • 可以拦截一些操作
/**
* 每当webView即将发送一个请求之前,都会调用这个方法
* 返回YES:允许加载这个请求
* 返回NO:禁止加载这个请求
*/
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// 请求中包含life的,不能请求,其他可以请求
if ([request.URL.absoluteString containsString:@"life"]) return NO;

// JS JavaScript
return YES;
}

  • 可以和JS进行交互

webView滚动原理

webView之所以能滚动,是因为它继承自UIScrollView

  • 可以甚至网页的滚动

    self.webView.scrollView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
  • 可以直接加载网页标签

    [self.webView loadHTMLString:@"<html><body><div style=\"color: red; font-size:10px; border:1px solid blue;\">哈哈哈哈哈</div></body></html>" baseURL:nil];
  • 可以直接加载网页

    self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.520it.com"]]];
  • 可以直接加载文件数据(MineTipe)

    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:@"/Users/xiaomage/Desktop/test.pptx"]]];
  • 网页内容缩小到适应整个设备屏幕

  • 网页内容缩小到适应整个设备屏幕

    self.webView.scalesPageToFit = YES;
  • 检测各种特殊的字符串:比如电话、网站

    self.webView.dataDetectorTypes = UIDataDetectorTypeAll;

了解网页开发

工具

  • 网页(前端):Dreamweaver(美工)、Sublime(前端)、WebStorm(前端)

网页组成

  • 完整的网页组成:
    HTML:内容(文字、图片)
    CSS:美化、样式
    JS:动态效果、事件处理、跟用户进行交互

自己在工程中写HTML文件

OC调用js代码

webVie的代理方法中,当网页加载完毕的时候调用

#pragma mark - <UIWebViewDelegate>
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// oc调用js alert(100) 提示框提示:100
[webView stringByEvaluatingJavaScriptFromString:@"alert(100);"];

// 利用JS获得当前网页的标题
self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title;"];
// 调用函数
NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"login();"];
NSLog(@"%@", result);
}

js调用OC代码

这个代理方法是js和oc的桥梁,js想掉函数,发请求,就会被这个方法拦截

/**
* 通过这个方法完成JS调用OC
* JS和OC交互的第三方框架:WebViewJavaScriptBridge
*/
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = request.URL.absoluteString;
NSString *scheme = @"xmg://";
if ([url hasPrefix:scheme]) {
//截取方法名的字符串
NSString *methodName = [url substringFromIndex:scheme.length];
// 执行方法(方法名来自字符串)
[self performSelector:NSSelectorFromString(methodName) withObject:nil];

return NO;
}

NSLog(@"想加载其他请求,不是想调用OC的方法");

return YES;
}

但是这个方法是有缺陷的,不能够传参数

js代码
<html>
<!-- 网页的描述信息 -->
<head>
<meta charset="UTF-8">
<title>第一个网页</title>

<script>
function login()
{
// 让webView跳转到百度页面(加载百度页面)
// _代替:,因为:会出问题xmg://后面是要调用的方法名.
location.href = 'xmg://sendMessage_number2_number3_?100&100&99';
}
</script>
</head>

<!-- 网页的具体内容 -->
<body>
电话:10086
<button style="background: red; width:100px; height:30px;" onclick="login();">Call</button>

<br>
<a href="http://www.baidu.com">百度</a>
</body>
</html>

一个或者两个参数的时候,调用系统的performSlector可以,可以通过拼接参数的方法,完成调用,就像下面一样

#pragma mark - <UIWebViewDelegate>
/**
* 通过这个方法完成JS调用OC
* JS和OC交互的第三方框架:WebViewJavaScriptBridge
*/
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// url == xmg://sendMessage_?200
NSString *url = request.URL.absoluteString;
NSString *scheme = @"xmg://";
if ([url hasPrefix:scheme]) {
// 获得协议后面的路径 path == sendMessage_number2_?200&300
NSString *path = [url substringFromIndex:scheme.length];
// 利用?切割路径
NSArray *subpaths = [path componentsSeparatedByString:@"?"];
// 方法名 methodName == sendMessage:number2:
NSString *methodName = [[subpaths firstObject] stringByReplacingOccurrencesOfString:@"_" withString:@":"];
// 参数 200&300
NSArray *params = nil;
if (subpaths.count == 2) {
params = [[subpaths lastObject] componentsSeparatedByString:@"&"];
}
// 调用
[self performSelector:NSSelectorFromString(methodName) withObjects:params];

return NO;
}

NSLog(@"想加载其他请求,不是想调用OC的方法");

return YES;
}

但是三个参数以上的时候,就不可以这样了,系统提供的方法不能满足需求,只能另想办法,这里通过NSInvocation来解决

  • 利用一个NSInvocation对象包装一次方法调用
- (id)performSelector:(SEL)selector withObjects:(NSArray *)objects
{
// 方法签名(签名就是方法的描述)
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];

// NSInvocation : 利用一个NSInvocation对象包装一次方法调用(方法调用者、方法名、方法参数、方法返回值)
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
// 制定调用者是控制器
invocation.target = self;
// 指定调用的方法
invocation.selector = selector;

// 设置参数(除self、_cmd以外的参数个数,这两个参数是默认存在的,第0个和第一个)
NSInteger paramsCount = signature.numberOfArguments - 2;
paramsCount = MIN(paramsCount, objects.count);
for (NSInteger i = 0; i < paramsCount; i++) {
id object = objects[i];
// 判断传的参数是不是空,如果想传空参数[NSnull null]
if ([object isKindOfClass:[NSNull class]]) continue;
[invocation setArgument:&object atIndex:i + 2];
}

// 调用方法
[invocation invoke];

// 获取返回值
// 先判断要调用的方法有没有返回值(methodReturnLength--返回值类型,void对应的值是0)
// getReturnValue获取返回值类型的值
id returnValue = nil;
if (signature.methodReturnLength) { // 有返回值类型,才去获得返回值
[invocation getReturnValue:&returnValue];
}

return returnValue;
}

一般会将其封装成一个工具类(nsobjct的分类)

完善

上面的添加参数的方法还有一个小问题,当数组中的参数个数比实际需要的参数个数多时候,就会产生数组越界的问题,所以取参数的count不能用数组中的参数.

异常处理

抛异常的方式

    if (signature == nil) {
// 先创建一个异常对象,然后跑出去 错误的名称,原因
@throw [NSException exceptionWithName:@"牛逼的错误" reason:@"方法找不到" userInfo:nil];
或者:
// 直接raise出去,异常的名称 原因 哪个方法
[NSException raise:@"牛逼的错误" format:@"%@方法找不到", NSStringFromSelector(selector)];
}

抛异常后,程序就卡主了,所以,慎用

捕捉异常

 @try {
// 最简单的异常,数组越界
// 先尝试执行这里面可能有异常的代码,如果有异常,就不在执行,直接到catch
@[][0];

NSLog(@"--------2222");
}
@catch (NSException *exception) {
// 代码能来到这里,说明上面的代码有异常
NSLog(@"-----代码有异常----%@", exception);
}
@finally {
// 不管有没有异常,程序都会正常执行
NSLog(@"---------33333-");
}

捕捉异常,可以防止程序崩溃.

崩溃统计分析

第一种方式
@try {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
} @catch (NSException *exception) {
NSLog(@"main------%@", [exception callStackSymbols]);
}
}

这种方法不常用,先报错,后告诉你,有点晚

第二种方式
/**
* 拦截异常
*/
void handleException2(NSException *exception)
{
NSMutableDictionary *info = [NSMutableDictionary dictionary];
info[@"callStack"] = [exception callStackSymbols]; // 调用栈信息(错误来源于哪个方法)
info[@"name"] = [exception name]; // 异常名字
info[@"reason"] = [exception reason]; // 异常描述(报错理由)
// 将沙盒中的错误信息传递给服务器(只是提供一个思路)
// [info writeToFile:<#(NSString *)#> atomically:<#(BOOL)#>];
}
// 程序一启动,就捕捉异常
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.

// 设置捕捉异常的回调,用上面的方法处理异常
NSSetUncaughtExceptionHandler(handleException);

return YES;
}
第三种方式
  • 崩溃统计
    • 1.友盟
    • 2.Flurry
    • 3.Crashlytics

程序崩溃不死的方法

void handleException(NSException *exception)
{
// 这里不能用self,C语言函数没有self,拿到代理对象
[[UIApplication sharedApplication].delegate performSelector:@selector(handle)];
}

- (void)handle
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"哈哈" message:@"傻逼了把" delegate:self cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
[alertView show];

// 重新启动RunLoop
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
// 弹框提示,用户体验会好些.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSLog(@"-------点击了好的");
exit(0);
}

强制忽略xcode警告信息

pragma clang diagnostic push  
pragma clang diagnostic ignored "-Wincompatible-pointer-types"
//含警告的代码,如下,btn为UIButton类型的指针
UIView *view = btn;
pragma clang diagnostic pop
  • push和pop是用来定义范围的
  • -Wincompatible-pointer-types 是警告类型

  • 高亮后面那一段,就是警告类型

转载于:https://www.cnblogs.com/UncleX/p/6378846.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值