├── LICENSE ├── README.md ├── WKWebView+QWFullScreenPopGesture.h └── WKWebView+QWFullScreenPopGesture.m /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Pandex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QWWebViewFullScreenPopGesture 2 | A WKWebView's category to enable fullscreen back/forward gesture in an iOS8+ system style with AOP. 3 | 0代码实现 WKWebView 滑动手势全屏返回和前进 4 | 5 | OverView 6 | == 7 | ![overview gif](https://s19.aconvert.com/convert/p3r68-cdx67/pg1p6-jwsh6.gif) 8 | 9 | 项目中同时用了 [forkingdog/FDFullscreenPopGesture](https://github.com/forkingdog/FDFullscreenPopGesture) 和 WKWebView。 10 | WKWebView是自带屏幕边缘滑动返回/前进手势的,为了统一交互体验, 11 | 我仿照 FDFullscreenPopGesture 为 WKWebView 写一个支持全屏滑动返回上一个页面的分类。 12 | 目前默认支持全屏右滑返回和全屏左滑前进 13 | 14 | 15 | 16 | Usage 17 | == 18 | 使用时只需要把文件分类加入工程即可,不需要做其他任何操作 19 | 20 | 想禁止个别 WKWebView 的手势可直接调用,不需要导入头文件     21 | ``` 22 | webView.allowsBackForwardNavigationGestures = NO; 23 | ``` 24 | 25 | 配合 FDFullscreenPopGesture 使用时要依赖手势 26 | ``` 27 | MYViewController.m 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | UIGestureRecognizer * navGesture = self.navigationController.fd_fullscreenPopGestureRecognizer; 32 | [navGesture requireGestureRecognizerToFail:self.webView.qw_fullscreenPopGestureRecognizer]; 33 | } 34 | ``` 35 | 36 | 支持 iOS 8+ 37 | -------------------------------------------------------------------------------- /WKWebView+QWFullScreenPopGesture.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKWebView+QWFullScreenPopGesture.h 3 | // ProCalendar 4 | // 5 | // Created by 505 on 2018/3/21. 6 | // Copyright © 2018年 Quickwis. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WKWebView (QWFullScreenPopGesture) 12 | 13 | @property (nonatomic, strong, readonly) UIPanGestureRecognizer *qw_fullscreenPopGestureRecognizer; 14 | @property (nonatomic, strong, readonly) UIPanGestureRecognizer *qw_fullscreenRightPopGestureRecognizer; 15 | 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /WKWebView+QWFullScreenPopGesture.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKWebView+QWFullScreenPopGesture.m 3 | // ProCalendar 4 | // 5 | // Created by Pandex on 2018/3/21. 6 | // Copyright © 2018年 Quickwis. All rights reserved. 7 | // 8 | 9 | #import "WKWebView+QWFullScreenPopGesture.h" 10 | #import 11 | 12 | @interface _QWFullScreenPopGestureDelegate : NSObject 13 | 14 | // 弱引用控件,避免循环引用 15 | @property (weak,nonatomic) WKWebView * webView; 16 | @property (weak,nonatomic) UIPanGestureRecognizer * leftGes; 17 | @property (weak,nonatomic) UIPanGestureRecognizer * rightGes; 18 | 19 | @end 20 | 21 | @implementation _QWFullScreenPopGestureDelegate 22 | 23 | - (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer{ 24 | //不可返回时忽略左侧手势 25 | if (![self.webView canGoBack] && [gestureRecognizer isEqual:self.leftGes]) { 26 | return NO; 27 | }else if (![self.webView canGoForward] && [gestureRecognizer isEqual:self.rightGes]){ 28 | //不可前进时忽略右侧手势 29 | return NO; 30 | } 31 | 32 | //禁止滑动手势时禁用 33 | if (!self.webView.allowsBackForwardNavigationGestures) { 34 | return NO; 35 | } 36 | 37 | //禁止水平向右以外的滑动 38 | CGPoint translation = [gestureRecognizer translationInView:self.webView]; 39 | CGFloat absX = fabs(translation.x); 40 | CGFloat absY = fabs(translation.y); 41 | 42 | if (absX > absY ) { //水平滑动 43 | if (translation.x < 0 && [gestureRecognizer isEqual:self.leftGes]) { 44 | //禁止左侧手势向左滑动 45 | return NO; 46 | }else if (translation.x > 0 && [gestureRecognizer isEqual:self.rightGes]){ 47 | //禁止右侧手势向右滑动 48 | return NO; 49 | } 50 | } else if (absX < absY ){//纵向滑动 51 | return NO; 52 | } 53 | return YES; 54 | } 55 | 56 | @end 57 | 58 | 59 | 60 | @implementation WKWebView (QWFullScreenPopGesture) 61 | 62 | 63 | + (void)load{ 64 | 65 | static dispatch_once_t onceToken; 66 | dispatch_once(&onceToken, ^{ 67 | 68 | [self swizzleInstanceMethodWithOriginSel:@selector(loadRequest:) 69 | swizzledSel:@selector(qw_loadRequest:)]; 70 | 71 | [self swizzleInstanceMethodWithOriginSel:@selector(loadHTMLString:baseURL:) 72 | swizzledSel:@selector(qw_loadHTMLString:baseURL:)]; 73 | 74 | if (DEVICE_iOS_9) { 75 | [self swizzleInstanceMethodWithOriginSel:@selector(loadFileURL:allowingReadAccessToURL:) 76 | swizzledSel:@selector(qw_loadFileURL:allowingReadAccessToURL:)]; 77 | 78 | [self swizzleInstanceMethodWithOriginSel:@selector(loadData:MIMEType:characterEncodingName:baseURL:) 79 | swizzledSel:@selector(qw_loadData:MIMEType:characterEncodingName:baseURL:)]; 80 | } 81 | 82 | }); 83 | } 84 | 85 | + (void)swizzleInstanceMethodWithOriginSel:(SEL)originalSelector swizzledSel:(SEL)swizzledSelector{ 86 | Class class = [self class]; 87 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 88 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 89 | BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); 90 | if (success) { 91 | class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); 92 | } else { 93 | method_exchangeImplementations(originalMethod, swizzledMethod); 94 | } 95 | } 96 | 97 | - (WKNavigation *)qw_loadRequest:(NSURLRequest *)request{ 98 | [self checkGestureIsAdded]; 99 | return [self qw_loadRequest:request]; 100 | } 101 | 102 | - (WKNavigation *)qw_loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL{ 103 | [self checkGestureIsAdded]; 104 | return [self qw_loadHTMLString:string baseURL:baseURL]; 105 | } 106 | 107 | - (WKNavigation *)qw_loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL{ 108 | [self checkGestureIsAdded]; 109 | return [self qw_loadFileURL:URL allowingReadAccessToURL:readAccessURL]; 110 | } 111 | 112 | - (WKNavigation *)qw_loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL{ 113 | [self checkGestureIsAdded]; 114 | return [self qw_loadData:data MIMEType:MIMEType characterEncodingName:characterEncodingName baseURL:baseURL]; 115 | } 116 | 117 | 118 | 119 | - (void)checkGestureIsAdded{ 120 | //self.gestureRecognizers一共两个手势 121 | //firstObject是向右滑动返回的手势(UIScreenEdgePanGesture),lastObject是向左滑动前进的手势 122 | //避免后面添加了自定义手势造成顺序问题,先引用自带的两个手势 123 | UIPanGestureRecognizer * originEdgeRecognizer = self.gestureRecognizers.firstObject; 124 | UIPanGestureRecognizer * originRightEdgeRecognizer = self.gestureRecognizers.lastObject; 125 | if (![self.gestureRecognizers containsObject:self.qw_fullscreenPopGestureRecognizer] && 126 | ![self.gestureRecognizers containsObject:self.qw_fullscreenRightPopGestureRecognizer]) { 127 | //添加左侧手势 128 | [originEdgeRecognizer.view addGestureRecognizer:self.qw_fullscreenPopGestureRecognizer]; 129 | 130 | // Forward the gesture events to the private handler of the onboard gesture recognizer. 131 | NSArray *internalTargets = [originEdgeRecognizer valueForKey:@"_targets"]; 132 | id internalTarget = [internalTargets.firstObject valueForKey:@"target"]; 133 | SEL internalAction = NSSelectorFromString(@"handleNavigationTransition:"); 134 | if (originEdgeRecognizer.delegate) { 135 | self.qw_fullscreenPopGestureRecognizer.delegate = self.qw_popGestureRecognizerDelegate; 136 | } 137 | [self.qw_fullscreenPopGestureRecognizer addTarget:internalTarget action:internalAction]; 138 | 139 | // Disable the onboard gesture recognizer. 140 | originEdgeRecognizer.enabled = NO; 141 | 142 | //添加右侧手势 143 | [originRightEdgeRecognizer.view addGestureRecognizer:self.qw_fullscreenRightPopGestureRecognizer]; 144 | 145 | // Forward the gesture events to the private handler of the onboard gesture recognizer. 146 | NSArray *internalRightTargets = [originRightEdgeRecognizer valueForKey:@"_targets"]; 147 | id internalRightTarget = [internalRightTargets.firstObject valueForKey:@"target"]; 148 | SEL internalRightAction = NSSelectorFromString(@"handleNavigationTransition:"); 149 | if (originRightEdgeRecognizer.delegate) { 150 | self.qw_fullscreenRightPopGestureRecognizer.delegate = self.qw_popGestureRecognizerDelegate; 151 | } 152 | [self.qw_fullscreenRightPopGestureRecognizer addTarget:internalRightTarget action:internalRightAction]; 153 | 154 | // Disable the onboard gesture recognizer. 155 | originRightEdgeRecognizer.enabled = NO; 156 | 157 | } 158 | } 159 | 160 | - (UIPanGestureRecognizer *)qw_fullscreenPopGestureRecognizer{ 161 | UIPanGestureRecognizer *panGestureRecognizer = objc_getAssociatedObject(self, _cmd); 162 | if (!panGestureRecognizer) { 163 | panGestureRecognizer = [[UIPanGestureRecognizer alloc] init]; 164 | panGestureRecognizer.maximumNumberOfTouches = 1; 165 | 166 | objc_setAssociatedObject(self, _cmd, panGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 167 | } 168 | return panGestureRecognizer; 169 | } 170 | 171 | - (UIPanGestureRecognizer *)qw_fullscreenRightPopGestureRecognizer{ 172 | UIPanGestureRecognizer *panGestureRecognizer = objc_getAssociatedObject(self, _cmd); 173 | if (!panGestureRecognizer) { 174 | panGestureRecognizer = [[UIPanGestureRecognizer alloc] init]; 175 | panGestureRecognizer.maximumNumberOfTouches = 1; 176 | 177 | objc_setAssociatedObject(self, _cmd, panGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 178 | } 179 | return panGestureRecognizer; 180 | } 181 | 182 | - (_QWFullScreenPopGestureDelegate *)qw_popGestureRecognizerDelegate{ 183 | _QWFullScreenPopGestureDelegate *delegate = objc_getAssociatedObject(self, _cmd); 184 | 185 | if (!delegate) { 186 | delegate = [[_QWFullScreenPopGestureDelegate alloc] init]; 187 | delegate.webView = self; 188 | delegate.leftGes = self.qw_fullscreenPopGestureRecognizer; 189 | delegate.rightGes = self.qw_fullscreenRightPopGestureRecognizer; 190 | objc_setAssociatedObject(self, _cmd, delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 191 | } 192 | return delegate; 193 | } 194 | 195 | 196 | @end 197 | --------------------------------------------------------------------------------