原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 一、CoreLocation 的使用
- 1、用户授权
- 2、设置定位管理者
- 3、获取用户当前的位置
- 4、磁力感应
- 5、区域检测
- 6、地理编码与反地理编码
- 二、WKWebView 的使用
- 1、WKWebView 涉及的一些类
- 2、WKWebView涉及的代理方法
- 3、网页内容加载进度条和title的实现
- 4、JS和OC的交互
- 5、Cookie 问题
一、CoreLocation 的使用
1、用户授权
a、运行效果
![](https://img.haomeiwen.com/i9570900/108a6b68e9671713.png)
2020-11-06 11:12:32.676599+0800 LocationDemo[52957:5777093] 等待用户授权
2020-11-06 11:13:28.432218+0800 LocationDemo[52957:5777093] 授权成功,开始定位
b、Info.plist 添加定位权限
Privacy - Location When In Use Usage Description
:需要在运行APP的时候使用定位权限
Privacy - Location Always and When In Use Usage Description
:需要使用定位权限
c、CLAuthorizationStatus
kCLAuthorizationStatusNotDetermined //用户从未选择过权限,无法使用定位服务,该状态用户无法改变
kCLAuthorizationStatusRestricted //用户拒绝该应用使用定位服务,或是定位服务总开关处于关闭状态
kCLAuthorizationStatusDenied //用户拒绝授权
kCLAuthorizationStatusAuthorizedAlways //用户允许该程序无论何时都可以使用地理信息
kCLAuthorizationStatusAuthorizedWhenInUse //用户同意程序在可见时使用地理位置
d、授权状态发生改变时调用
- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager
{
if (manager.authorizationStatus == kCLAuthorizationStatusNotDetermined)
{
NSLog(@"等待用户授权");
}
else if (manager.authorizationStatus == kCLAuthorizationStatusAuthorizedAlways || manager.authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse)
{
NSLog(@"授权成功,开始定位");
[self.mgr startUpdatingLocation];
}
else
{
NSLog(@"授权失败");
}
}
2、设置定位管理者
a、引入定位头文件
#import <CoreLocation/CoreLocation.h>
@interface CoreLocationViewController ()<CLLocationManagerDelegate>
@property (nonatomic ,strong) CLLocationManager *locationManager;// 定位管理者
@end
b、设置定位管理者的属性
- (void)configLocationManager
{
self.place = @"";
// 判断定位权限是否打开
if ([CLLocationManager locationServicesEnabled])
{
_locationManager = [[CLLocationManager alloc] init];
// 成为CoreLocation管理者的代理监听,用于获取到用户位置
_locationManager.delegate = self;
// 设置寻址精度
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// 设置多远获取一次
_locationManager.distanceFilter = 100.0f;
// 开始定位
[_locationManager startUpdatingLocation];
}
else
{
// 必须在info.plist文件中配置一项属性才能弹出授权窗口
// 要主动请求授权定位权限,当授权状态改变就会通知代理
[_locationManager requestAlwaysAuthorization];
}
}
3、获取用户当前的位置
a、运行效果
2020-11-06 11:30:33.256619+0800 LocationDemo[926:133009] 纬度:24.477126,经度:118.183663,速度:-1.000000
2020-11-06 11:30:33.492373+0800 LocationDemo[926:133009] 在下当前所在位置为:省:福建省,市:厦门市,街道:思明区 展鸿路81号
b、位置信息
可以设置模拟器模拟速度
bicycle ride// 骑车移动
run// 跑动
freeway drive// 高速公路驾车
可以获取到的位置信息
location.coordinate; //坐标, 包含经纬度
location.altitude; //设备海拔高度 单位是米
location.course; //设置前进方向 0表示北 90东 180南 270西
location.horizontalAccuracy; //水平精准度
location.verticalAccuracy; //垂直精准度
location.timestamp; //定位信息返回的时间
location.speed; //设备移动速度 单位是米/秒, 适用于行车速度而不太适用于不行
c、获取到位置信息之后就会调用(调用频率非常高)
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
// 如果只需要获取一次, 可以获取到位置之后就停止
[_locationManager stopUpdatingHeading];
[_locationManager stopUpdatingLocation];
// 获取最后一次的位置
CLLocation *location = [locations lastObject];
NSLog(@"纬度:%f,经度:%f,速度:%f", location.coordinate.latitude, location.coordinate.longitude, location.speed);
CLLocation *currentLocation = [locations lastObject];
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
// 利用经纬度进行反编译获取位置信息
[geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (placemarks.count > 0)
{
// 获取当前定位信息
CLPlacemark *placeMark = [placemarks firstObject];
if (placeMark)
{
//省、市、县、街
self.place = [NSString stringWithFormat:@"省:%@,市:%@,街道:%@ %@",placeMark.administrativeArea,placeMark.locality,placeMark.subLocality,placeMark.thoroughfare];
NSLog(@"在下当前所在位置为:%@",self.place);
}
}
}];
}
d、定位失败时调用
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
[_locationManager startUpdatingLocation];
}
4、磁力感应
a、运行效果
![](https://img.haomeiwen.com/i9570900/46b32e19d9d919ef.png)
2020-11-06 13:51:35.295963+0800 LocationDemo[1309:196044] 设备支持则开启磁力感应
2020-11-06 13:51:35.364536+0800 LocationDemo[1309:196044] 设备与磁北的相对角度:232.313324
b、判断设备是否支持则开启磁力感应
if ([CLLocationManager headingAvailable])
{
_locationManager.headingFilter = kCLHeadingFilterNone;
[_locationManager startUpdatingHeading];
NSLog(@"设备支持则开启磁力感应");
}
else
{
NSLog(@"设备不支持则开启磁力感应");
}
c、当获取到用户方向时就会调用
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
// magneticHeading: 设备与磁北的相对角度
// trueHeading:设备与真北的相对角度,真北始终指向地理北极点
NSLog(@"设备与磁北的相对角度:%f",newHeading.magneticHeading);
// 将获取到的角度转为弧度 = (角度 * π) / 180;
CGFloat angle = newHeading.magneticHeading * M_PI / 180;
// 如果需要根据角度来旋转图片,如指南针应用等,可以这样调用
// 顺时针 正 逆时针 负
self.compasspointer.transform = CGAffineTransformMakeRotation(-angle);
}
5、区域检测
a、监听区域
- (void)areaDetection
{
// 创建中心点
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(24.477117, 118.183691);
// 创建圆形区域, 指定区域中心点的经纬度, 以及半径
CLCircularRegion *circular = [[CLCircularRegion alloc] initWithCenter:center radius:0.1 identifier:@"厦门"];
[_locationManager startMonitoringForRegion:circular];
}
b、进入监听区域时调用
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(@"进入监听区域时调用");
}
c、离开监听区域时调用
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(@"离开监听区域时调用");
}
6、地理编码与反地理编码
a、运行效果
2020-11-06 14:55:09.540017+0800 LocationDemo[1463:228984] name=福建省厦门市思明区岭兜南片区156号楼 locality=厦门市 country=中国 postalCode=(null)
2020-11-06 14:55:09.540113+0800 LocationDemo[1463:228984] 地理编码,获取到的详细地址名称:福建省厦门市思明区岭兜南片区156号楼,纬度:24.48,经度:118.18
2020-11-06 14:55:09.480586+0800 LocationDemo[1463:228984] 反地理编码获取到的位置名称为: 岭兜南片区156号楼
b、地理编码: 地名—>经纬度坐标
- (void)geocoding
{
...
}
❶ 根据传入的地址获取该地址对应的经纬度信息
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:@"岭兜南片区156号楼" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
...
}];
❷ 如果有错误信息,或者是数组中获取的地名元素数量为0,那么说明没有找到
if (error || placemarks.count == 0)
{
NSLog(@"你输入的地址没找到,可能在月球上");
}
❸ 否则打印查看找到的所有的位置信息
// locality:城市 country:国家 postalCode:邮政编码
for (CLPlacemark *placemark in placemarks)
{
NSLog(@"name=%@ locality=%@ country=%@ postalCode=%@",placemark.name, placemark.locality, placemark.country, placemark.postalCode);
}
❹ 取出获取的地理信息数组中的第一个显示在界面上
CLPlacemark *firstPlacemark = [placemarks firstObject];
// 详细地址名称
NSString *latitude = [NSString stringWithFormat:@"%.2f",firstPlacemark.location.coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:@"%.2f",firstPlacemark.location.coordinate.longitude];
NSLog(@"地理编码,获取到的详细地址名称:%@,纬度:%@,经度:%@",firstPlacemark.name,latitude,longitude);
c、反地理编码:经纬度坐标—>地名
- (void)antiGeocoding
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
// 1.获得输入的经纬度
CLLocationDegrees latitude = [@"24.477139" doubleValue];
CLLocationDegrees longitude = [@"118.183671" doubleValue];
CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
// 2.反地理编码
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error)
{
if (error||placemarks.count==0)
{
NSLog(@"你输入的地址没找到,可能在月球上");
}
else
{
// 显示最前面的地标信息
CLPlacemark *firstPlacemark= [placemarks firstObject];
// 详细地址名称
NSLog(@"反地理编码获取到的位置名称为: %@",firstPlacemark.name);
}
}];
}
二、WKWebView 的使用
![](https://img.haomeiwen.com/i9570900/299f9344e7474fa0.png)
![](https://img.haomeiwen.com/i9570900/3876b09fe8bf6d71.png)
![](https://img.haomeiwen.com/i9570900/0c07d016532cef34.png)
![](https://img.haomeiwen.com/i9570900/41db0850ad0e437c.png)
![](https://img.haomeiwen.com/i9570900/1d8ec650c25bf352.png)
![](https://img.haomeiwen.com/i9570900/206a5d9451a98fe3.png)
![](https://img.haomeiwen.com/i9570900/41420af6c6ab241e.png)
1、WKWebView 涉及的一些类
WKWebView:网页的渲染与展示
#import <WebKit/WebKit.h>
初始化
_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) configuration:config];
设置代理
// UI代理
_webView.UIDelegate = self;
// 导航代理
_webView.navigationDelegate = self;
是否允许手势左滑返回上一级,类似导航控制的左滑返回
_webView.allowsBackForwardNavigationGestures = YES;
可返回的页面列表,存储已打开过的网页
WKBackForwardList * backForwardList = [_webView backForwardList];
页面后退\前进\刷新
//页面后退
[_webView goBack];
//页面前进
[_webView goForward];
//刷新当前页面
[_webView reload];
加载本地html
文件
NSString *path = [[NSBundle mainBundle] pathForResource:@"JStoOC.html" ofType:nil];
NSString *htmlString = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[_webView loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
WKWebViewConfiguration:添加WKWebView配置信息
创建网页配置对象
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
创建设置对象
WKPreferences *preference = [[WKPreferences alloc] init];
config.preferences = preference;
默认为NO,表示是否允许不经过用户交互由javaScript
自动打开窗口
preference.javaScriptCanOpenWindowsAutomatically = YES;
是使用h5的视频播放器在线播放还是使用原生播放器全屏播放
config.allowsInlineMediaPlayback = YES;
设置视频是否需要用户手动播放 。设置为NO则会允许自动播放
config.mediaTypesRequiringUserActionForPlayback = YES;
设置是否允许画中画技术。在特定设备上有效
config.allowsPictureInPictureMediaPlayback = YES;
设置请求的User-Agent
信息中应用程序名称
config.applicationNameForUserAgent = @"LearnWKWebview";
自定义的WKScriptMessageHandler
。是为了解决内存不释放的问题
WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
用完记得移除注册的js
方法
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcNoPrams"];
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcWithPrams"];
WKUserContentController:这个类主要用来做native与JavaScript的交互管理
这个类主要用来做native
与JavaScript
的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
config.userContentController = wkUController;
注册一个name
为jsToOcNoPrams
的js
方法。设置处理接收JS
方法的对象
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcNoPrams"];
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcWithPrams"];
WKUserScript:用于进行JavaScript注入
以下代码适配文本大小
NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
用于进行JavaScript
注入
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[config.userContentController addUserScript:wkUScript];
WKScriptMessageHandler
这个协议类专门用来处理监听JavaScript
方法从而调用原生OC方法,和WKUserContentController
搭配使用。遵循WKScriptMessageHandler
协议,必须实现如下方法,然后把方法向外传递。通过接收JS传出消息的name
进行捕捉的回调方法。
被自定义的WKScriptMessageHandler
在回调方法里通过代理回调回来,绕了一圈就是为了解决内存不释放的问题。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
//用message.body获得JS传出的参数体
NSDictionary * parameter = message.body;
//JS调用OC
if([message.name isEqualToString:@"jsToOcNoPrams"]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js调用到了oc" message:@"不带参数" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}])];
[self presentViewController:alertController animated:YES completion:nil];
}else if([message.name isEqualToString:@"jsToOcWithPrams"]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js调用到了oc" message:parameter[@"params"] preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
}
2、WKWebView涉及的代理方法
WKNavigationDelegate :主要处理一些跳转、加载处理操作
页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
}
页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
[self.progressView setProgress:0.0f animated:NO];
}
当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[self getCookie];
}
提交发生错误时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
[self.progressView setProgress:0.0f animated:NO];
}
接收到服务器跳转请求即服务重定向时之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}
根据WebView
对于即将跳转的HTTP
请求头信息和相关信息来决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString * urlStr = navigationAction.request.URL.absoluteString;
NSLog(@"发送跳转请求:%@",urlStr);
//自己定义的协议头
NSString *htmlHeadString = @"github://";
if([urlStr hasPrefix:htmlHeadString]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"通过截取URL调用OC" message:@"你想前往我的Github主页?" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"打开" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSURL * url = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]];
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {
}];
}])];
[self presentViewController:alertController animated:YES completion:nil];
decisionHandler(WKNavigationActionPolicyCancel);
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}
根据客户端收到的服务器响应头以及response
相关信息来决定是否可以跳转。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSString * urlStr = navigationResponse.response.URL.absoluteString;
NSLog(@"当前跳转地址:%@",urlStr);
//允许跳转
decisionHandler(WKNavigationResponsePolicyAllow);
//不允许跳转
//decisionHandler(WKNavigationResponsePolicyCancel);
}
需要响应身份验证时调用 同样在block
中需要传入用户身份凭证
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
//用户身份信息
NSURLCredential * newCred = [[NSURLCredential alloc] initWithUser:@"userxiejiapei" password:@"xiejiapei" persistence:NSURLCredentialPersistenceNone];
//为 challenge 的发送方提供 credential
[challenge.sender useCredential:newCred forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
}
进程被终止时调用
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
}
WKUIDelegate :主要处理JS脚本,确认框,警告框等
web
界面中有弹出警告框时调用
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"HTML的弹出框" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
JavaScript
调用confirm
方法后回调的方法。confirm
是js中的确定框,需要在block
中把用户选择的情况传递进去。
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
JavaScript
调用prompt
方法后回调的方法。prompt
是js
中的输入框,需要在block
中把用户输入的信息传入。
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.text = defaultText;
}];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(alertController.textFields[0].text?:@"");
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
页面是弹出窗口_blank
处理
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
3、网页内容加载进度条和title的实现
添加监测网页加载进度的观察者
[self.webView addObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress)) options:0 context:nil];
[self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
kvo
监听进度,必须实现此方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))]
&& object == _webView) {
NSLog(@"网页加载进度 = %f",_webView.estimatedProgress);
self.progressView.progress = _webView.estimatedProgress;
if (_webView.estimatedProgress >= 1.0f) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.progressView.progress = 0;
});
}
} else if([keyPath isEqualToString:@"title"]
&& object == _webView){
self.navigationItem.title = _webView.title;
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
移除观察者
[_webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
[_webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(title))];
4、JS和OC的交互
JS调用OC
这个实现主要是依靠WKScriptMessageHandler
协议类和WKUserContentController
两个类:WKUserContentController
对象负责注册JS方法,设置处理接收JS方法的代理,代理遵守WKScriptMessageHandler
,实现捕捉到JS消息的回调方法,详情可以看第一步中对这两个类的介绍。
//这个类主要用来做native与JavaScript的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//注册一个name为jsToOcNoPrams的js方法,设置处理接收JS方法的代理
[wkUController addScriptMessageHandler:self name:@"jsToOcNoPrams"];
[wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"];
config.userContentController = wkUController;
遵守WKScriptMessageHandler
协议,代理是由WKUserContentControl
设置。通过接收JS传出消息的name
进行捕捉的回调方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
}
OC调用JS
changeColor()
是JS方法名,completionHandler
是异步回调block
。
NSString *jsString = [NSString stringWithFormat:@"changeColor('%@')", @"Js颜色参数"];
[_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {
NSLog(@"改变HTML的背景色");
}];
改变字体大小,调用原生JS方法
NSString *jsFont = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", arc4random()%99 + 100];
[_webView evaluateJavaScript:jsFont completionHandler:nil];
切换本地头像
NSString * path = [[NSBundle mainBundle] pathForResource:@"girl" ofType:@"png"];
NSString *jsPicture = [NSString stringWithFormat:@"changePicture('%@','%@')", @"pictureId",path];
[_webView evaluateJavaScript:jsPicture completionHandler:^(id _Nullable data, NSError * _Nullable error) {
NSLog(@"切换本地头像");
}];
5、Cookie 问题
解决页面内跳转(a标签等)还是取不到cookie
的问题
- (void)getCookie{
//取出cookie
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
//js函数
NSString *JSFuncString =
@"function setCookie(name,value,expires)\
{\
var oDate=new Date();\
oDate.setDate(oDate.getDate()+expires);\
document.cookie=name+'='+value+';expires='+oDate+';path=/'\
}\
function getCookie(name)\
{\
var arr = document.cookie.match(new RegExp('(^| )'+name+'=([^;]*)(;|$)'));\
if(arr != null) return unescape(arr[2]); return null;\
}\
function delCookie(name)\
{\
var exp = new Date();\
exp.setTime(exp.getTime() - 1);\
var cval=getCookie(name);\
if(cval!=null) document.cookie= name + '='+cval+';expires='+exp.toGMTString();\
}";
//拼凑js字符串
NSMutableString *JSCookieString = JSFuncString.mutableCopy;
for (NSHTTPCookie *cookie in cookieStorage.cookies) {
NSString *excuteJSString = [NSString stringWithFormat:@"setCookie('%@', '%@', 1);", cookie.name, cookie.value];
[JSCookieString appendString:excuteJSString];
}
//执行js
[_webView evaluateJavaScript:JSCookieString completionHandler:nil];
}
解决第一次进入cookie
丢失问题
- (NSString *)readCurrentCookieWithDomain:(NSString *)domainStr{
NSHTTPCookieStorage * cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSMutableString * cookieString = [[NSMutableString alloc]init];
for (NSHTTPCookie*cookie in [cookieJar cookies]) {
[cookieString appendFormat:@"%@=%@;",cookie.name,cookie.value];
}
// 删除最后一个“;”
if ([cookieString hasSuffix:@";"]) {
[cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];
}
return cookieString;
}
网友评论