简介
-
什么是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 是警告类型
- 高亮后面那一段,就是警告类型