.H typedef NS_ENUM(int, FromeType) { From_MainShop = 1, From_Category = 2 }; @interface WKWebVC : BaseVC @property (nonatomic, strong) NSString *url; @property (nonatomic, strong) NSString *titleText; @property (nonatomic, assign) BOOL isShippingStatus; @property (nonatomic, assign) FromeType fromType; @property (nonatomic, copy) void(^checkMySizeResult)(NSString *size); @property (nonatomic, assign) BOOL isTabVC; @end .M @interface WKWebVC () <WKNavigationDelegate, FBSDKSharingDelegate, LoginDelegate, UILoadingViewDelegate, WKScriptMessageHandler> @property (nonatomic, strong) UILoadingView *loadingView; @property (weak, nonatomic) IBOutlet UILabel *titleLb; @property (weak, nonatomic) IBOutlet UIImageView *titleDiliver; @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *indicator; @property (nonatomic, strong) WKUserContentController *userContentController; @property (weak, nonatomic) IBOutlet UIButton *backBtn; @property (nonatomic, strong) WKWebView *webView; @end - (void)viewDidLoad { [super viewDidLoad]; [self.indicator setHidesWhenStopped:YES]; self.titleLb.text=self.titleText; //配置信息 WKWebViewConfiguration *config=[[WKWebViewConfiguration alloc] init]; config.preferences=[[WKPreferences alloc]init]; // config.preferences.minimumFontSize = 10; config.preferences.javaScriptEnabled = true; // 默认是不能通过JS自动打开窗口的,必须通过用户交互才能打开 config.preferences.javaScriptCanOpenWindowsAutomatically =true; // 通过js与webview内容交互配置 self.userContentController = [[WKUserContentController alloc]init]; config.userContentController = self.userContentController; // 添加一个名称,在JS通过这个名称发送消息 [config.userContentController addScriptMessageHandler:self name:@"theyub"]; WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, ZKScreenW, ZKScreenH) configuration:config]; webView.navigationDelegate = self; [self.view addSubview:webView]; self.webView = webView; self.loadingView=[UILoadingView instanceViewWithSuperView:self.view delegate:self]; self.loadingView.delegate=self; [self.view bringSubviewToFront:self.indicator]; [webView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.titleDiliver.mas_bottom); make.leading.equalTo(self.view.mas_leading); make.trailing.equalTo(self.view.mas_trailing); if (self.isTabVC) { make.bottom.equalTo(self.view.mas_bottom).with.offset(-49); } else { make.bottom.equalTo(self.view.mas_bottom); } }]; // 获取 url if (self.isTabVC) { [self getUrlRequest]; } else { HttpManager * httpManager = [HttpManager sharedManager]; id<GAITracker> tracker = [[GAI sharedInstance] trackerWithTrackingId:GAP_TRACKING_ID]; NSString *clientid = [tracker get:kGAIClientId]; self.url = [NSString stringWithFormat:@"%@?app_token=%@&userId=%d&cid=%@&app_equip=ios&language=%@&siteUID=%@¤cy=%@&device_type=ios",self.url,httpManager.meInfo.token,httpManager.meInfo.member_id,clientid,[PhoneUtil getCurrentLanguage], [httpManager getCurrentSiteUid], [FileUtil getCurrency].code]; // @"http://www.ifeng.com"; NSURL *url=[NSURL URLWithString:self.url]; [self.webView loadRequest:[self headForRequest:url]]; [self.loadingView setHidden:YES]; } if (self.titleText.length <= 0) { // 添加KVO监听网页Title 变化 [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:@"WKWebVC"]; } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if (self.titleText.length <=0 ) { // 如果 没有标题 取网页的标题 if ([keyPath isEqualToString:@"title"]) { self.titleLb.text = self.webView.title; } } } // 页面已经消失的时候 移除KVO和命名空间 - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; if (!self.isTabVC) { NSArray *vcs = self.navigationController.viewControllers; if (vcs.count > 0) { UIViewController *lastVC = [vcs lastObject]; if ([lastVC isKindOfClass:[self class]]) { [self removeScriptAndObserver]; } } else { // 页面已经消失 [self removeScriptAndObserver]; } } } - (void)removeScriptAndObserver { @try { [self.userContentController removeScriptMessageHandlerForName:@"theyub"]; [self.webView removeObserver:self forKeyPath:@"title" context:@"WKWebVC"]; } @catch (NSException *exception) { NSLog(@"========%@", exception); } @finally {} } - (IBAction)popVC:(id)sender { if (self.webView.canGoBack) { [self.webView goBack]; return; } [self.navigationController popViewControllerAnimated:YES]; } - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { NSLog(@"=============%@", message.body); NSString *jsonString = [NSString stringWithFormat:@"%@", message.body]; if (jsonString.length > 0) { NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; NSLog(@"=============%@", dict); NSString *method = parseStringFromDict(@"method", dict); // 根据方法名判断需要执行的方法 if ([method isEqualToString:@"webToMobile"]) { [self webToMobile:jsonString]; } else if ([method isEqualToString:@"webToMobileAction"]) { [self webToMobileAction:jsonString]; } } } // 获取URL - (void)getUrlRequest { ExclusiveHttpRequest *req = [[ExclusiveHttpRequest alloc] init]; [req getExclusiveRequstCallback:^(BOOL success, NSObject *result, NSString *errorMsg) { if (success) { self.url = (NSString *)result; if (self.url .length > 0) { HttpManager * httpManager = [HttpManager sharedManager]; id<GAITracker> tracker = [[GAI sharedInstance] trackerWithTrackingId:GAP_TRACKING_ID]; NSString *clientid = [tracker get:kGAIClientId]; self.url = [NSString stringWithFormat:@"%@?app_token=%@&userId=%d&cid=%@&app_equip=ios&language=%@&siteUID=%@¤cy=%@&device_type=ios",self.url,httpManager.meInfo.token,httpManager.meInfo.member_id,clientid,[PhoneUtil getCurrentLanguage], [httpManager getCurrentSiteUid], [FileUtil getCurrency].code]; NSURL *url=[NSURL URLWithString:self.url]; [self.webView loadRequest:[self headForRequest:url]]; [self.loadingView setHidden:YES]; } else { [self.loadingView showErrorView]; } } else { [self.loadingView showErrorView]; [UIToastUtil showToast:self.view message:errorMsg]; } }]; } //返回一个urlRequest 对象, - (NSMutableURLRequest*)headForRequest:(NSURL*)url{ NSMutableURLRequest *request=[[NSMutableURLRequest alloc] initWithURL:url]; NSString *preferredLang = [PhoneUtil getCurrentLanguage]; if([preferredLang hasPrefix:@"zh-Hant"]){ if([[[HttpManager sharedManager] getLocalCountry] isOrderedSameString:@"TW"]){ preferredLang=@"zh-tw"; }else if([[[HttpManager sharedManager] getLocalCountry] isOrderedSameString:@"HK"]){ preferredLang=@"zh-hk"; }else{ preferredLang=@"zh-tw"; } } HttpManager * httpManager = [HttpManager sharedManager]; [request addValue:[FileUtil getCurrency].code forHTTPHeaderField:@"Currency"]; [request addValue:preferredLang forHTTPHeaderField:@"Language"]; [request addValue:@"iPhone" forHTTPHeaderField:@"Devtype"]; [request addValue:[HttpManager sharedManager].UUID forHTTPHeaderField:@"dev_id"]; [request setValue:@"app" forHTTPHeaderField:@"SiteUID"]; if([httpManager getLocalCountry]){ [request setValue:[httpManager getLocalCountry] forHTTPHeaderField:@"LocalCountry"]; } if([HttpManager sharedManager].meInfo && [HttpManager sharedManager].meInfo.member_id){ [request setValue:[NSString stringWithFormat:@"%d",[HttpManager sharedManager].meInfo.member_id] forHTTPHeaderField:@"NewUid"]; } [request setValue:[NSString stringWithFormat:@"%@ %@%@",[[[PhoneUtil alloc] init] getDevice],@"ios",[[UIDevice currentDevice] systemVersion]] forHTTPHeaderField:@"Device"]; [request setValue:@"shein app" forHTTPHeaderField:@"AppName"]; [request setValue:@"shein" forHTTPHeaderField:@"AppType"]; [request setValue:[PhoneUtil getAppVersion] forHTTPHeaderField:@"AppVersion"]; [request setValue:[FileUtil getCurrency].code forHTTPHeaderField:@"AppCurrency"]; [request setValue:[PhoneUtil getCountry] forHTTPHeaderField:@"AppCountry"]; [request setValue:[PhoneUtil getNetWorkStates] forHTTPHeaderField:@"network-type"]; [request setValue:preferredLang forHTTPHeaderField:@"AppLanguage"]; if([HttpManager sharedManager].meInfo!=nil&&[HttpManager sharedManager].meInfo.token!=nil){ [request addValue:[HttpManager sharedManager].meInfo.token forHTTPHeaderField:@"token"]; } return request; } // 重新加载 - (void)didTryAgain { if (self.isTabVC) { [self getUrlRequest]; } else { [self.webView reload]; } } // 页面开始加载时调用 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation { [self.indicator startAnimating]; } // 内容开始加载. 等同于 UIWebViewDelegate: - webViewDidStartLoad: - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation { // [self.indicator startAnimating]; } // 页面加载完成。 等同于 UIWebViewDelegate: - webViewDidFinishLoad: - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { [self.indicator stopAnimating]; [self.loadingView setHidden:YES]; } // 页面加载失败调用 ---- 好像是网络原因失败 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { if (self.isTabVC) { [self.loadingView showErrorView]; } [self.indicator stopAnimating]; } // 页面加载失败。 等同于 UIWebViewDelegate: - webView:didFailLoadWithError: - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { NSLog(@"===============withError"); [self.indicator stopAnimating]; } #pragma mark - 交互通用方法 ----------------------- - (void)webToMobile:(NSString *)json { NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; if(dict){ if (![NSThread isMainThread]) { return; } NSString *checkMySize = parseStringFromDict(@"checkMySize", dict); NSString *link = parseStringFromDict(@"shareLink", dict); NSString *popVC = parseStringFromDict(@"popVC", dict); if (checkMySize.length > 0) { // check my size if (self.checkMySizeResult) { self.checkMySizeResult(checkMySize); } [self.navigationController popViewControllerAnimated:YES]; } else if (link.length > 0) { // 分享 [[VAShareTool alloc ] shareInViewConroller:self withTitle:@"" description:@"" url:link imageUrl:link image:nil completionHandler:^(UIActivityType _Nullable activityType, BOOL completed, NSArray * _Nullable returnedItems, NSError * _Nullable activityError) { if (completed) { NSLog(@"completed"); } else { NSLog(@"cancel"); } }]; } else if ([popVC isEqualToString:@"1"]) { [self.navigationController popViewControllerAnimated:YES]; } } } #pragma mark - 交互新增行为方法 ----------------------- - (void)webToMobileAction:(NSString *)json { NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; if(dict){ if ([NSThread isMainThread]) { [self pushActionWithDict:dict]; } else { dispatch_async(dispatch_get_main_queue(), ^{ [self pushActionWithDict:dict]; }); } } } //WKNavigationDelegate - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSString *actionType = navigationAction.request.URL.host; if([actionType isEqualToString:@"WebBlogVC.shareDownloadYub"]){//分享app [GaUtil addMe:@"BACK BLOGGER" label:nil]; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://m.theyub.com"]]; NSArray *objectsToShare = @[url]; UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil]; NSArray *excludedActivities = @[UIActivityTypePrint, UIActivityTypeCopyToPasteboard, UIActivityTypeSaveToCameraRoll, UIActivityTypeAddToReadingList]; controller.excludedActivityTypes = excludedActivities; [self presentViewController:controller animated:YES completion:nil]; } // if([request.URL.absoluteString isEqualToString:@"about:blank"]&&navigationType==2){//如果是返回,且是空白页面,则从新刷新页面 // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // [self.webView reload]; // }); // } else if (self.isTabVC && navigationAction.navigationType == WKNavigationTypeLinkActivated) { if (navigationAction.request.URL.absoluteString && ![navigationAction.request.URL.absoluteString containsString:self.url]) { UIStoryboard *sb=[UIStoryboard storyboardWithName:@"Setting" bundle:[NSBundle mainBundle]]; WebViewVC *webVC = [sb instantiateViewControllerWithIdentifier:@"WebViewVC"]; webVC.url = navigationAction.request.URL.absoluteString; [self.navigationController pushViewController:webVC animated:YES]; decisionHandler(WKNavigationActionPolicyCancel); return; } } decisionHandler(WKNavigationActionPolicyAllow); } - (void)downloadImageWithUrl:(NSString *)urlStr { NSLog(@"回退----%@",urlStr); [[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString:[urlStr fixUrlSting]] options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize) { } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { if (finished && nil != image) { // PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { if (status == PHAuthorizationStatusRestricted || status == PHAuthorizationStatusDenied) { //无权限 dispatch_async(dispatch_get_main_queue(), ^{ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; }); } else{ dispatch_async(dispatch_get_main_queue(), ^{ UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), (__bridge void *)self); }); } }]; } if (image == nil) { UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:[LocalizedUtil getStringByKey:@"string_key_1364"] preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:[LocalizedUtil getStringByKey:@"string_key_342"] style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self presentViewController:alert animated:YES completion:nil]; } }]; } - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { if (error == nil) { UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:[LocalizedUtil getStringByKey:@"string_key_1365"] preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:[LocalizedUtil getStringByKey:@"string_key_342"] style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self presentViewController:alert animated:YES completion:nil]; } else{ UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:getStringByKey(@"string_key_1364") preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:[LocalizedUtil getStringByKey:@"string_key_342"] style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; [self presentViewController:alert animated:YES completion:nil]; } NSLog(@"image = %@, error = %@, contextInfo = %@", image, error, contextInfo); } - (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results{ NSLog(@"didCompleteWithResults"); } - (void)sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error{ NSLog(@"didFailWithError,%@",error); if(error==nil||[[NSNull null] isEqual:error]){ [UIToastUtil showToast:self.view message:@"you have not install facebook app."]; } } - (void)sharerDidCancel:(id<FBSDKSharing>)sharer{ NSLog(@"share..."); } #pragma mark - 登录 代理 -------------- - (void)loginSuccess:(MeInfo*)meInfo { } - (void)registSuccess:(MeInfo*)meInfo { } #pragma mark - 页面跳转 ------------------- - (void)pushActionWithDict:(NSDictionary *)dict { NSString *type_id = parseStringFromDict(@"type_id", dict); NSString *tid = parseStringFromDict(@"tid", dict); NSString *category_type = parseStringFromDict(@"category_type", dict); NSString *title = parseStringFromDict(@"title", dict); NSString *popVC = parseStringFromDict(@"popVC", dict); if (type_id.length > 0) { if ([type_id isEqualToString:@"1"]) { //虚拟分类 UIStoryboard *sb=[UIStoryboard storyboardWithName:@"Promotions" bundle:[NSBundle mainBundle]]; PromotionGoodsListVC *vc=[sb instantiateViewControllerWithIdentifier:@"PromotionGoodsListVC"]; vc.virtual_category_id = tid; vc.navTitle = title; // vc.siteFrom = @"shop "; vc.categoryType = 1; [self.navigationController pushViewController:vc animated:YES]; } else if ([type_id isEqualToString:@"2"]) { // 真实分类 if ([category_type isEqualToString:@"1"]) { // 一级分类 UIStoryboard *sb=[UIStoryboard storyboardWithName:@"Category" bundle:[NSBundle mainBundle]]; CategoryAllGoodsListVC *vc=[sb instantiateViewControllerWithIdentifier:@"CategoryAllGoodsListVC"]; vc.navTitle = title; vc.category = @""; vc.cat_id = tid; // vc.siteFrom = @"shop "; [self.navigationController pushViewController:vc animated:YES]; } else if ([category_type isEqualToString:@"2"]) { // 二级分类 UIStoryboard *sb=[UIStoryboard storyboardWithName:@"Category" bundle:[NSBundle mainBundle]]; CategoryGoodsListVC *vc=[sb instantiateViewControllerWithIdentifier:@"CategoryGoodsListVC"]; vc.navTitle = title; vc.category = @""; vc.cat_id = tid; // vc.siteFrom = @"shop "; [self.navigationController pushViewController:vc animated:YES]; } } else if ([type_id isEqualToString:@"11"]) { // 跳登录 UIStoryboard *sb=[UIStoryboard storyboardWithName:@"Login" bundle:[NSBundle mainBundle]]; LoginVC *vc=[sb instantiateViewControllerWithIdentifier:@"LoginVC"]; vc.delegate=self; [self presentViewController:vc animated:YES completion:NULL]; if([tid isEqualToString:@"1101"]){ // 登录 vc.doAction=1; } else{ // 注册 vc.doAction=2; } [self presentViewController:vc animated:YES completion:NULL]; } else if ([type_id isEqualToString:@"12"]) { // 商品详情 GoodsInfo *goodsInfo=[[GoodsInfo alloc] init]; goodsInfo.goods_id=[tid intValue]; UIStoryboard *sb=[UIStoryboard storyboardWithName:@"Goods" bundle:[NSBundle mainBundle]]; GoodsDetailVC *vc=[sb instantiateViewControllerWithIdentifier:@"GoodsDetailVC"]; vc.goodsInfo=goodsInfo; [self.navigationController pushViewController:vc animated:YES]; } } else if (popVC.length > 0) { [self.navigationController popViewControllerAnimated:YES]; } } // 设置 WKWebview 滚动速率 需要使用scroll 代理来做 webView.scrollView.delegate = self; - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { scrollView.decelerationRate = UIScrollViewDecelerationRateNormal; }
//请求之前,决定是否要跳转:用户点击网页上的链接,需要打开新页面时,将先调用这个方法。 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler; //接收到相应数据后,决定是否跳转 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler; //页面开始加载时调用 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation; // 主机地址被重定向时调用 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation; // 页面加载失败时调用 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error; // 当内容开始返回时调用 - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation; // 页面加载完毕时调用 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation; //跳转失败时调用 - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error; // 如果需要证书验证,与使用AFN进行HTTPS证书验证是一样的 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler; //9.0才能使用,web内容处理中断时会触发 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
参考