IOS 学习笔记 WebView

通过WebView加载网页展示效果图:

目录

WKWebView

WKWebView的使用

1.1 WKWebView加载网络数据

1.1.1 WKWebView使用报错问题

1.1.2  两种方式实现WKWebView布局

1.2 WKWebView加载本地数据

1.2.1 使用loadHTMLString加载本地html页面

1.2.2 协议的的回调函数不调用问题

1.2.3 使用loadData加载本地html页面


WKWebView

移动端目前对于H5的使用越来越广泛了,随着5G时代的到来,网络提速,WebView在移动端的使用只会更加多。Android端使用WebView这个控件加载HTML+CSS+JS,IOS类似,目前使用的是WKWebView,之前UIWebView目前已经过时了,UIWebView在使用过程中对于内存开销比较大,而且加载速度也存在问题,WKWebView替代了UIWebView解决了这两个问题。

WKWebView内存占用23M,而UIWebView内存占用85M;允许JavaScript的Nitro库加载并使用(UIWebView中限制);支持了更多的H5特性;支持高达60fps的滚动刷新率以及内置手势;

IOS 学习笔记 WebView_第1张图片

 

WKWebView的使用

WKWebView加载的内容可以是网络资源也可以是本地的html页面。

 

1.1 WKWebView加载网络数据

WKWebView网络数据的加载可以调用loadRequest函数,如下:

- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;

布局如下图,红色矩形框是WKWebView,当点击“加载网络数据”Button的时候显示简述的界面:

 

1.1.1 WKWebView使用报错问题

在学习的过程中遇到一个比较坑的问题,如下

通过故事板拖拽布局一个WKWebview,两个UIButton,然后拖拽通过Outlets在ViewController中自动生成属性变量mWebView、localButton和netButtton,给netButtton这个按钮添加一个事件,在事件调用的函数中使用loadRequest加载网页,运行代码直接报错无法进入应用,错误信息如下:

2019-04-16 20:31:11.810262+0800 WebView[24662:890526] libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not supported on this platform.
2019-04-16 20:31:11.881966+0800 WebView[24662:890526] *** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: 'Could not instantiate class named WKWebView'
*** First throw call stack:
(
	0   CoreFoundation                      0x000000010c8ce1bb __exceptionPreprocess + 331
	1   libobjc.A.dylib                     0x000000010be6c735 objc_exception_throw + 48
	2   CoreFoundation                      0x000000010c8ce015 +[NSException raise:format:] + 197
	3   UIFoundation                        0x00000001152d0427 UINibDecoderDecodeObjectForValue + 359
	4   UIFoundation                        0x00000001152d02b3 -[UINibDecoder decodeObjectForKey:] + 251
	5   UIKitCore                           0x000000010f3a47b8 -[UIRuntimeConnection initWithCoder:] + 178
	6   UIFoundation                        0x00000001152d05ad UINibDecoderDecodeObjectForValue + 749
	7   UIFoundation                        0x00000001152d0854 UINibDecoderDecodeObjectForValue + 1428
	8   UIFoundation                        0x00000001152d02b3 -[UINibDecoder decodeObjectForKey:] + 251
	9   UIKitCore                           0x000000010f3a2067 -[UINib instantiateWithOwner:options:] + 1220
	10  UIKitCore                           0x000000010f115452 -[UIViewController _loadViewFromNibNamed:bundle:] + 383
	11  UIKitCore                           0x000000010f115ddc -[UIViewController loadView] + 177
	12  UIKitCore                           0x000000010f1160ee -[UIViewController loadViewIfRequired] + 175
	13  UIKitCore                           0x000000010f116940 -[UIViewController view] + 27
	14  UIKitCore                           0x000000010f76dc53 -[UIWindow addRootViewControllerViewIfPossible] + 122
	15  UIKitCore                           0x000000010f76e36e -[UIWindow _setHidden:forced:] + 294
	16  UIKitCore                           0x000000010f7815c0 -[UIWindow makeKeyAndVisible] + 42
	17  UIKitCore                           0x000000010f72e833 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4595
	18  UIKitCore                           0x000000010f733c2f -[UIApplication _runWithMainScene:transitionContext:completion:] + 1623
	19  UIKitCore                           0x000000010ef524e9 __111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke + 866
	20  UIKitCore                           0x000000010ef5b29c +[_UICanvas _enqueuePostSettingUpdateTransactionBlock:] + 153
	21  UIKitCore                           0x000000010ef52126 -[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:] + 233
	22  UIKitCore                           0x000000010ef52ae0 -[__UICanvasLifecycleMonitor_Compatability activateEventsOnly:withContext:completion:] + 1085
	23  UIKitCore                           0x000000010ef50cb5 __82-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:]_block_invoke + 795
	24  UIKitCore                           0x000000010ef5095f -[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:] + 435
	25  UIKitCore                           0x000000010ef55a90 __125-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:]_block_invoke + 584
	26  UIKitCore                           0x000000010ef5680e _performActionsWithDelayForTransitionContext + 100
	27  UIKitCore                           0x000000010ef557ef -[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:] + 221
	28  UIKitCore                           0x000000010ef5a93a -[_UICanvas scene:didUpdateWithDiff:transitionContext:completion:] + 392
	29  UIKitCore                           0x000000010f73244e -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 515
	30  UIKitCore                           0x000000010f2d6d09 -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 357
	31  FrontBoardServices                  0x0000000117f552da -[FBSSceneImpl _didCreateWithTransitionContext:completion:] + 448
	32  FrontBoardServices                  0x0000000117f60443 __56-[FBSWorkspace client:handleCreateScene:withCompletion:]_block_invoke_2 + 271
	33  FrontBoardServices                  0x0000000117f5fb3a __40-[FBSWorkspace _performDelegateCallOut:]_block_invoke + 53
	34  libdispatch.dylib                   0x000000010e1cc602 _dispatch_client_callout + 8
	35  libdispatch.dylib                   0x000000010e1cfb78 _dispatch_block_invoke_direct + 301
	36  FrontBoardServices                  0x0000000117f94ba8 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
	37  FrontBoardServices                  0x0000000117f94860 -[FBSSerialQueue _performNext] + 457
	38  FrontBoardServices                  0x0000000117f94e40 -[FBSSerialQueue _performNextFromRunLoopSource] + 45
	39  CoreFoundation                      0x000000010c833721 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
	40  CoreFoundation                      0x000000010c832f93 __CFRunLoopDoSources0 + 243
	41  CoreFoundation                      0x000000010c82d63f __CFRunLoopRun + 1263
	42  CoreFoundation                      0x000000010c82ce11 CFRunLoopRunSpecific + 625
	43  GraphicsServices                    0x0000000114f941dd GSEventRunModal + 62
	44  UIKitCore                           0x000000010f73581d UIApplicationMain + 140
	45  WebView                             0x000000010b54d400 main + 112
	46  libdyld.dylib                       0x000000010e242575 start + 1
	47  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

其中重要的一句是:reason: 'Could not instantiate class named WKWebView'

代码如下(注意viewDidLoad函数中最后一行注释后就会报上面的错误,我是采用这这句话来解决,上面遇到的初始化问题的)网上说WKWebView在Interface Builder中没有可以直接拖拽使用的WKWebView视图对象,说必须通过代码添加来实现,比如先在故事板或者xib中WKWebView用到的地方用一个UIView先占位,然后通过这个UIView的addSubview方法添加一个初始化好的WKWebView对象。

但是我却并没有直接使用代码布局使用WKWebView,而是拖拽实现,并通过代码初始化一个无用的WKWebView,就能解决问题了,具体原因我猜想是不是和Android中的WebView一样,全局就只是使用一个,在用之前只需要初始化一个就好了,具体原因之后再分析。

 

1.1.2  两种方式实现WKWebView布局

下面使用两种布局方式使用WKWebView

第一种方式,直接Interface Builder中拖拽控件布局


#import "ViewController.h"
#import 

@interface ViewController ()
@property (weak, nonatomic) IBOutlet WKWebView *webView;
@property (strong, nonatomic) IBOutlet WKWebView *mWebView;
@property (weak, nonatomic) IBOutlet UIButton *localButton;
@property (weak, nonatomic) IBOutlet UIButton *netButton;


@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.mWebView.navigationDelegate = self;//协议绑定
    //注意下面这句如果没有的话,在故事板中拖拽的WKWebView在初始化的时候会报错,具体原因不知道,但是有下面这句话先初始化一下却可以,很奇怪
    self.webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 0, 0)];
    
}


- (void)loadLoacalHtml {
    
}

#pragma mark 加载网络数据,点击按钮会调用该函数,访问网络数据
- (IBAction)loadNetHtml:(id)sender {
    NSURL *url = [NSURL URLWithString:@"https://www.jianshu.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.mWebView loadRequest:request];
    
}


#pragma mark 下面连续的四个函数是对UIWebViewDelegate的实现
#pragma mark 开始加载数据
- (void) webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"UIWebView-->start");
}

#pragma mark 数据返回 开始在界面上显示
- (void) webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"UIWebView-->response");
}

#pragma mark 数据加载完毕
- (void) webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"UIWebView-->finish");
}

#pragma mark 加载数据错误
- (void) webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(nonnull NSError *)error{
    NSLog(@"UIWebView-->fail");
}



@end

测试结果如下图:

 

第二种方式,代码WKWebView

#import "ViewController.h"
#import 

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *webViewContainer;
@property (weak, nonatomic) IBOutlet UIButton *localButton;
@property (weak, nonatomic) IBOutlet UIButton *netButton;
@property (strong, nonatomic) IBOutlet WKWebView *mWebView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.mWebView.navigationDelegate = self;//协议绑定
    
    //1、获取容器的宽,准备赋值给WKWebView
    CGFloat width = self.webViewContainer.frame.size.width;
    //2、获取容器的高,准备赋值给WKWebView
    CGFloat height = self.webViewContainer.frame.size.height;
    //3、根据获取到的宽高初始化将要使用的WKWebView对象
    self.mWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, width, height)];
    //4、使用容器将初始化好的WKWebView对象添加进来
    [self.webViewContainer addSubview:self.mWebView];
}


#pragma mark 加载网络数据,点击按钮会调用该函数,访问网络数据
- (IBAction)loadNetHtml:(id)sender {
    NSURL *url = [NSURL URLWithString:@"https://www.jianshu.com/p/432583b3b6e0"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.mWebView loadRequest:request];
    
}



- (IBAction)loadLoacalData:(id)sender {
}



#pragma mark 下面连续的四个函数是对UIWebViewDelegate的实现
#pragma mark 开始加载数据
- (void) webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"UIWebView-->start");
}

#pragma mark 数据返回 开始在界面上显示
- (void) webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"UIWebView-->response");
}

#pragma mark 数据加载完毕
- (void) webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"UIWebView-->finish");
}

#pragma mark 加载数据错误
- (void) webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(nonnull NSError *)error{
    NSLog(@"UIWebView-->fail");
}

@end

测试结果如下图:

 

 

 

1.2 WKWebView加载本地数据

WKWebView除了能够加载网络上的资源意外,同样能够加载本地html资源,对应的方法如下:

- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;

- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

 

1.2.1 使用loadHTMLString加载本地html页面

 

1、布局沿用上面的布局

2、给“加载本地数据”按钮添加一个调用的函数loadHTMLString,通过addTarget代码添加,或者拖拽连线的方式都行,这里用代码

3、准备展示的html,随便在浏览器保存一个网页到桌面,然后拖拽进项目,注意保存的css等资源包可以不要,只需要测试个结果,如下图:

IOS 学习笔记 WebView_第2张图片

4、loadHTMLString函数中添加加载代码如下


#import "ViewController.h"
#import 

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *webViewContainer;
@property (weak, nonatomic) IBOutlet UIButton *localButton;
@property (weak, nonatomic) IBOutlet UIButton *netButton;
@property (strong, nonatomic) IBOutlet WKWebView *mWebView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.mWebView.navigationDelegate = self;//协议绑定
    //1、获取容器的宽,准备赋值给WKWebView
    CGFloat width = self.webViewContainer.frame.size.width;
    //2、获取容器的高,准备赋值给WKWebView
    CGFloat height = self.webViewContainer.frame.size.height;
    //3、根据获取到的宽高初始化将要使用的WKWebView对象
    self.mWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, width, height)];
    //4、使用容器将初始化好的WKWebView对象添加进来
    [self.webViewContainer addSubview:self.mWebView];
    
   
    //给按钮localButton添加调用函数loadHTMLString
    [self.localButton addTarget:self action:@selector(loadHTMLString:) forControlEvents:UIControlEventTouchUpInside];
}




- (IBAction)loadHTMLString:(id)sender {
    NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
    NSURL * baseUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle]bundlePath]];
    NSError *error = nil;
    NSString *html = [[NSString alloc]initWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:&error];
    if(error != nil) {
        NSLog(@"数据加载错误!!!");
        return;
    }
    
    [self.mWebView loadHTMLString:html baseURL:baseUrl];
}

#pragma mark 下面连续的四个函数是对UIWebViewDelegate的实现
#pragma mark 开始加载数据
- (void) webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"UIWebView-->start");
}

#pragma mark 数据返回 开始在界面上显示
- (void) webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    NSLog(@"UIWebView-->response");
}

#pragma mark 数据加载完毕
- (void) webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSLog(@"UIWebView-->finish");
}

#pragma mark 加载数据错误
- (void) webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
    NSLog(@"UIWebView-->fail");
}



@end

5、测试结果

因为没有样式等修饰,是个基本的页面

IOS 学习笔记 WebView_第3张图片

 

1.2.2 协议的的回调函数不调用问题

在做这个测试的时候发现下面函数不调用

- (void) webView:(WKWebView *)webView didStartProvisionalNavigation:

- (void) webView:(WKWebView *)webView didCommitNavigation:

- (void) webView:(WKWebView *)webView didFinishNavigation:

- (void) webView: (WKWebView *)webViewdidFailNavigation:

我就在想是不是本地的加载数据就不调用呢?

不是,无论是加载本地网页还是网络上的网页都会回调这些函数。

其实原因很简单,之前布局的时候WKWebView'是直接拖拽布局,然后在viewDidLoad中通过:

self.mWebView.navigationDelegate = self;//协议绑定

但是后来使用一个占位的UIView,然后通过[self.webViewContainer addSubview:self.mWebView];将代码中初始化的WKWebView添加在布局中,但是回头看上面的代码我们会发现self.mWebView.navigationDelegate = self这句话在最前面,也就是mWebView这个对象我们还没有初始化就添加了协议,这样运行的时候不会像android中的java代码报mWebView空指针,但是实际上这个时候mWebView还不存在,所以将self.mWebView.navigationDelegate = self这句话移到mWebView初始化之后即可,如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
//    self.mWebView.navigationDelegate = self;//协议绑定
    //1、获取容器的宽,准备赋值给WKWebView
    CGFloat width = self.webViewContainer.frame.size.width;
    //2、获取容器的高,准备赋值给WKWebView
    CGFloat height = self.webViewContainer.frame.size.height;
    //3、根据获取到的宽高初始化将要使用的WKWebView对象
    self.mWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, width, height)];
    //4、使用容器将初始化好的WKWebView对象添加进来
    [self.webViewContainer addSubview:self.mWebView];
    
    self.mWebView.navigationDelegate = self;//协议绑定
    
    //给按钮localButton添加调用函数loadHTMLString
    [self.localButton addTarget:self action:@selector(loadHTMLString:) forControlEvents:UIControlEventTouchUpInside];
    
}

 

1.2.3 使用loadData加载本地html页面

步骤和loadHTMLString一样,只是给localButton添加点击事件的时候换成loadData即可

- (void)viewDidLoad {
    ... ... 
    [self.localButton addTarget:self action:@selector(loadData:) forControlEvents:UIControlEventTouchUpInside];
}

- (IBAction)loadData:(id)sender {
    NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
    NSURL * baseUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle]bundlePath]];
    NSData * htmldata = [[NSData alloc] initWithContentsOfFile:htmlPath];
    
    [self.mWebView loadData:htmldata MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseUrl];
}

测试结果与loadHTMLString也是一样,也一样会回调WKNavigationDelegate中的回调函数

 

 

 

 

 

你可能感兴趣的:(IOS,IOS从0到1入门笔记)