美文网首页
IOS基础使用:CoreLocation、WKWebView

IOS基础使用:CoreLocation、WKWebView

作者: 时光啊混蛋_97boy | 来源:发表于2021-12-05 23:20 被阅读0次

原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的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、运行效果
用户授权
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、运行效果
磁力感应
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 的使用


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的交互管理

这个类主要用来做nativeJavaScript的交互管理

WKUserContentController * wkUController = [[WKUserContentController alloc] init];
config.userContentController = wkUController;

注册一个namejsToOcNoPramsjs方法。设置处理接收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方法后回调的方法。promptjs中的输入框,需要在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;
}

相关文章

网友评论

      本文标题:IOS基础使用:CoreLocation、WKWebView

      本文链接:https://www.haomeiwen.com/subject/uarwxrtx.html