iOS - WKWebView的使用和长按手势识别二维码并保存
WKWebView的图片二维码使用:
1.长按手势识别二维码并保存 2.识别二维码跳转;不是链接显示内容点击网址跳转 3.解决url包含中文不能编码的问题 4.文字带链接网址,点击跳转 5.纯文本-文字html展示可拷贝,查询 6.解决html页面适配屏幕宽度的问题和保留源文件的格式 7.判断是web网页图片否存在二维码并进行识别
代码:
// 添加长按手势识别二维码 [self WKWebViewHandleLongPress:_detailWebView]; // 识别二维码跳转;不是链接显示内容点击网址跳转 if ([self.m_url hasPrefix:@"http"]) { // NSString *urlStr = [self.m_url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; // 解决url包含中文不能编码的问题 NSString *urlStr = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,(CFStringRef)self.m_url,(CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]",NULL,kCFStringEncodingUTF8)); NSURL *url = [NSURL URLWithString:urlStr]; NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url]; [request setHTTPShouldHandleCookies:YES]; [_detailWebView loadRequest:request]; } else { self.topTitleLabel.text = @"扫描结果"; QRCodeLabel = [[ZXLabel alloc]initWithFrame:CGRectMake(10, 10, SCREENWIDTH-20, 50) fontSize:16 text:@"" textColor:[UIColor colorWithHexString:@"#000000"] textAlignment:NSTextAlignmentLeft numberOfLines:0]; [_detailWebView addSubview:QRCodeLabel]; if ([self urlValidation:[NSString stringWithFormat:@"%@",self.m_url]]==YES) {//网址,点击跳转 [self textColour]; QRurlStr = [NSString stringWithFormat:@"http://%@",[NSString stringWithFormat:@"%@",self.m_url]]; } else { // 文字html可拷贝,查询 // "<html><head><meta charset='UTF-8'><meta name='viewport content=initial-scale=1,maximum-scale=1, minimum-scale=1'><meta name='apple-mobile-web-app-capable' content='yes'><meta name='apple-mobile-web-app-status-bar-style' content='black'><meta name='format-detection' content='telephone=no'><title></title><style>*{margin: 0;padding: 0;}html,body {width: 100%;height: 100%;}p {width: 90%;font-size: 16px;color: #333;text-align: justify;}</style></head><body><p></p ></body></html>" // @"<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'><meta name='viewport' content='width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no'><style>*{margin:5px;padding:0px;}</style><title></title></head<body><pre>%@</pre></body></html>" // @"<html><head><meta charset='UTF-8'><meta name='viewport' content='initial-scale=1,maximum-scale=1, minimum-scale=1'><meta name='apple-mobile-web-app-capable' content='yes'><meta name='apple-mobile-web-app-status-bar-style' content='black'><meta name='format-detection' content='telephone=no'><title></title><style>*{margin: 0;padding: 0;box-sizing: border-box;}p {width: 6.4rem;padding: .2rem;font-size: .2rem;color: #333;text-align: justify;}</style></head><body><p>%@</p ></body><script>(function() { function getViewPort() {if(document.compatMode == 'BackCompat') { return {width: document.body.clientWidth,height: document.body.clientHeight}; } else {return {width: document.documentElement.clientWidth,height: document.documentElement.clientHeight};} }function screenZoom() {var _obj = getViewPort(); var Width = _obj.width;var Height = _obj.height;if (Width>640) { Width = 640;}document.documentElement.style.fontSize = Width/6.4 + 'px';}screenZoom();window.onresize = function() {screenZoom();};})();</script></html>" // 解决html页面适配屏幕宽度的问题和保留源文件的格式 QRurlStr = [NSString stringWithFormat: @"<html><head><meta charset='UTF-8'><meta name='viewport' content='initial-scale=1,maximum-scale=1, minimum-scale=1'><meta name='apple-mobile-web-app-capable' content='yes'><meta name='apple-mobile-web-app-status-bar-style' content='black'><meta name='format-detection' content='telephone=no'><title></title><style>*{margin: 0;padding: 0;box-sizing: border-box;}pre {/* width: 6.4rem; */padding: 10px;font-size: 15px;color: #333;text-align: justify;white-space: pre-wrap; /*css-3*/white-space: -moz-pre-wrap; /*Mozilla,since1999*/ white-space: -pre-wrap; /*Opera4-6*/white-space: -o-pre-wrap; /*Opera7*/word-wrap: break-word; /*InternetExplorer5.5+*/ }</style></head><body><pre>%@</pre></body><script>(function() { function getViewPort() {if(document.compatMode == 'BackCompat') { return {width: document.body.clientWidth,height: document.body.clientHeight}; } else {return {width: document.documentElement.clientWidth,height: document.documentElement.clientHeight};} }function screenZoom() {var _obj = getViewPort(); var Width = _obj.width;var Height = _obj.height;if (Width>640) { Width = 640;}document.documentElement.style.fontSize = Width/6.4 + 'px';}screenZoom();window.onresize = function() {screenZoom();};})();</script></html>",[NSString stringWithFormat:@"%@",self.m_url]]; [_detailWebView loadHTMLString:QRurlStr baseURL:Nil]; } } #pragma mark -- 识别图中二维码 // app内部识别二维码 /** * 网址正则验证 * * @param string 要验证的字符串 * * @return 返回值类型为BOOL */ - (BOOL)urlValidation:(NSString *)string { NSError *error; NSString *regulaStr = @"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$"; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regulaStr options:NSRegularExpressionCaseInsensitive error:&error]; NSArray *arrayOfAllMatches = [regex matchesInString:string options:0 range:NSMakeRange(0, [string length])]; for (NSTextCheckingResult *match in arrayOfAllMatches){ NSString* substringForMatch = [string substringWithRange:match.range]; NSLog(@"匹配--%@",substringForMatch); return YES; } return NO; } - (void)textColour { NSMutableAttributedString *abs = [[NSMutableAttributedString alloc]initWithString:[NSString stringWithFormat:@"%@",self.m_url]]; [abs beginEditing]; //字体大小 // [abs addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:20.0] range:NSMakeRange(0, 2)]; //字体颜色 [abs addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(0, [NSString stringWithFormat:@"%@",self.m_url].length)]; //下划线 [abs addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, [NSString stringWithFormat:@"%@",self.m_url].length)]; QRCodeLabel.attributedText = abs; UITapGestureRecognizer *LabelTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(capchaBtn:)]; QRCodeLabel.userInteractionEnabled = YES; [QRCodeLabel addGestureRecognizer:LabelTap]; } // 链接跳转 - (void)capchaBtn:(UITapGestureRecognizer *)sendr{ NSLog(@"跳转网页~~"); [QRCodeLabel removeFromSuperview]; NSString *urlStr = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,(CFStringRef)QRurlStr,(CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]",NULL,kCFStringEncodingUTF8)); NSURL *url =[NSURL URLWithString:urlStr]; NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url]; [request setHTTPShouldHandleCookies:YES]; [_detailWebView loadRequest:request]; } #pragma mark -- common WKWebView -(void)WKWebViewHandleLongPress:(WKWebView *)webView { UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(webViewHandleLongPress:)]; longPress.minimumPressDuration = 0.2; longPress.delegate = self; m_webView = webView; [webView addGestureRecognizer:longPress]; } - (void)webkitTouchCallout:(WKWebView *)webView { // 不执行前段界面弹出列表的JS代码 [webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:nil]; [webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil]; } // 是否允许支持多个手势,默认是不支持:NO - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ return YES; } // 网页内长按识别二维码 - (void)webViewHandleLongPress:(UILongPressGestureRecognizer *)sender{ if (sender.state == UIGestureRecognizerStateBegan) { CGPoint touchPoint = [sender locationInView:m_webView]; // 获取长按位置对应的图片url的JS代码 NSString *imgJS = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", touchPoint.x, touchPoint.y]; // 执行对应的JS代码 获取url [m_webView evaluateJavaScript:imgJS completionHandler:^(id _Nullable imgUrl, NSError * _Nullable error) { if (imgUrl) { NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgUrl]]; UIImage *image = [UIImage imageWithData:data]; if (!image) { NSLog(@"读取图片失败"); return; } _saveImage = image; // 禁用选中效果 [self webkitTouchCallout:m_webView]; if ([self isAvailableQRcodeIn:image]) { [self filterPopViewWithTag:100002 WithTitleArray:[NSMutableArray arrayWithObjects:@"保存图片",@"识别图中二维码",nil]]; } else { [self filterPopViewWithTag:100001 WithTitleArray:[NSMutableArray arrayWithObjects:@"保存图片",nil]]; } } else { // 选中效果 [m_webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='text'" completionHandler:nil]; [m_webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='text'" completionHandler:nil]; } }]; } } #pragma mark -- RomAlertViewDelegate 弹框识别图中二维码 // 判断是web网页图片否存在二维码 - (BOOL)isAvailableQRcodeIn:(UIImage *)img { //方法:一 // UIGraphicsBeginImageContextWithOptions(img.size, NO, 3);//0,获取当前屏幕分辨率[UIScreen mainScreen].scale // CGContextRef context = UIGraphicsGetCurrentContext(); // [self.view.layer renderInContext:context]; // UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // UIGraphicsEndImageContext(); //方法:二 UIImage *image = [self snapshot:self.view]; //方法:三 // UIImage *image = [self imageByInsetEdge:UIEdgeInsetsMake(-20, -20, -20, -20) withColor:[UIColor lightGrayColor] withImage:img]; CIImage *ciImage = [[CIImage alloc] initWithCGImage:image.CGImage options:nil]; CIContext *ciContext = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer : @(YES)}]; // 软件渲染 CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:ciContext options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];// 二维码识别 NSArray *features = [detector featuresInImage:ciImage]; if (features.count > 0) { // for (CIQRCodeFeature *feature in features) { // NSLog(@"qrCodeUrl = %@",feature.messageString); // 打印二维码中的信息 // qrCodeUrl = feature.messageString; // } CIQRCodeFeature *feature = [features objectAtIndex:0]; qrCodeUrl = [feature.messageString copy]; NSLog(@"二维码信息:%@", qrCodeUrl); return YES; } else { NSLog(@"图片中没有二维码"); return NO; } } // you can also implement by UIView category - (UIImage *)snapshot:(UIView *)view { UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 3);//view.bounds.size, YES, view.window.screen.scale if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]; } UIImage* image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } // you can also implement by UIImage category - (UIImage *)imageByInsetEdge:(UIEdgeInsets)insets withColor:(UIColor *)color withImage:(UIImage *)image { CGSize size = image.size; size.width -= insets.left + insets.right; size.height -= insets.top + insets.bottom; if (size.width <= 0 || size.height <= 0) { return nil; } CGRect rect = CGRectMake(-insets.left, -insets.top, image.size.width, image.size.height); UIGraphicsBeginImageContextWithOptions(size, NO, image.scale); CGContextRef context = UIGraphicsGetCurrentContext(); if (color) { CGContextSetFillColorWithColor(context, color.CGColor); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height)); CGPathAddRect(path, NULL, rect); CGContextAddPath(context, path); CGContextEOFillPath(context); CGPathRelease(path); } [image drawInRect:rect]; UIImage *insetEdgedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return insetEdgedImage; } // 网页内部识别二维码 - (void)alertview:(RomAlertView *)alertview didSelectWebRowAtIndexPath:(NSIndexPath *)indexPath { if (alertview.tag == 100001) { if ([alertview.otherTitles[indexPath.row] isEqualToString:@"保存图片"]) { NSLog(@"保存图片"); // UIImageWriteToSavedPhotosAlbum(_saveImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); [self saveWebLongPressed]; } } else if (alertview.tag == 100002) { if ([alertview.otherTitles[indexPath.row] isEqualToString:@"保存图片"]) { NSLog(@"保存图片"); // UIImageWriteToSavedPhotosAlbum(_saveImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); [self saveWebLongPressed]; }else if ([alertview.otherTitles[indexPath.row] isEqualToString:@"识别图中二维码"]){ NSLog(@"识别图中二维码"); ADWebViewViewController *controller = [[ADWebViewViewController alloc] init]; controller.m_url = qrCodeUrl; controller.hidesBottomBarWhenPushed = YES; [self.navigationController pushViewController:controller animated:YES]; } } } //- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{ // NSString *message = @"Succeed"; // if (error) { // message = @"Fail"; // } // NSLog(@"save result :%@", message); //} #pragma mark --web保存图片 //保存 - (void)saveWebLongPressed { // if (webPhotoSave == YES) { // 图片已经保存到相册 提示 // [self.view makeToast:@"该图片已经保存到相册" duration:2 position:CSToastPositionCenter]; // return; // } [self saveWebPhoto]; } - (void)saveWebPhoto { PHAuthorizationStatus oldStatus = [PHPhotoLibrary authorizationStatus]; [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { dispatch_async(dispatch_get_main_queue(), ^{ switch (status) { case PHAuthorizationStatusAuthorized: { // 保存图片到相册 [self saveWebImageIntoAlbum]; break; } case PHAuthorizationStatusDenied: { if (oldStatus == PHAuthorizationStatusNotDetermined) return; NSLog(@"提醒用户打开相册的访问开关"); UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"无法保存" message:@"请在iPhone的“设置-隐私-照片”选项中,允许访问你的照片。" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alert show]; break; } case PHAuthorizationStatusRestricted: { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"无法保存" message:@"因系统原因,无法访问相册!" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alert show]; break; } default: break; } }); }]; } // 获得刚才添加到【相机胶卷】中的图片 - (PHFetchResult<PHAsset *> *)createdAssets { __block NSString *createdAssetId = nil; // 添加图片到【相机胶卷】 [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ createdAssetId = [PHAssetChangeRequest creationRequestForAssetFromImage:_saveImage].placeholderForCreatedAsset.localIdentifier; } error:nil]; if (createdAssetId == nil) return nil; // 在保存完毕后取出图片 return [PHAsset fetchAssetsWithLocalIdentifiers:@[createdAssetId] options:nil]; } //获得【自定义相册】 -(PHAssetCollection *)createdCollection { // 获取软件的名字作为相册的标题(如果需求不是要软件名称作为相册名字就可以自己把这里改成想要的名称) NSString *title = [NSBundle mainBundle].infoDictionary[(NSString *)kCFBundleNameKey]; // 获得所有的自定义相册 PHFetchResult<PHAssetCollection *> *collections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; for (PHAssetCollection *collection in collections) { if ([collection.localizedTitle isEqualToString:title]) { return collection; } } // 代码执行到这里,说明还没有自定义相册 __block NSString *createdCollectionId = nil; // 创建一个新的相册 [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ createdCollectionId = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title].placeholderForCreatedAssetCollection.localIdentifier; } error:nil]; if (createdCollectionId == nil) return nil; // 创建完毕后再取出相册 return [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[createdCollectionId] options:nil].firstObject; } //保存图片到相册 - (void)saveWebImageIntoAlbum { // 获得相片 PHFetchResult<PHAsset *> *createdAssets = self.createdAssets; // 获得相册 PHAssetCollection *createdCollection = self.createdCollection; if (createdAssets == nil || createdCollection == nil) { [self.view makeToast:@"图片保存失败!" duration:2 position:CSToastPositionCenter]; return; } // 将相片添加到相册 NSError *error = nil; [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:createdCollection]; [request insertAssets:createdAssets atIndexes:[NSIndexSet indexSetWithIndex:0]]; } error:&error]; // 保存结果 NSString *msg = nil ; if(error){ msg = @"图片保存失败!"; [self.view makeToast:msg duration:2 position:CSToastPositionCenter]; }else{ msg = @"已成功保存到系统相册"; // webPhotoSave = YES; [self.view makeToast:msg duration:2 position:CSToastPositionCenter]; } }
PS:WKWebView官网
天行健,君子以自强不息;地势坤,君子以厚德载物!