WKWebView之长按手势
PAWebView
PAWebView 之 cookie 管理与同步;
PAWebView 之长按手势;
PAWebView 之POST请求;
一、引言
在hybird app 里,长按手势的运用非常广泛,特别是DOM的运用与native和JS交互的实现,使得Navitve对HTML的操作无所不能。如查看、屏蔽、替换、复制、下载、识别二维码和分享等功能都可以轻松实现,那么长按手势更是可以实现让用户便捷的一些功能。
二、WKWebView长按手势
1. WebView触摸事件
绝大多WebView层的触摸事件暂时还不能由Native直接拦截更改,目前Native可以监听长按、点击等一些手势状态,但是如果需要禁止或改变这些事件或属性,我们可以通过向H5注入JS代码来实现,iOS WebKit 框架下的CSS扩展也可以实现部分功能
2.关闭网页的长按手势
利用WebKit CSS 扩展 webkitTouchCallout属性实现。
document.documentElement.style.webkitTouchCallout=’none’;
错误用法:
一些开发者在webViewDidFinishLoad 代理中实现该方法。实际上,经过测试,这种调用方法不能完全禁止WebView端的长按手势,估计是网页加载过慢时方法注入延时导致WebView端长按依然有效。
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// Disable callout
[webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
正确用法:
在初始化WebView时使用WKUserContentController对象注入禁止长按的方法。
NSMutableString *javascript = [NSMutableString string];
[javascript appendString:@"document.documentElement.style.webkitTouchCallout='none';"];//禁止长按
WKUserScript *noneSelectScript = [[WKUserScript alloc] initWithSource:javascript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[_config.userContentController addUserScript:noneSelectScript];
3.WebView添加手势
- webview添加事件
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(startLongPress:)];
longPress.delegate = self;
longPress.minimumPressDuration = 0.4f;
longPress.numberOfTouchesRequired = 1;
longPress.cancelsTouchesInView = YES;
[self addGestureRecognizer:longPress];
- 实现UILongPressGestureRecognizer代理,精确处理手势动作
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
NSLog(@"%@",otherGestureRecognizer.class);
if ([otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
//只有当手势为长按手势时反馈,飞长按手势将阻止。
return YES;
}else{
return NO;
}
}
- 长按反馈
- (void)startLongPress:(UILongPressGestureRecognizer *)pressSender
{
if(pressSender.state == UIGestureRecognizerStateBegan){
//实现相关功能
[self detectInWebView:pressSender];
NSLog(@"1. 开始长按手势");
}else if(pressSender.state == UIGestureRecognizerStateEnded){
//可以添加你长按手势执行的方法,不过是在手指松开后执行
NSLog(@"2. 结束长按手势");
}else if(pressSender.state == UIGestureRecognizerStateChanged){
//在手指点下去一直不松开的状态执行
NSLog(@"3. 长按手势改变");
}
}
4.解决长按结束时a对应URL超链接触发跳转问题
查阅了很多国内技术论坛资料,这个问题普遍都困扰着开发者。我研究了一下系统自带的长按手势的功能,当长按触发时,WebView 的手势响应事件被全部终止,然后弹出UIMenuController视图,UC也是类似的处理。目前不清楚怎么实现,知道的伙伴交流一下。但是可以使用其他方法来代替。
方法一:
以弹出的视图作为长按触发成功的标志,视图弹出,长按触发,然后在WKWebView的代理方法 decidePolicyForNavigationAction 里拦截网页跳转;视图消失,长按完毕。方法虽笨,但是还是很实用,目前的很多开发者都采用这种方式实现。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:
(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
if (_longpress) {
_longpress = NO;
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}
方法二
借助系统自带的长按响应机制忽略单击事件。去掉document.documentElement.style.webkitTouchCallout=’none’;设置。
移除UIMenuController清除系统的长按响应形式。弹出我们自定义的弹框形式。
移除视图的方法
for (UIView* subview in self.webViewWK.scrollView.subviews)
{
if ([subview isKindOfClass:NSClassFromString(@"WKContentViewMinusAccessoryView")])
{
for (UIGestureRecognizer* longPress in subview.gestureRecognizers)
{
[subview removeGestureRecognizer:longPress];
}
}
}
5.抓取/更改网页元素属性
抓取H5主要使用了WKWebView的JS代码注入和DOM的H5文件操作来实现。
JS代码注入方法。届时除了了解JS代码注入的实现,还需要了解JavaScript/jQuery,HTML,DOM的相关知识。
相关教程:
JS:http://www.w3school.com.cn/js/js_library_jquery.asp
HTML:http://www.w3school.com.cn/html5/index.asp
DOM:http://www.w3school.com.cn/htmldom/dom_intro.asp
WKWebView框架注入代码API
[self evaluateJavaScript:JSFunction completionHandler:^(id _Nullable href, NSError * _Nullable error)
{}];
相关JS方法
1.获取点击位置的url
function JSSearchHref(x,y) {
var e = document.elementFromPoint(x, y);
while(e){
if(e.href){
return e.href;
}
e = e.parentElement;
}
return e.href;
}
2.获取标题
function JSSearchText(x,y) {
return document.elementFromPoint(x, y).innerText;
}
3.获取图片
function JSSearchImage(x,y) {
return document.elementFromPoint(x, y).src;
}
4.获取HTML所有的图片
function JSSearchAllImage(){
var img = [];
for(var i=0;i<$('img').length;i++){
if(parseInt($('img').eq(i).css('width'))>60){
//获取所有符合放大要求的图片,将图片路径(src)获取
img[i] = $('img').eq(i).attr('src');
}
}
var img_info = {};
img_info.list = img; //保存所有图片的url
return img;
}
5.获取web page的宽
document.getElementById('content').offsetWidth
6.获取web page的长
document.getElementById('content').offsetHeight
等等。。
JS注入使用例子
/** 获取HTML所有的图片 */
NSString* const JSSearchAllImageFromHtml =
@"function JSSearchAllImage(){\
var img = [];\
for(var i=0;i<$('img').length;i++){\
if(parseInt($('img').eq(i).css('width'))>60){ \
//获取所有符合放大要求的图片,将图片路径(src)获取
img[i] = $('img').eq(i).attr('src');\
}\
}\
var img_info = {};\
img_info.list = img;\ //保存所有图片的url
return img;\
};
//注入JS方法
NSString *hrefJS = JSSearchAllImageFromHtml;
[self evaluateJavaScript:hrefJS completionHandler:nil];
//调用JS方法
NSString *hrefFunc = [NSString stringWithFormat:@"JSSearchAllImage();"];
[self evaluateJavaScript:hrefFunc completionHandler:^(id _Nullable image, NSError * _Nullable error)
{
NSArray *imageArray = [self sortImageFromArray:image];
NSLog(@"%@",imageArray);
}];
6.具体功能的实现
PAWebView里面具体实现了该功能。具体代码和功能的实现请查看 github :https://github.com/llyouss/PAWeView