├── README.md ├── WZTWebView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── Beck.Wang.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── BY-iMac.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── WZTWebView.xcscheme │ │ └── xcschememanagement.plist │ └── Beck.Wang.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── WZTWebView ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Icon │ ├── empty.png │ ├── full.png │ ├── icon-save-local@2x.png │ ├── icon-save-local@3x.png │ ├── icon-save-local@x.png │ ├── icon-send@2x.png │ ├── icon-send@3x.png │ ├── icon-send@x.png │ ├── step1.png │ ├── step2.png │ └── step3.png ├── Info.plist ├── ViewController.h ├── ViewController.m ├── WZTWebView.xcdatamodeld │ ├── .xccurrentversion │ └── WZTWebView.xcdatamodel │ │ └── contents ├── WebView │ ├── BrowserViewCtrlBase.h │ ├── BrowserViewCtrlBase.m │ ├── NJKWebViewProgress.h │ ├── NJKWebViewProgress.m │ ├── WebviewPictureViewCtrl.h │ ├── WebviewPictureViewCtrl.m │ ├── ZTWebView.h │ └── ZTWebView.m └── main.m ├── WZTWebViewTests ├── Info.plist └── WZTWebViewTests.m └── WZTWebViewUITests ├── Info.plist └── WZTWebViewUITests.m /README.md: -------------------------------------------------------------------------------- 1 | # WebView生成长图 2 | 3 | --- 4 | 5 | iOS开发中,几乎每个app都会有分享功能,有时分享的是一个网页链接,有时确需要把网页生成长图分享出去,iPhone的用户都知道Home键+电源键就可以截屏了,但是这种方式一次只能截取一个屏幕的高度,如果网页超过了屏幕高度,这种方式就行不通了。 6 | 7 | 所以我利用空闲时间写了一个WebView生成长图的Demo,整合了`UIWebView`和`WKWebView`,让系统去自适应以何种容器加载网页,并集成了防微信进度条功能,至于JS交互,里面就只有很基础的协议,因为每个公司的约定不一样,大家需要因地制宜。话不多说,先码图: 8 | 9 | ![image](https://github.com/BeckWang0912/WZTWebView/blob/master/WZTWebView/Icon/step1.png) ![image](https://github.com/BeckWang0912/WZTWebView/blob/master/WZTWebView/Icon/step2.png) 10 | 11 | 我们实现简单点的逻辑:把网页生成一张图片(UIImage) 12 | 13 | ```Objective-C 14 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, scale); 15 | [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 16 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 17 | UIGraphicsEndImageContext(); 18 | ``` 19 | 20 | 关键代码:`renderInContext` 是`CALayer`的方法,`CALayer`是CoreGraphic底层的图层, 组成UIView。UIGraphic等相关操作Context是Quartz 2D框架中的API, 而Quartz 2D是CoreGraphic的其中一个组成。 21 | 22 | 这样一个屏幕高度的网页我们就可以保存成图片了,那么多个屏幕的高度呢?思路也很简单,我们先计算出网页的全部可滚动长度 23 | 24 | `self.scrollView.contentSize.height`与屏幕的高度 `self.bounds.size.height` ,利用while循环滚动网页来获取每次Context下的UIImage,存入images集合中: 25 | 26 | ```Objective-C 27 | CGFloat scale = [UIScreen mainScreen].scale; 28 | CGFloat boundsWidth = self.bounds.size.width; 29 | CGFloat boundsHeight = self.bounds.size.height; 30 | CGFloat contentWidth = self.scrollView.contentSize.height; 31 | CGFloat contentHeight = self.scrollView.contentSize.height; 32 | CGPoint offset = self.scrollView.contentOffset; 33 | [self.scrollView setContentOffset:CGPointMake(0, 0)]; 34 | 35 | NSMutableArray *images = [NSMutableArray array]; 36 | while (contentHeight > 0) { 37 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, scale); 38 | [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 39 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 40 | UIGraphicsEndImageContext(); 41 | [images addObject:image]; 42 | 43 | CGFloat offsetY = self.scrollView.contentOffset.y; 44 | [self.scrollView setContentOffset:CGPointMake(0, offsetY + boundsHeight)]; 45 | contentHeight -= boundsHeight; 46 | } 47 | 48 | [self.scrollView setContentOffset:offset]; 49 | ``` 50 | 51 | 然后,将图片集合进行拼接,形成一个完整的长图: 52 | 53 | ```Objective-C 54 | CGSize imageSize = CGSizeMake(contentWidth * scale, self.scrollView.contentSize.height * scale); 55 | UIGraphicsBeginImageContext(imageSize); 56 | [images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger idx, BOOL *stop) { 57 | [image drawInRect:CGRectMake(0,scale * boundsHeight * idx,scale * boundsWidth,scale * boundsHeight)]; 58 | }]; 59 | ``` 60 | 61 | ```Objective-C 62 | UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext(); 63 | UIGraphicsEndImageContext(); 64 | UIImageView * snapshotView = [[UIImageView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)]; 65 | snapshotView.image = [fullImage resizableImageWithCapInsets:capInsets]; 66 | ``` 67 | 68 | 完整代码附上: 69 | 70 | ```Objective-C 71 | #pragma mark - UIWebview 滚动生成长图 72 | - (void)ZTUIWebViewScrollCaptureCompletionHandler:(CGRect)rect withCapInsets:(UIEdgeInsets)capInsets completionHandler:(void(^)(UIImage *capturedImage))completionHandler{ 73 | CGFloat scale = [UIScreen mainScreen].scale; 74 | CGFloat boundsWidth = self.bounds.size.width; 75 | CGFloat boundsHeight = self.bounds.size.height; 76 | CGFloat contentWidth = self.scrollView.contentSize.height; 77 | CGFloat contentHeight = self.scrollView.contentSize.height; 78 | CGPoint offset = self.scrollView.contentOffset; 79 | [self.scrollView setContentOffset:CGPointMake(0, 0)]; 80 | 81 | NSMutableArray *images = [NSMutableArray array]; 82 | while (contentHeight > 0) { 83 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, scale); 84 | [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 85 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 86 | UIGraphicsEndImageContext(); 87 | [images addObject:image]; 88 | 89 | CGFloat offsetY = self.scrollView.contentOffset.y; 90 | [self.scrollView setContentOffset:CGPointMake(0, offsetY + boundsHeight)]; 91 | contentHeight -= boundsHeight; 92 | } 93 | 94 | [self.scrollView setContentOffset:offset]; 95 | CGSize imageSize = CGSizeMake(contentWidth * scale, self.scrollView.contentSize.height * scale); 96 | UIGraphicsBeginImageContext(imageSize); 97 | [images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger idx, BOOL *stop) { 98 | [image drawInRect:CGRectMake(0,scale * boundsHeight * idx,scale * boundsWidth,scale * boundsHeight)]; 99 | }]; 100 | 101 | UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext(); 102 | UIGraphicsEndImageContext(); 103 | UIImageView * snapshotView = [[UIImageView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, 104 | rect.size.width, rect.size.height)]; 105 | snapshotView.image = [fullImage resizableImageWithCapInsets:capInsets]; 106 | completionHandler(snapshotView.image); 107 | } 108 | ``` 109 | 110 | 写完后很高兴,马上实践,UIWebView妥妥的完成目标,然而WKWebView生成的长图最后会有一大段的空白页,造成部分页面丢失,比较如下: 111 | 112 | ![image](https://github.com/BeckWang0912/WZTWebView/blob/master/WZTWebView/Icon/full.png) ![image](https://github.com/BeckWang0912/WZTWebView/blob/master/WZTWebView/Icon/empty.png) 113 | 114 | 如果你用WKWebView使用这个方法,会发现最终截取的只有屏幕上显示的一部分是因为UIWebView与WKWebView渲染机制的不同。WKWebView并不能简单的使用`layer.renderInContext`的方法去绘制图形。如果直接调用`layer.renderInContext`需要获取对应的Context, 但是在WKWebView中执行`UIGraphicsGetCurrentContext()`的返回结果是nil。 115 | 116 | 我做了大量搜索和实践后发现WKWebView中通过直接调用WKWebView的`drawViewHierarchyInRect`方法,可以成功的截取WKWebView的屏幕内容。 117 | 118 | ```Objective-C 119 | - (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates NS_AVAILABLE_IOS(7_0); 120 | ``` 121 | 122 | 使用时保证afterScreenUpdates = YES 123 | 124 | 完整代码附上: 125 | 126 | ```Objective-C 127 | #pragma mark - WKWebView 滚动生成长图 128 | - (void)ZTWKWebViewScrollCaptureCompletionHandler:(void(^)(UIImage *capturedImage))completionHandler{ 129 | // 制作了一个UIView的副本 130 | UIView *snapShotView = [self snapshotViewAfterScreenUpdates:YES]; 131 | 132 | snapShotView.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, snapShotView.frame.size.width, snapShotView.frame.size.height); 133 | 134 | [self.superview addSubview:snapShotView]; 135 | 136 | // 获取当前UIView可滚动的内容长度 137 | CGPoint scrollOffset = self.scrollView.contentOffset; 138 | 139 | // 向上取整数 - 可滚动长度与UIView本身屏幕边界坐标相差倍数 140 | float maxIndex = ceilf(self.scrollView.contentSize.height/self.bounds.size.height); 141 | 142 | // 保持清晰度 143 | UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, [UIScreen mainScreen].scale); 144 | 145 | // 滚动截图 146 | [self ZTContentScrollPageDraw:0 maxIndex:(int)maxIndex drawCallback:^{ 147 | UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext(); 148 | UIGraphicsEndImageContext(); 149 | 150 | // 恢复原UIView 151 | [self.scrollView setContentOffset:scrollOffset animated:NO]; 152 | [snapShotView removeFromSuperview]; 153 | 154 | completionHandler(capturedImage); 155 | }]; 156 | } 157 | 158 | // 滚动截图 159 | - (void)ZTContentScrollPageDraw:(int)index maxIndex:(int)maxIndex drawCallback:(void(^)(void))drawCallback{ 160 | [self.scrollView setContentOffset:CGPointMake(0, (float)index * self.frame.size.height)]; 161 | CGRect splitFrame = CGRectMake(0, (float)index * self.frame.size.height, self.bounds.size.width, self.bounds.size.height); 162 | 163 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 164 | [self drawViewHierarchyInRect:splitFrame afterScreenUpdates:YES]; 165 | if(index < maxIndex){ 166 | [self ZTContentScrollPageDraw: index + 1 maxIndex:maxIndex drawCallback:drawCallback]; 167 | }else{ 168 | drawCallback(); 169 | } 170 | }); 171 | } 172 | ``` 173 | 174 | 至此,WKWebView截屏生成长图实现。 175 | 176 | 此外,demo还封装了加载进度条NJKWebViewProgress(KVO原理),能自适应UIWebView和WKWebView。 177 | 178 | >NJKWebViewProgress 179 | 180 | ```Objective-C 181 | // 添加KVO 182 | [(ZTWKWebView *)_webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL]; 183 | [(ZTWKWebView *)_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL]; 184 | ``` 185 | 186 | ```Objective-C 187 | #pragma mark - KVO 188 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ 189 | 190 | if ([keyPath isEqualToString:@"title"]) { 191 | self.title = change[NSKeyValueChangeNewKey]; 192 | return; 193 | } 194 | 195 | if (self.canShowProgress && object == self.webView && [keyPath isEqualToString:@"estimatedProgress"]) { 196 | CGFloat newprogress = [[change objectForKey:NSKeyValueChangeNewKey] doubleValue]; 197 | self.estimatedProgress = newprogress; 198 | if (newprogress == 1) { 199 | self.progressView.hidden = YES; 200 | [self.progressView setProgress:0 animated:NO]; 201 | }else { 202 | self.progressView.hidden = NO; 203 | [self.progressView setProgress:newprogress animated:YES]; 204 | } 205 | return; 206 | } 207 | 208 | return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 209 | } 210 | ``` 211 | 212 | >ZTWebViewDelegate 213 | 214 | ```Objective-C 215 | /** 216 | 定义ZTWebView代理 217 | */ 218 | @protocol ZTWebViewDelegate 219 | @optional 220 | - (BOOL)zt_webView:(id)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(ZTWebViewNavType)navigationType; 221 | - (void)zt_webViewDidStartLoad:(id)webView; 222 | - (void)zt_webViewDidFinishLoad:(id)webView; 223 | - (void)zt_webView:(id)webView didFailLoadWithError:(NSError *)error; 224 | @end 225 | ``` 226 | 227 | 228 | 后续将会更新内容截图功能。……^\_^ 229 | 230 | 如果对您有帮助,请star鼓励下😊 231 | 232 | -------------------------------------------------------------------------------- /WZTWebView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3A1953271EE63B7D009FDE38 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A1953261EE63B7D009FDE38 /* main.m */; }; 11 | 3A19532A1EE63B7D009FDE38 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A1953291EE63B7D009FDE38 /* AppDelegate.m */; }; 12 | 3A19532D1EE63B7D009FDE38 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A19532C1EE63B7D009FDE38 /* ViewController.m */; }; 13 | 3A1953301EE63B7D009FDE38 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A19532E1EE63B7D009FDE38 /* Main.storyboard */; }; 14 | 3A1953331EE63B7D009FDE38 /* WZTWebView.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 3A1953311EE63B7D009FDE38 /* WZTWebView.xcdatamodeld */; }; 15 | 3A1953351EE63B7D009FDE38 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3A1953341EE63B7D009FDE38 /* Assets.xcassets */; }; 16 | 3A1953381EE63B7D009FDE38 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3A1953361EE63B7D009FDE38 /* LaunchScreen.storyboard */; }; 17 | 3A1953431EE63B7D009FDE38 /* WZTWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A1953421EE63B7D009FDE38 /* WZTWebViewTests.m */; }; 18 | 3A19534E1EE63B7D009FDE38 /* WZTWebViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A19534D1EE63B7D009FDE38 /* WZTWebViewUITests.m */; }; 19 | 3A1953631EE63BBE009FDE38 /* NJKWebViewProgress.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A19535F1EE63BBE009FDE38 /* NJKWebViewProgress.m */; }; 20 | 3A1953641EE63BBE009FDE38 /* WebviewPictureViewCtrl.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A1953611EE63BBE009FDE38 /* WebviewPictureViewCtrl.m */; }; 21 | 3A1953671EE63C7E009FDE38 /* ZTWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A1953661EE63C7E009FDE38 /* ZTWebView.m */; }; 22 | 3A19536A1EE655D6009FDE38 /* BrowserViewCtrlBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A1953691EE655D6009FDE38 /* BrowserViewCtrlBase.m */; }; 23 | 3A1953721EE67CDC009FDE38 /* icon-send@x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A19536C1EE67CDC009FDE38 /* icon-send@x.png */; }; 24 | 3A1953731EE67CDC009FDE38 /* icon-send@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A19536D1EE67CDC009FDE38 /* icon-send@3x.png */; }; 25 | 3A1953741EE67CDC009FDE38 /* icon-send@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A19536E1EE67CDC009FDE38 /* icon-send@2x.png */; }; 26 | 3A1953751EE67CDC009FDE38 /* icon-save-local@x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A19536F1EE67CDC009FDE38 /* icon-save-local@x.png */; }; 27 | 3A1953761EE67CDC009FDE38 /* icon-save-local@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A1953701EE67CDC009FDE38 /* icon-save-local@3x.png */; }; 28 | 3A1953771EE67CDC009FDE38 /* icon-save-local@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A1953711EE67CDC009FDE38 /* icon-save-local@2x.png */; }; 29 | 3FA6D6FA202EED880089CECF /* step2.png in Resources */ = {isa = PBXBuildFile; fileRef = 3FA6D6F5202EED870089CECF /* step2.png */; }; 30 | 3FA6D6FB202EED880089CECF /* step3.png in Resources */ = {isa = PBXBuildFile; fileRef = 3FA6D6F6202EED870089CECF /* step3.png */; }; 31 | 3FA6D6FC202EED880089CECF /* full.png in Resources */ = {isa = PBXBuildFile; fileRef = 3FA6D6F7202EED870089CECF /* full.png */; }; 32 | 3FA6D6FD202EED880089CECF /* step1.png in Resources */ = {isa = PBXBuildFile; fileRef = 3FA6D6F8202EED870089CECF /* step1.png */; }; 33 | 3FA6D6FE202EED880089CECF /* empty.png in Resources */ = {isa = PBXBuildFile; fileRef = 3FA6D6F9202EED880089CECF /* empty.png */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXContainerItemProxy section */ 37 | 3A19533F1EE63B7D009FDE38 /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = 3A19531A1EE63B7D009FDE38 /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = 3A1953211EE63B7D009FDE38; 42 | remoteInfo = WZTWebView; 43 | }; 44 | 3A19534A1EE63B7D009FDE38 /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = 3A19531A1EE63B7D009FDE38 /* Project object */; 47 | proxyType = 1; 48 | remoteGlobalIDString = 3A1953211EE63B7D009FDE38; 49 | remoteInfo = WZTWebView; 50 | }; 51 | /* End PBXContainerItemProxy section */ 52 | 53 | /* Begin PBXFileReference section */ 54 | 3A1953221EE63B7D009FDE38 /* WZTWebView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WZTWebView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 3A1953261EE63B7D009FDE38 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 56 | 3A1953281EE63B7D009FDE38 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 57 | 3A1953291EE63B7D009FDE38 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 58 | 3A19532B1EE63B7D009FDE38 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 59 | 3A19532C1EE63B7D009FDE38 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 60 | 3A19532F1EE63B7D009FDE38 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 61 | 3A1953321EE63B7D009FDE38 /* WZTWebView.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = WZTWebView.xcdatamodel; sourceTree = ""; }; 62 | 3A1953341EE63B7D009FDE38 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 63 | 3A1953371EE63B7D009FDE38 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 64 | 3A1953391EE63B7D009FDE38 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 3A19533E1EE63B7D009FDE38 /* WZTWebViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WZTWebViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 3A1953421EE63B7D009FDE38 /* WZTWebViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WZTWebViewTests.m; sourceTree = ""; }; 67 | 3A1953441EE63B7D009FDE38 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 3A1953491EE63B7D009FDE38 /* WZTWebViewUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WZTWebViewUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 3A19534D1EE63B7D009FDE38 /* WZTWebViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WZTWebViewUITests.m; sourceTree = ""; }; 70 | 3A19534F1EE63B7D009FDE38 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 71 | 3A19535E1EE63BBE009FDE38 /* NJKWebViewProgress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NJKWebViewProgress.h; path = WebView/NJKWebViewProgress.h; sourceTree = ""; }; 72 | 3A19535F1EE63BBE009FDE38 /* NJKWebViewProgress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NJKWebViewProgress.m; path = WebView/NJKWebViewProgress.m; sourceTree = ""; }; 73 | 3A1953601EE63BBE009FDE38 /* WebviewPictureViewCtrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebviewPictureViewCtrl.h; path = WebView/WebviewPictureViewCtrl.h; sourceTree = ""; }; 74 | 3A1953611EE63BBE009FDE38 /* WebviewPictureViewCtrl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WebviewPictureViewCtrl.m; path = WebView/WebviewPictureViewCtrl.m; sourceTree = ""; }; 75 | 3A1953651EE63C7E009FDE38 /* ZTWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZTWebView.h; path = WebView/ZTWebView.h; sourceTree = ""; }; 76 | 3A1953661EE63C7E009FDE38 /* ZTWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ZTWebView.m; path = WebView/ZTWebView.m; sourceTree = ""; }; 77 | 3A1953681EE655D6009FDE38 /* BrowserViewCtrlBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BrowserViewCtrlBase.h; path = WebView/BrowserViewCtrlBase.h; sourceTree = ""; }; 78 | 3A1953691EE655D6009FDE38 /* BrowserViewCtrlBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BrowserViewCtrlBase.m; path = WebView/BrowserViewCtrlBase.m; sourceTree = ""; }; 79 | 3A19536C1EE67CDC009FDE38 /* icon-send@x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-send@x.png"; path = "Icon/icon-send@x.png"; sourceTree = ""; }; 80 | 3A19536D1EE67CDC009FDE38 /* icon-send@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-send@3x.png"; path = "Icon/icon-send@3x.png"; sourceTree = ""; }; 81 | 3A19536E1EE67CDC009FDE38 /* icon-send@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-send@2x.png"; path = "Icon/icon-send@2x.png"; sourceTree = ""; }; 82 | 3A19536F1EE67CDC009FDE38 /* icon-save-local@x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-save-local@x.png"; path = "Icon/icon-save-local@x.png"; sourceTree = ""; }; 83 | 3A1953701EE67CDC009FDE38 /* icon-save-local@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-save-local@3x.png"; path = "Icon/icon-save-local@3x.png"; sourceTree = ""; }; 84 | 3A1953711EE67CDC009FDE38 /* icon-save-local@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-save-local@2x.png"; path = "Icon/icon-save-local@2x.png"; sourceTree = ""; }; 85 | 3FA6D6F5202EED870089CECF /* step2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = step2.png; path = Icon/step2.png; sourceTree = ""; }; 86 | 3FA6D6F6202EED870089CECF /* step3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = step3.png; path = Icon/step3.png; sourceTree = ""; }; 87 | 3FA6D6F7202EED870089CECF /* full.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = full.png; path = Icon/full.png; sourceTree = ""; }; 88 | 3FA6D6F8202EED870089CECF /* step1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = step1.png; path = Icon/step1.png; sourceTree = ""; }; 89 | 3FA6D6F9202EED880089CECF /* empty.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = empty.png; path = Icon/empty.png; sourceTree = ""; }; 90 | /* End PBXFileReference section */ 91 | 92 | /* Begin PBXFrameworksBuildPhase section */ 93 | 3A19531F1EE63B7D009FDE38 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | 3A19533B1EE63B7D009FDE38 /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | 3A1953461EE63B7D009FDE38 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | /* End PBXFrameworksBuildPhase section */ 115 | 116 | /* Begin PBXGroup section */ 117 | 3A1953191EE63B7D009FDE38 = { 118 | isa = PBXGroup; 119 | children = ( 120 | 3A1953241EE63B7D009FDE38 /* WZTWebView */, 121 | 3A1953411EE63B7D009FDE38 /* WZTWebViewTests */, 122 | 3A19534C1EE63B7D009FDE38 /* WZTWebViewUITests */, 123 | 3A1953231EE63B7D009FDE38 /* Products */, 124 | ); 125 | sourceTree = ""; 126 | }; 127 | 3A1953231EE63B7D009FDE38 /* Products */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 3A1953221EE63B7D009FDE38 /* WZTWebView.app */, 131 | 3A19533E1EE63B7D009FDE38 /* WZTWebViewTests.xctest */, 132 | 3A1953491EE63B7D009FDE38 /* WZTWebViewUITests.xctest */, 133 | ); 134 | name = Products; 135 | sourceTree = ""; 136 | }; 137 | 3A1953241EE63B7D009FDE38 /* WZTWebView */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 3A19536B1EE67CA3009FDE38 /* Icon */, 141 | 3A19535B1EE63B98009FDE38 /* WebView */, 142 | 3A1953281EE63B7D009FDE38 /* AppDelegate.h */, 143 | 3A1953291EE63B7D009FDE38 /* AppDelegate.m */, 144 | 3A19532B1EE63B7D009FDE38 /* ViewController.h */, 145 | 3A19532C1EE63B7D009FDE38 /* ViewController.m */, 146 | 3A19532E1EE63B7D009FDE38 /* Main.storyboard */, 147 | 3A1953341EE63B7D009FDE38 /* Assets.xcassets */, 148 | 3A1953361EE63B7D009FDE38 /* LaunchScreen.storyboard */, 149 | 3A1953391EE63B7D009FDE38 /* Info.plist */, 150 | 3A1953311EE63B7D009FDE38 /* WZTWebView.xcdatamodeld */, 151 | 3A1953251EE63B7D009FDE38 /* Supporting Files */, 152 | ); 153 | path = WZTWebView; 154 | sourceTree = ""; 155 | }; 156 | 3A1953251EE63B7D009FDE38 /* Supporting Files */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 3A1953261EE63B7D009FDE38 /* main.m */, 160 | ); 161 | name = "Supporting Files"; 162 | sourceTree = ""; 163 | }; 164 | 3A1953411EE63B7D009FDE38 /* WZTWebViewTests */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 3A1953421EE63B7D009FDE38 /* WZTWebViewTests.m */, 168 | 3A1953441EE63B7D009FDE38 /* Info.plist */, 169 | ); 170 | path = WZTWebViewTests; 171 | sourceTree = ""; 172 | }; 173 | 3A19534C1EE63B7D009FDE38 /* WZTWebViewUITests */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | 3A19534D1EE63B7D009FDE38 /* WZTWebViewUITests.m */, 177 | 3A19534F1EE63B7D009FDE38 /* Info.plist */, 178 | ); 179 | path = WZTWebViewUITests; 180 | sourceTree = ""; 181 | }; 182 | 3A19535B1EE63B98009FDE38 /* WebView */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 3A1953651EE63C7E009FDE38 /* ZTWebView.h */, 186 | 3A1953661EE63C7E009FDE38 /* ZTWebView.m */, 187 | 3A19535E1EE63BBE009FDE38 /* NJKWebViewProgress.h */, 188 | 3A19535F1EE63BBE009FDE38 /* NJKWebViewProgress.m */, 189 | 3A1953601EE63BBE009FDE38 /* WebviewPictureViewCtrl.h */, 190 | 3A1953611EE63BBE009FDE38 /* WebviewPictureViewCtrl.m */, 191 | 3A1953681EE655D6009FDE38 /* BrowserViewCtrlBase.h */, 192 | 3A1953691EE655D6009FDE38 /* BrowserViewCtrlBase.m */, 193 | ); 194 | name = WebView; 195 | sourceTree = ""; 196 | }; 197 | 3A19536B1EE67CA3009FDE38 /* Icon */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | 3A19536C1EE67CDC009FDE38 /* icon-send@x.png */, 201 | 3A19536D1EE67CDC009FDE38 /* icon-send@3x.png */, 202 | 3A19536E1EE67CDC009FDE38 /* icon-send@2x.png */, 203 | 3A19536F1EE67CDC009FDE38 /* icon-save-local@x.png */, 204 | 3A1953701EE67CDC009FDE38 /* icon-save-local@3x.png */, 205 | 3A1953711EE67CDC009FDE38 /* icon-save-local@2x.png */, 206 | 3FA6D6F9202EED880089CECF /* empty.png */, 207 | 3FA6D6F7202EED870089CECF /* full.png */, 208 | 3FA6D6F8202EED870089CECF /* step1.png */, 209 | 3FA6D6F5202EED870089CECF /* step2.png */, 210 | 3FA6D6F6202EED870089CECF /* step3.png */, 211 | ); 212 | name = Icon; 213 | sourceTree = ""; 214 | }; 215 | /* End PBXGroup section */ 216 | 217 | /* Begin PBXNativeTarget section */ 218 | 3A1953211EE63B7D009FDE38 /* WZTWebView */ = { 219 | isa = PBXNativeTarget; 220 | buildConfigurationList = 3A1953521EE63B7D009FDE38 /* Build configuration list for PBXNativeTarget "WZTWebView" */; 221 | buildPhases = ( 222 | 3A19531E1EE63B7D009FDE38 /* Sources */, 223 | 3A19531F1EE63B7D009FDE38 /* Frameworks */, 224 | 3A1953201EE63B7D009FDE38 /* Resources */, 225 | ); 226 | buildRules = ( 227 | ); 228 | dependencies = ( 229 | ); 230 | name = WZTWebView; 231 | productName = WZTWebView; 232 | productReference = 3A1953221EE63B7D009FDE38 /* WZTWebView.app */; 233 | productType = "com.apple.product-type.application"; 234 | }; 235 | 3A19533D1EE63B7D009FDE38 /* WZTWebViewTests */ = { 236 | isa = PBXNativeTarget; 237 | buildConfigurationList = 3A1953551EE63B7D009FDE38 /* Build configuration list for PBXNativeTarget "WZTWebViewTests" */; 238 | buildPhases = ( 239 | 3A19533A1EE63B7D009FDE38 /* Sources */, 240 | 3A19533B1EE63B7D009FDE38 /* Frameworks */, 241 | 3A19533C1EE63B7D009FDE38 /* Resources */, 242 | ); 243 | buildRules = ( 244 | ); 245 | dependencies = ( 246 | 3A1953401EE63B7D009FDE38 /* PBXTargetDependency */, 247 | ); 248 | name = WZTWebViewTests; 249 | productName = WZTWebViewTests; 250 | productReference = 3A19533E1EE63B7D009FDE38 /* WZTWebViewTests.xctest */; 251 | productType = "com.apple.product-type.bundle.unit-test"; 252 | }; 253 | 3A1953481EE63B7D009FDE38 /* WZTWebViewUITests */ = { 254 | isa = PBXNativeTarget; 255 | buildConfigurationList = 3A1953581EE63B7D009FDE38 /* Build configuration list for PBXNativeTarget "WZTWebViewUITests" */; 256 | buildPhases = ( 257 | 3A1953451EE63B7D009FDE38 /* Sources */, 258 | 3A1953461EE63B7D009FDE38 /* Frameworks */, 259 | 3A1953471EE63B7D009FDE38 /* Resources */, 260 | ); 261 | buildRules = ( 262 | ); 263 | dependencies = ( 264 | 3A19534B1EE63B7D009FDE38 /* PBXTargetDependency */, 265 | ); 266 | name = WZTWebViewUITests; 267 | productName = WZTWebViewUITests; 268 | productReference = 3A1953491EE63B7D009FDE38 /* WZTWebViewUITests.xctest */; 269 | productType = "com.apple.product-type.bundle.ui-testing"; 270 | }; 271 | /* End PBXNativeTarget section */ 272 | 273 | /* Begin PBXProject section */ 274 | 3A19531A1EE63B7D009FDE38 /* Project object */ = { 275 | isa = PBXProject; 276 | attributes = { 277 | LastUpgradeCheck = 0910; 278 | ORGANIZATIONNAME = beck.wang; 279 | TargetAttributes = { 280 | 3A1953211EE63B7D009FDE38 = { 281 | CreatedOnToolsVersion = 8.2.1; 282 | ProvisioningStyle = Automatic; 283 | }; 284 | 3A19533D1EE63B7D009FDE38 = { 285 | CreatedOnToolsVersion = 8.2.1; 286 | ProvisioningStyle = Automatic; 287 | TestTargetID = 3A1953211EE63B7D009FDE38; 288 | }; 289 | 3A1953481EE63B7D009FDE38 = { 290 | CreatedOnToolsVersion = 8.2.1; 291 | ProvisioningStyle = Automatic; 292 | TestTargetID = 3A1953211EE63B7D009FDE38; 293 | }; 294 | }; 295 | }; 296 | buildConfigurationList = 3A19531D1EE63B7D009FDE38 /* Build configuration list for PBXProject "WZTWebView" */; 297 | compatibilityVersion = "Xcode 3.2"; 298 | developmentRegion = English; 299 | hasScannedForEncodings = 0; 300 | knownRegions = ( 301 | en, 302 | Base, 303 | ); 304 | mainGroup = 3A1953191EE63B7D009FDE38; 305 | productRefGroup = 3A1953231EE63B7D009FDE38 /* Products */; 306 | projectDirPath = ""; 307 | projectRoot = ""; 308 | targets = ( 309 | 3A1953211EE63B7D009FDE38 /* WZTWebView */, 310 | 3A19533D1EE63B7D009FDE38 /* WZTWebViewTests */, 311 | 3A1953481EE63B7D009FDE38 /* WZTWebViewUITests */, 312 | ); 313 | }; 314 | /* End PBXProject section */ 315 | 316 | /* Begin PBXResourcesBuildPhase section */ 317 | 3A1953201EE63B7D009FDE38 /* Resources */ = { 318 | isa = PBXResourcesBuildPhase; 319 | buildActionMask = 2147483647; 320 | files = ( 321 | 3A1953381EE63B7D009FDE38 /* LaunchScreen.storyboard in Resources */, 322 | 3A1953761EE67CDC009FDE38 /* icon-save-local@3x.png in Resources */, 323 | 3FA6D6FC202EED880089CECF /* full.png in Resources */, 324 | 3A1953731EE67CDC009FDE38 /* icon-send@3x.png in Resources */, 325 | 3A1953351EE63B7D009FDE38 /* Assets.xcassets in Resources */, 326 | 3FA6D6FA202EED880089CECF /* step2.png in Resources */, 327 | 3A1953721EE67CDC009FDE38 /* icon-send@x.png in Resources */, 328 | 3FA6D6FB202EED880089CECF /* step3.png in Resources */, 329 | 3A1953751EE67CDC009FDE38 /* icon-save-local@x.png in Resources */, 330 | 3A1953771EE67CDC009FDE38 /* icon-save-local@2x.png in Resources */, 331 | 3A1953301EE63B7D009FDE38 /* Main.storyboard in Resources */, 332 | 3FA6D6FE202EED880089CECF /* empty.png in Resources */, 333 | 3FA6D6FD202EED880089CECF /* step1.png in Resources */, 334 | 3A1953741EE67CDC009FDE38 /* icon-send@2x.png in Resources */, 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | 3A19533C1EE63B7D009FDE38 /* Resources */ = { 339 | isa = PBXResourcesBuildPhase; 340 | buildActionMask = 2147483647; 341 | files = ( 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | 3A1953471EE63B7D009FDE38 /* Resources */ = { 346 | isa = PBXResourcesBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | ); 350 | runOnlyForDeploymentPostprocessing = 0; 351 | }; 352 | /* End PBXResourcesBuildPhase section */ 353 | 354 | /* Begin PBXSourcesBuildPhase section */ 355 | 3A19531E1EE63B7D009FDE38 /* Sources */ = { 356 | isa = PBXSourcesBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | 3A19532D1EE63B7D009FDE38 /* ViewController.m in Sources */, 360 | 3A1953331EE63B7D009FDE38 /* WZTWebView.xcdatamodeld in Sources */, 361 | 3A19532A1EE63B7D009FDE38 /* AppDelegate.m in Sources */, 362 | 3A1953631EE63BBE009FDE38 /* NJKWebViewProgress.m in Sources */, 363 | 3A1953641EE63BBE009FDE38 /* WebviewPictureViewCtrl.m in Sources */, 364 | 3A1953271EE63B7D009FDE38 /* main.m in Sources */, 365 | 3A1953671EE63C7E009FDE38 /* ZTWebView.m in Sources */, 366 | 3A19536A1EE655D6009FDE38 /* BrowserViewCtrlBase.m in Sources */, 367 | ); 368 | runOnlyForDeploymentPostprocessing = 0; 369 | }; 370 | 3A19533A1EE63B7D009FDE38 /* Sources */ = { 371 | isa = PBXSourcesBuildPhase; 372 | buildActionMask = 2147483647; 373 | files = ( 374 | 3A1953431EE63B7D009FDE38 /* WZTWebViewTests.m in Sources */, 375 | ); 376 | runOnlyForDeploymentPostprocessing = 0; 377 | }; 378 | 3A1953451EE63B7D009FDE38 /* Sources */ = { 379 | isa = PBXSourcesBuildPhase; 380 | buildActionMask = 2147483647; 381 | files = ( 382 | 3A19534E1EE63B7D009FDE38 /* WZTWebViewUITests.m in Sources */, 383 | ); 384 | runOnlyForDeploymentPostprocessing = 0; 385 | }; 386 | /* End PBXSourcesBuildPhase section */ 387 | 388 | /* Begin PBXTargetDependency section */ 389 | 3A1953401EE63B7D009FDE38 /* PBXTargetDependency */ = { 390 | isa = PBXTargetDependency; 391 | target = 3A1953211EE63B7D009FDE38 /* WZTWebView */; 392 | targetProxy = 3A19533F1EE63B7D009FDE38 /* PBXContainerItemProxy */; 393 | }; 394 | 3A19534B1EE63B7D009FDE38 /* PBXTargetDependency */ = { 395 | isa = PBXTargetDependency; 396 | target = 3A1953211EE63B7D009FDE38 /* WZTWebView */; 397 | targetProxy = 3A19534A1EE63B7D009FDE38 /* PBXContainerItemProxy */; 398 | }; 399 | /* End PBXTargetDependency section */ 400 | 401 | /* Begin PBXVariantGroup section */ 402 | 3A19532E1EE63B7D009FDE38 /* Main.storyboard */ = { 403 | isa = PBXVariantGroup; 404 | children = ( 405 | 3A19532F1EE63B7D009FDE38 /* Base */, 406 | ); 407 | name = Main.storyboard; 408 | sourceTree = ""; 409 | }; 410 | 3A1953361EE63B7D009FDE38 /* LaunchScreen.storyboard */ = { 411 | isa = PBXVariantGroup; 412 | children = ( 413 | 3A1953371EE63B7D009FDE38 /* Base */, 414 | ); 415 | name = LaunchScreen.storyboard; 416 | sourceTree = ""; 417 | }; 418 | /* End PBXVariantGroup section */ 419 | 420 | /* Begin XCBuildConfiguration section */ 421 | 3A1953501EE63B7D009FDE38 /* Debug */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ALWAYS_SEARCH_USER_PATHS = NO; 425 | CLANG_ANALYZER_NONNULL = YES; 426 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 427 | CLANG_CXX_LIBRARY = "libc++"; 428 | CLANG_ENABLE_MODULES = YES; 429 | CLANG_ENABLE_OBJC_ARC = YES; 430 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 431 | CLANG_WARN_BOOL_CONVERSION = YES; 432 | CLANG_WARN_COMMA = YES; 433 | CLANG_WARN_CONSTANT_CONVERSION = YES; 434 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 435 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 436 | CLANG_WARN_EMPTY_BODY = YES; 437 | CLANG_WARN_ENUM_CONVERSION = YES; 438 | CLANG_WARN_INFINITE_RECURSION = YES; 439 | CLANG_WARN_INT_CONVERSION = YES; 440 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 441 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 442 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 443 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 444 | CLANG_WARN_STRICT_PROTOTYPES = YES; 445 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 446 | CLANG_WARN_UNREACHABLE_CODE = YES; 447 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 448 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 449 | COPY_PHASE_STRIP = NO; 450 | DEBUG_INFORMATION_FORMAT = dwarf; 451 | ENABLE_STRICT_OBJC_MSGSEND = YES; 452 | ENABLE_TESTABILITY = YES; 453 | GCC_C_LANGUAGE_STANDARD = gnu99; 454 | GCC_DYNAMIC_NO_PIC = NO; 455 | GCC_NO_COMMON_BLOCKS = YES; 456 | GCC_OPTIMIZATION_LEVEL = 0; 457 | GCC_PREPROCESSOR_DEFINITIONS = ( 458 | "DEBUG=1", 459 | "$(inherited)", 460 | ); 461 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 462 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 463 | GCC_WARN_UNDECLARED_SELECTOR = YES; 464 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 465 | GCC_WARN_UNUSED_FUNCTION = YES; 466 | GCC_WARN_UNUSED_VARIABLE = YES; 467 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 468 | MTL_ENABLE_DEBUG_INFO = YES; 469 | ONLY_ACTIVE_ARCH = YES; 470 | SDKROOT = iphoneos; 471 | }; 472 | name = Debug; 473 | }; 474 | 3A1953511EE63B7D009FDE38 /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ALWAYS_SEARCH_USER_PATHS = NO; 478 | CLANG_ANALYZER_NONNULL = YES; 479 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 480 | CLANG_CXX_LIBRARY = "libc++"; 481 | CLANG_ENABLE_MODULES = YES; 482 | CLANG_ENABLE_OBJC_ARC = YES; 483 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 484 | CLANG_WARN_BOOL_CONVERSION = YES; 485 | CLANG_WARN_COMMA = YES; 486 | CLANG_WARN_CONSTANT_CONVERSION = YES; 487 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 488 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 489 | CLANG_WARN_EMPTY_BODY = YES; 490 | CLANG_WARN_ENUM_CONVERSION = YES; 491 | CLANG_WARN_INFINITE_RECURSION = YES; 492 | CLANG_WARN_INT_CONVERSION = YES; 493 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 494 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 495 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 496 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 497 | CLANG_WARN_STRICT_PROTOTYPES = YES; 498 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 499 | CLANG_WARN_UNREACHABLE_CODE = YES; 500 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 501 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 502 | COPY_PHASE_STRIP = NO; 503 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 504 | ENABLE_NS_ASSERTIONS = NO; 505 | ENABLE_STRICT_OBJC_MSGSEND = YES; 506 | GCC_C_LANGUAGE_STANDARD = gnu99; 507 | GCC_NO_COMMON_BLOCKS = YES; 508 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 509 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 510 | GCC_WARN_UNDECLARED_SELECTOR = YES; 511 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 512 | GCC_WARN_UNUSED_FUNCTION = YES; 513 | GCC_WARN_UNUSED_VARIABLE = YES; 514 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 515 | MTL_ENABLE_DEBUG_INFO = NO; 516 | SDKROOT = iphoneos; 517 | VALIDATE_PRODUCT = YES; 518 | }; 519 | name = Release; 520 | }; 521 | 3A1953531EE63B7D009FDE38 /* Debug */ = { 522 | isa = XCBuildConfiguration; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | INFOPLIST_FILE = WZTWebView/Info.plist; 526 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 528 | PRODUCT_BUNDLE_IDENTIFIER = com.ztwang.WZTWebView; 529 | PRODUCT_NAME = "$(TARGET_NAME)"; 530 | }; 531 | name = Debug; 532 | }; 533 | 3A1953541EE63B7D009FDE38 /* Release */ = { 534 | isa = XCBuildConfiguration; 535 | buildSettings = { 536 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 537 | INFOPLIST_FILE = WZTWebView/Info.plist; 538 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 540 | PRODUCT_BUNDLE_IDENTIFIER = com.ztwang.WZTWebView; 541 | PRODUCT_NAME = "$(TARGET_NAME)"; 542 | }; 543 | name = Release; 544 | }; 545 | 3A1953561EE63B7D009FDE38 /* Debug */ = { 546 | isa = XCBuildConfiguration; 547 | buildSettings = { 548 | BUNDLE_LOADER = "$(TEST_HOST)"; 549 | INFOPLIST_FILE = WZTWebViewTests/Info.plist; 550 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 551 | PRODUCT_BUNDLE_IDENTIFIER = com.ztwang.WZTWebViewTests; 552 | PRODUCT_NAME = "$(TARGET_NAME)"; 553 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WZTWebView.app/WZTWebView"; 554 | }; 555 | name = Debug; 556 | }; 557 | 3A1953571EE63B7D009FDE38 /* Release */ = { 558 | isa = XCBuildConfiguration; 559 | buildSettings = { 560 | BUNDLE_LOADER = "$(TEST_HOST)"; 561 | INFOPLIST_FILE = WZTWebViewTests/Info.plist; 562 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 563 | PRODUCT_BUNDLE_IDENTIFIER = com.ztwang.WZTWebViewTests; 564 | PRODUCT_NAME = "$(TARGET_NAME)"; 565 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WZTWebView.app/WZTWebView"; 566 | }; 567 | name = Release; 568 | }; 569 | 3A1953591EE63B7D009FDE38 /* Debug */ = { 570 | isa = XCBuildConfiguration; 571 | buildSettings = { 572 | INFOPLIST_FILE = WZTWebViewUITests/Info.plist; 573 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 574 | PRODUCT_BUNDLE_IDENTIFIER = com.ztwang.WZTWebViewUITests; 575 | PRODUCT_NAME = "$(TARGET_NAME)"; 576 | TEST_TARGET_NAME = WZTWebView; 577 | }; 578 | name = Debug; 579 | }; 580 | 3A19535A1EE63B7D009FDE38 /* Release */ = { 581 | isa = XCBuildConfiguration; 582 | buildSettings = { 583 | INFOPLIST_FILE = WZTWebViewUITests/Info.plist; 584 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 585 | PRODUCT_BUNDLE_IDENTIFIER = com.ztwang.WZTWebViewUITests; 586 | PRODUCT_NAME = "$(TARGET_NAME)"; 587 | TEST_TARGET_NAME = WZTWebView; 588 | }; 589 | name = Release; 590 | }; 591 | /* End XCBuildConfiguration section */ 592 | 593 | /* Begin XCConfigurationList section */ 594 | 3A19531D1EE63B7D009FDE38 /* Build configuration list for PBXProject "WZTWebView" */ = { 595 | isa = XCConfigurationList; 596 | buildConfigurations = ( 597 | 3A1953501EE63B7D009FDE38 /* Debug */, 598 | 3A1953511EE63B7D009FDE38 /* Release */, 599 | ); 600 | defaultConfigurationIsVisible = 0; 601 | defaultConfigurationName = Release; 602 | }; 603 | 3A1953521EE63B7D009FDE38 /* Build configuration list for PBXNativeTarget "WZTWebView" */ = { 604 | isa = XCConfigurationList; 605 | buildConfigurations = ( 606 | 3A1953531EE63B7D009FDE38 /* Debug */, 607 | 3A1953541EE63B7D009FDE38 /* Release */, 608 | ); 609 | defaultConfigurationIsVisible = 0; 610 | defaultConfigurationName = Release; 611 | }; 612 | 3A1953551EE63B7D009FDE38 /* Build configuration list for PBXNativeTarget "WZTWebViewTests" */ = { 613 | isa = XCConfigurationList; 614 | buildConfigurations = ( 615 | 3A1953561EE63B7D009FDE38 /* Debug */, 616 | 3A1953571EE63B7D009FDE38 /* Release */, 617 | ); 618 | defaultConfigurationIsVisible = 0; 619 | defaultConfigurationName = Release; 620 | }; 621 | 3A1953581EE63B7D009FDE38 /* Build configuration list for PBXNativeTarget "WZTWebViewUITests" */ = { 622 | isa = XCConfigurationList; 623 | buildConfigurations = ( 624 | 3A1953591EE63B7D009FDE38 /* Debug */, 625 | 3A19535A1EE63B7D009FDE38 /* Release */, 626 | ); 627 | defaultConfigurationIsVisible = 0; 628 | defaultConfigurationName = Release; 629 | }; 630 | /* End XCConfigurationList section */ 631 | 632 | /* Begin XCVersionGroup section */ 633 | 3A1953311EE63B7D009FDE38 /* WZTWebView.xcdatamodeld */ = { 634 | isa = XCVersionGroup; 635 | children = ( 636 | 3A1953321EE63B7D009FDE38 /* WZTWebView.xcdatamodel */, 637 | ); 638 | currentVersion = 3A1953321EE63B7D009FDE38 /* WZTWebView.xcdatamodel */; 639 | path = WZTWebView.xcdatamodeld; 640 | sourceTree = ""; 641 | versionGroupType = wrapper.xcdatamodel; 642 | }; 643 | /* End XCVersionGroup section */ 644 | }; 645 | rootObject = 3A19531A1EE63B7D009FDE38 /* Project object */; 646 | } 647 | -------------------------------------------------------------------------------- /WZTWebView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WZTWebView.xcodeproj/project.xcworkspace/xcuserdata/Beck.Wang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView.xcodeproj/project.xcworkspace/xcuserdata/Beck.Wang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /WZTWebView.xcodeproj/xcuserdata/BY-iMac.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /WZTWebView.xcodeproj/xcuserdata/BY-iMac.xcuserdatad/xcschemes/WZTWebView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /WZTWebView.xcodeproj/xcuserdata/BY-iMac.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WZTWebView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 3A1953211EE63B7D009FDE38 16 | 17 | primary 18 | 19 | 20 | 3A19533D1EE63B7D009FDE38 21 | 22 | primary 23 | 24 | 25 | 3A1953481EE63B7D009FDE38 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /WZTWebView.xcodeproj/xcuserdata/Beck.Wang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /WZTWebView.xcodeproj/xcuserdata/Beck.Wang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WZTWebView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /WZTWebView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WZTWebView 4 | // 5 | // Created by BY-iMac on 17/6/6. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | 16 | @property (readonly, strong) NSPersistentContainer *persistentContainer; 17 | 18 | - (void)saveContext; 19 | 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /WZTWebView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WZTWebView 4 | // 5 | // Created by BY-iMac on 17/6/6. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "BrowserViewCtrlBase.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[BrowserViewCtrlBase alloc] initWithTitle:@"http://www.cocoachina.com/cms/wap.php" strTitle:@"加载H5并截图"]]; 21 | self.window.backgroundColor = [UIColor whiteColor]; 22 | return YES; 23 | } 24 | 25 | 26 | - (void)applicationWillResignActive:(UIApplication *)application { 27 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 28 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 29 | } 30 | 31 | 32 | - (void)applicationDidEnterBackground:(UIApplication *)application { 33 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | 38 | - (void)applicationWillEnterForeground:(UIApplication *)application { 39 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 40 | } 41 | 42 | 43 | - (void)applicationDidBecomeActive:(UIApplication *)application { 44 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 45 | } 46 | 47 | 48 | - (void)applicationWillTerminate:(UIApplication *)application { 49 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 50 | // Saves changes in the application's managed object context before the application terminates. 51 | [self saveContext]; 52 | } 53 | 54 | 55 | #pragma mark - Core Data stack 56 | 57 | @synthesize persistentContainer = _persistentContainer; 58 | 59 | - (NSPersistentContainer *)persistentContainer { 60 | // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it. 61 | @synchronized (self) { 62 | if (_persistentContainer == nil) { 63 | _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"WZTWebView"]; 64 | [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) { 65 | if (error != nil) { 66 | // Replace this implementation with code to handle the error appropriately. 67 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 68 | 69 | /* 70 | Typical reasons for an error here include: 71 | * The parent directory does not exist, cannot be created, or disallows writing. 72 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 73 | * The device is out of space. 74 | * The store could not be migrated to the current model version. 75 | Check the error message to determine what the actual problem was. 76 | */ 77 | NSLog(@"Unresolved error %@, %@", error, error.userInfo); 78 | abort(); 79 | } 80 | }]; 81 | } 82 | } 83 | 84 | return _persistentContainer; 85 | } 86 | 87 | #pragma mark - Core Data Saving support 88 | 89 | - (void)saveContext { 90 | NSManagedObjectContext *context = self.persistentContainer.viewContext; 91 | NSError *error = nil; 92 | if ([context hasChanges] && ![context save:&error]) { 93 | // Replace this implementation with code to handle the error appropriately. 94 | // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 95 | NSLog(@"Unresolved error %@, %@", error, error.userInfo); 96 | abort(); 97 | } 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /WZTWebView/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /WZTWebView/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /WZTWebView/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /WZTWebView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /WZTWebView/Icon/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/empty.png -------------------------------------------------------------------------------- /WZTWebView/Icon/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/full.png -------------------------------------------------------------------------------- /WZTWebView/Icon/icon-save-local@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/icon-save-local@2x.png -------------------------------------------------------------------------------- /WZTWebView/Icon/icon-save-local@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/icon-save-local@3x.png -------------------------------------------------------------------------------- /WZTWebView/Icon/icon-save-local@x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/icon-save-local@x.png -------------------------------------------------------------------------------- /WZTWebView/Icon/icon-send@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/icon-send@2x.png -------------------------------------------------------------------------------- /WZTWebView/Icon/icon-send@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/icon-send@3x.png -------------------------------------------------------------------------------- /WZTWebView/Icon/icon-send@x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/icon-send@x.png -------------------------------------------------------------------------------- /WZTWebView/Icon/step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/step1.png -------------------------------------------------------------------------------- /WZTWebView/Icon/step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/step2.png -------------------------------------------------------------------------------- /WZTWebView/Icon/step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeckWang0912/WZTWebView/2e98ccf58ff61ae51158643b68e16e96e97cc373/WZTWebView/Icon/step3.png -------------------------------------------------------------------------------- /WZTWebView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | NSCameraUsageDescription 29 | app需要访问您的相机 30 | NSLocationWhenInUseUsageDescription 31 | app需要在您使用的时候获取您的地理位置 32 | NSMicrophoneUsageDescription 33 | app需要访问你的麦克风 34 | NSPhotoLibraryUsageDescription 35 | app需要访问您的媒体库 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIMainStoryboardFile 39 | Main 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /WZTWebView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // WZTWebView 4 | // 5 | // Created by BY-iMac on 17/6/6. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /WZTWebView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // WZTWebView 4 | // 5 | // Created by BY-iMac on 17/6/6. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | 23 | - (void)didReceiveMemoryWarning { 24 | [super didReceiveMemoryWarning]; 25 | // Dispose of any resources that can be recreated. 26 | } 27 | 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /WZTWebView/WZTWebView.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | WZTWebView.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /WZTWebView/WZTWebView.xcdatamodeld/WZTWebView.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /WZTWebView/WebView/BrowserViewCtrlBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // BrowserViewCtrlBase.h 3 | // BoyingInstallment 4 | // 5 | // Created by beck.wang on 16/8/24. 6 | // Copyright © 2016年 beck.wang. All rights reserved. 7 | // 网页(h5)页面基类 8 | // 特别提醒:针对有些页面头部会重复截取的问题,可以和前端开发约定方案解决,如: 9 | // 在点击生成截图的时候,前端js隐藏头部,或者把头部和滚动内容放在一个层里 10 | 11 | #import 12 | 13 | @interface BrowserViewCtrlBase : UIViewController 14 | 15 | // 是否显示加载进度 16 | @property (nonatomic,assign)BOOL canShowProgress; 17 | // 是否截图保存 18 | @property (nonatomic,assign)BOOL canCutSavePic; 19 | // 是否需要刷新 20 | @property (nonatomic,assign)BOOL needReload; 21 | // H5 Url 22 | @property (nonatomic,copy)NSString *strUrlPath; 23 | 24 | - (instancetype)initWithTitle:(NSString*)strPath strTitle:(NSString*)strTitle; 25 | 26 | - (void)webGoBack:(UIButton*)sender; 27 | 28 | - (void)commonGoBack:(UIButton*)sender; 29 | 30 | - (BOOL)jsFunctionDo:(NSString*)strJsFunction; 31 | 32 | - (void)shareWebView:(id)sender; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /WZTWebView/WebView/BrowserViewCtrlBase.m: -------------------------------------------------------------------------------- 1 | // 2 | // BrowserViewCtrlBase.m 3 | // BoyingInstallment 4 | // 5 | // Created by beck.wang on 16/8/24. 6 | // Copyright © 2016年 sboying. All rights reserved. 7 | // 网页(h5)页面基类 8 | 9 | #import "BrowserViewCtrlBase.h" 10 | #import "ZTWebView.h" 11 | #import "WebviewPictureViewCtrl.h" 12 | 13 | @interface BrowserViewCtrlBase () 14 | @property (nonatomic,strong)UIScrollView *mainScrollView; 15 | @property (nonatomic,strong)ZTWebView *viewWeb; 16 | @end 17 | 18 | @implementation BrowserViewCtrlBase 19 | 20 | #pragma mark - life cycle 21 | - (instancetype)initWithTitle:(NSString*)strPath strTitle:(NSString*)strTitle{ 22 | self = [super init]; 23 | @synchronized (self) { 24 | self.canShowProgress = YES; 25 | self.canCutSavePic = YES; 26 | self.needReload = NO; 27 | self.title = strTitle; 28 | _strUrlPath = strPath; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]; 36 | 37 | [self.view addSubview:self.mainScrollView]; 38 | [self.mainScrollView addSubview:self.viewWeb]; 39 | [self.viewWeb setCanShowProgress:self.canShowProgress]; 40 | 41 | if(self.canCutSavePic){ 42 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(shareWebView:)]; 43 | } 44 | 45 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadWebView:)]; 46 | 47 | [self loadData:_strUrlPath]; 48 | } 49 | 50 | - (void)viewWillAppear:(BOOL)animated{ 51 | [super viewWillAppear:animated]; 52 | self.navigationItem.title = self.title; 53 | 54 | if([self.title isEqualToString:@""]){ //使用H5自带导航头部 55 | UIView *statusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KS_Width, 20)]; 56 | statusBarView.backgroundColor=[UIColor whiteColor]; 57 | [self.view addSubview:statusBarView]; 58 | [self.navigationController setNavigationBarHidden:YES animated:NO]; 59 | } 60 | else{ // 使用原生导航头部 61 | [self.navigationController setNavigationBarHidden:NO animated:NO]; 62 | } 63 | } 64 | 65 | - (void)viewWillDisappear:(BOOL)animated{ 66 | [super viewWillDisappear:animated]; 67 | self.navigationItem.title = @""; 68 | [self.viewWeb loadHTMLString:@" " baseURL:nil]; 69 | } 70 | 71 | - (void)didReceiveMemoryWarning { 72 | [super didReceiveMemoryWarning]; 73 | float ver = [[[UIDevice currentDevice] systemVersion] floatValue]; 74 | if(ver >= 6.0f){ 75 | if(self.isViewLoaded && !self.view.window){ 76 | self.view = nil; //确保下次重新加载 77 | } 78 | } 79 | } 80 | 81 | - (void)dealloc{ 82 | NSLog(@"webview测试dealloc调用"); 83 | } 84 | 85 | #pragma mark - UIWebViewDelegate 86 | - (BOOL)zt_webView:(id)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(ZTWebViewNavType)navigationType{ 87 | 88 | if (webView != self.viewWeb) { 89 | return YES; 90 | } 91 | 92 | NSString *strUrl = [[request URL] absoluteString]; 93 | 94 | if ([strUrl isEqualToString:@"about:blank"]){ 95 | return NO; 96 | } 97 | 98 | return [self jsFunctionDo:strUrl]; 99 | } 100 | 101 | - (void)zt_webViewDidStartLoad:(id)webView{ 102 | NSLog(@"开始加载"); 103 | } 104 | 105 | - (void)zt_webViewDidFinishLoad:(id)webView{ 106 | NSLog(@"加载完成"); 107 | } 108 | 109 | - (void)zt_webView:(id)webView didFailLoadWithError:(NSError *)error{ 110 | NSLog(@"加载失败"); 111 | if([error code] == NSURLErrorCancelled){ 112 | return; 113 | }else if (error.code == NSURLErrorCannotFindHost){ 114 | if ([[[UIDevice currentDevice]systemVersion] floatValue] <= 8.0) { 115 | UIAlertView *tmpAlertView = [[UIAlertView alloc] initWithTitle:@"" message:@"网络不稳定,请切换网络环境重试!" delegate:self cancelButtonTitle:@"知道了" otherButtonTitles:nil]; 116 | [tmpAlertView show]; 117 | }else { 118 | UIAlertController *alterVc = [UIAlertController alertControllerWithTitle:@"" message:@"网络不稳定,请切换网络环境重试!" preferredStyle:UIAlertControllerStyleAlert]; 119 | UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]; 120 | [alterVc addAction:okAction]; 121 | [self presentViewController:alterVc animated:YES completion:nil]; 122 | } 123 | }else{ 124 | if ([[[UIDevice currentDevice]systemVersion] floatValue] <= 8.0) { 125 | UIAlertView *tmpAlertView = [[UIAlertView alloc] initWithTitle:@"" message:@"页面丢失!" delegate:self cancelButtonTitle:@"知道了" otherButtonTitles:nil]; 126 | [tmpAlertView show]; 127 | }else { 128 | UIAlertController *alterVc = [UIAlertController alertControllerWithTitle:@"" message:@"页面丢失!" preferredStyle:UIAlertControllerStyleAlert]; 129 | UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]; 130 | [alterVc addAction:okAction]; 131 | [self presentViewController:alterVc animated:YES completion:nil]; 132 | } 133 | } 134 | } 135 | 136 | #pragma mark - BrowserViewCtrlDelegate 137 | // JS返回函数 138 | - (BOOL)jsFunctionDo:(NSString*)strJsFunction{ 139 | if (!strJsFunction || [strJsFunction length] <= 0 ||[strJsFunction isEqualToString:@"about:blank"]){ 140 | return NO; 141 | } 142 | BOOL isDo = YES; 143 | 144 | // 这里可以处理与js简单的交互,通用处理在基类即可,如回退操作,jsGoBack()是与前端人员约定的调用函数,具体到每个不同的页面可以在子类中复写该函数 145 | NSRange range = [strJsFunction rangeOfString:@"jsGoBack"]; 146 | if (range.location != NSNotFound) { 147 | [self.navigationController popViewControllerAnimated:YES]; 148 | isDo = NO; 149 | } 150 | 151 | return isDo; 152 | } 153 | 154 | #pragma mark - 数据加载 155 | - (void)loadData:(NSString*)tagertUrl{ 156 | NSURL *url = [NSURL URLWithString:tagertUrl]; 157 | NSURLRequest *request = [NSURLRequest requestWithURL:url]; 158 | request = [NSURLRequest requestWithURL:[NSURL URLWithString:request.URL.absoluteString]]; 159 | [self.viewWeb loadRequest:request]; 160 | } 161 | 162 | #pragma mark - getters and setters 163 | - (UIScrollView*)mainScrollView{ 164 | if (!_mainScrollView) { 165 | CGRect rect = [UIScreen mainScreen].bounds; 166 | _mainScrollView = [[UIScrollView alloc] initWithFrame:rect]; 167 | } 168 | return _mainScrollView; 169 | } 170 | 171 | - (ZTWebView*)viewWeb{ 172 | if (!_viewWeb) { 173 | ZTWebViewConfiguration *configuration = [[ZTWebViewConfiguration alloc] init]; 174 | configuration.scalesPageToFit = YES; 175 | configuration.loadingHUD = YES; 176 | configuration.captureImage = NO; // 是否捕获H5内的image 177 | configuration.progressColor = [UIColor redColor]; 178 | _viewWeb = [ZTWebView webViewWithFrame:[UIScreen mainScreen].bounds configuration:configuration]; 179 | _viewWeb.delegate = self; 180 | _viewWeb.scrollView.showsVerticalScrollIndicator = NO; 181 | _viewWeb.scrollView.showsHorizontalScrollIndicator = NO; 182 | } 183 | return _viewWeb; 184 | } 185 | 186 | #pragma mark - private mthod 187 | - (void)webGoBack:(UIButton *)sender{ 188 | return [self.viewWeb canGoBack] ? [self.viewWeb goBack] : [self commonGoBack:nil]; 189 | } 190 | 191 | - (void)commonGoBack:(UIButton*)sender{ 192 | CATransition* transition = [CATransition animation]; 193 | transition.duration = 0.3; 194 | transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 195 | transition.type = kCATransitionPush; 196 | [self.navigationController.view.layer addAnimation:transition forKey:nil]; 197 | [self.navigationController popViewControllerAnimated:YES]; 198 | } 199 | 200 | - (void)shareWebView:(id)sender{ 201 | BIWeakObj(self) 202 | if(isWKWebView){ // >= iOS8 WKWebView截图生成长图 203 | [self.viewWeb ZTWKWebViewScrollCaptureCompletionHandler:^(UIImage *capturedImage) { 204 | [selfWeak shareForCutPIC:capturedImage]; 205 | }]; 206 | }else{ 207 | // <=iOS7 UIWebView截图生成长图 208 | CGRect snapshotFrame = CGRectMake(0, 0, selfWeak.viewWeb.scrollView.contentSize.width, selfWeak.viewWeb.scrollView.contentSize.height); 209 | UIEdgeInsets snapshotEdgeInsets = UIEdgeInsetsZero; 210 | [self.viewWeb ZTUIWebViewScrollCaptureCompletionHandler:snapshotFrame withCapInsets:snapshotEdgeInsets completionHandler:^(UIImage *capturedImage) { 211 | [selfWeak shareForCutPIC:capturedImage]; 212 | }]; 213 | } 214 | } 215 | 216 | -(void)shareForCutPIC:(UIImage*)shareImage{ 217 | WebviewPictureViewCtrl *picCtrl = [[WebviewPictureViewCtrl alloc] init]; 218 | [picCtrl setWebImg:shareImage andUrl:_strUrlPath]; 219 | [self presentViewController:[[UINavigationController alloc] initWithRootViewController:picCtrl] animated:YES completion:nil]; 220 | } 221 | 222 | - (void)reloadWebView:(id)sender{ 223 | [self.viewWeb reload]; 224 | } 225 | 226 | @end 227 | -------------------------------------------------------------------------------- /WZTWebView/WebView/NJKWebViewProgress.h: -------------------------------------------------------------------------------- 1 | // 2 | // NJKWebViewProgress.h 3 | // 4 | // Created by Satoshi Aasano on 4/20/13. 5 | // Copyright (c) 2013 Satoshi Asano. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #undef njk_weak 11 | #if __has_feature(objc_arc_weak) 12 | #define njk_weak weak 13 | #else 14 | #define njk_weak unsafe_unretained 15 | #endif 16 | 17 | extern const float NJKInitialProgressValue; 18 | extern const float NJKInteractiveProgressValue; 19 | extern const float NJKFinalProgressValue; 20 | 21 | typedef void (^NJKWebViewProgressBlock)(float progress); 22 | @protocol NJKWebViewProgressDelegate; 23 | @interface NJKWebViewProgress : NSObject 24 | @property (nonatomic, njk_weak) idprogressDelegate; 25 | @property (nonatomic, njk_weak) idwebViewProxyDelegate; 26 | @property (nonatomic, copy) NJKWebViewProgressBlock progressBlock; 27 | @property (nonatomic, readonly) float progress; // 0.0..1.0 28 | 29 | - (void)reset; 30 | @end 31 | 32 | @protocol NJKWebViewProgressDelegate 33 | - (void)webViewProgress:(NJKWebViewProgress *)webViewProgress updateProgress:(float)progress; 34 | @end 35 | 36 | -------------------------------------------------------------------------------- /WZTWebView/WebView/NJKWebViewProgress.m: -------------------------------------------------------------------------------- 1 | // 2 | // NJKWebViewProgress.m 3 | // 4 | // Created by Satoshi Aasano on 4/20/13. 5 | // Copyright (c) 2013 Satoshi Asano. All rights reserved. 6 | // 7 | 8 | #import "NJKWebViewProgress.h" 9 | 10 | NSString *completeRPCURL = @"webviewprogressproxy:///complete"; 11 | 12 | const float NJKInitialProgressValue = 0.1f; 13 | const float NJKInteractiveProgressValue = 0.5f; 14 | const float NJKFinalProgressValue = 0.9f; 15 | 16 | @implementation NJKWebViewProgress 17 | { 18 | NSUInteger _loadingCount; 19 | NSUInteger _maxLoadCount; 20 | NSURL *_currentURL; 21 | BOOL _interactive; 22 | } 23 | 24 | - (id)init 25 | { 26 | self = [super init]; 27 | if (self) { 28 | _maxLoadCount = _loadingCount = 0; 29 | _interactive = NO; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)startProgress 35 | { 36 | if (_progress < NJKInitialProgressValue) { 37 | [self setProgress:NJKInitialProgressValue]; 38 | } 39 | } 40 | 41 | - (void)incrementProgress 42 | { 43 | float progress = self.progress; 44 | float maxProgress = _interactive ? NJKFinalProgressValue : NJKInteractiveProgressValue; 45 | float remainPercent = (float)_loadingCount / (float)_maxLoadCount; 46 | float increment = (maxProgress - progress) * remainPercent; 47 | progress += increment; 48 | progress = fmin(progress, maxProgress); 49 | [self setProgress:progress]; 50 | } 51 | 52 | - (void)completeProgress 53 | { 54 | [self setProgress:1.0]; 55 | } 56 | 57 | - (void)setProgress:(float)progress 58 | { 59 | // progress should be incremental only 60 | if (progress > _progress || progress == 0) { 61 | _progress = progress; 62 | if ([_progressDelegate respondsToSelector:@selector(webViewProgress:updateProgress:)]) { 63 | [_progressDelegate webViewProgress:self updateProgress:progress]; 64 | } 65 | if (_progressBlock) { 66 | _progressBlock(progress); 67 | } 68 | } 69 | } 70 | 71 | - (void)reset 72 | { 73 | _maxLoadCount = _loadingCount = 0; 74 | _interactive = NO; 75 | [self setProgress:0.0]; 76 | } 77 | 78 | #pragma mark - 79 | #pragma mark UIWebViewDelegate 80 | 81 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 82 | { 83 | if ([request.URL.absoluteString isEqualToString:completeRPCURL]) { 84 | [self completeProgress]; 85 | return NO; 86 | } 87 | 88 | BOOL ret = YES; 89 | if ([_webViewProxyDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { 90 | ret = [_webViewProxyDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; 91 | } 92 | 93 | BOOL isFragmentJump = NO; 94 | if (request.URL.fragment) { 95 | NSString *nonFragmentURL = [request.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:request.URL.fragment] withString:@""]; 96 | isFragmentJump = [nonFragmentURL isEqualToString:webView.request.URL.absoluteString]; 97 | } 98 | 99 | BOOL isTopLevelNavigation = [request.mainDocumentURL isEqual:request.URL]; 100 | 101 | BOOL isHTTP = [request.URL.scheme isEqualToString:@"http"] || [request.URL.scheme isEqualToString:@"https"]; 102 | if (ret && !isFragmentJump && isHTTP && isTopLevelNavigation) { 103 | _currentURL = request.URL; 104 | [self reset]; 105 | } 106 | return ret; 107 | } 108 | 109 | - (void)webViewDidStartLoad:(UIWebView *)webView 110 | { 111 | if ([_webViewProxyDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) { 112 | [_webViewProxyDelegate webViewDidStartLoad:webView]; 113 | } 114 | 115 | _loadingCount++; 116 | _maxLoadCount = fmax(_maxLoadCount, _loadingCount); 117 | 118 | [self startProgress]; 119 | } 120 | 121 | - (void)webViewDidFinishLoad:(UIWebView *)webView 122 | { 123 | if ([_webViewProxyDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { 124 | [_webViewProxyDelegate webViewDidFinishLoad:webView]; 125 | } 126 | 127 | _loadingCount--; 128 | [self incrementProgress]; 129 | 130 | NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; 131 | 132 | BOOL interactive = [readyState isEqualToString:@"interactive"]; 133 | if (interactive) { 134 | _interactive = YES; 135 | NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@'; document.body.appendChild(iframe); }, false);", completeRPCURL]; 136 | [webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS]; 137 | } 138 | 139 | BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL]; 140 | BOOL complete = [readyState isEqualToString:@"complete"]; 141 | if (complete && isNotRedirect) { 142 | [self completeProgress]; 143 | } 144 | } 145 | 146 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 147 | { 148 | if ([_webViewProxyDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) { 149 | [_webViewProxyDelegate webView:webView didFailLoadWithError:error]; 150 | } 151 | 152 | _loadingCount--; 153 | [self incrementProgress]; 154 | 155 | NSString *readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; 156 | 157 | BOOL interactive = [readyState isEqualToString:@"interactive"]; 158 | if (interactive) { 159 | _interactive = YES; 160 | NSString *waitForCompleteJS = [NSString stringWithFormat:@"window.addEventListener('load',function() { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = '%@'; document.body.appendChild(iframe); }, false);", completeRPCURL]; 161 | [webView stringByEvaluatingJavaScriptFromString:waitForCompleteJS]; 162 | } 163 | 164 | BOOL isNotRedirect = _currentURL && [_currentURL isEqual:webView.request.mainDocumentURL]; 165 | BOOL complete = [readyState isEqualToString:@"complete"]; 166 | if (complete && isNotRedirect) { 167 | [self completeProgress]; 168 | } 169 | } 170 | 171 | #pragma mark - 172 | #pragma mark Method Forwarding 173 | // for future UIWebViewDelegate impl 174 | 175 | - (BOOL)respondsToSelector:(SEL)aSelector 176 | { 177 | if ( [super respondsToSelector:aSelector] ) 178 | return YES; 179 | 180 | if ([self.webViewProxyDelegate respondsToSelector:aSelector]) 181 | return YES; 182 | 183 | return NO; 184 | } 185 | 186 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector 187 | { 188 | NSMethodSignature *signature = [super methodSignatureForSelector:selector]; 189 | if(!signature) { 190 | if([_webViewProxyDelegate respondsToSelector:selector]) { 191 | return [(NSObject *)_webViewProxyDelegate methodSignatureForSelector:selector]; 192 | } 193 | } 194 | return signature; 195 | } 196 | 197 | - (void)forwardInvocation:(NSInvocation*)invocation 198 | { 199 | if ([_webViewProxyDelegate respondsToSelector:[invocation selector]]) { 200 | [invocation invokeWithTarget:_webViewProxyDelegate]; 201 | } 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /WZTWebView/WebView/WebviewPictureViewCtrl.h: -------------------------------------------------------------------------------- 1 | // 2 | // WebviewPictureViewCtrl.h 3 | // BoyingInstallment 4 | // 5 | // Created by beck.wang on 17/3/29. 6 | // Copyright © 2017年 beck.wang All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WebviewPictureViewCtrl : UIViewController 12 | 13 | - (void)setWebImg:(UIImage*)webImg andUrl:(NSString*)url; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /WZTWebView/WebView/WebviewPictureViewCtrl.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebviewPictureViewCtrl.m 3 | // BoyingInstallment 4 | // 5 | // Created by beck.wang on 17/3/29. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 8 | 9 | #import "WebviewPictureViewCtrl.h" 10 | 11 | // 屏幕尺寸 12 | #define KS_Width [UIScreen mainScreen].bounds.size.width 13 | #define KS_Heigth [UIScreen mainScreen].bounds.size.height 14 | 15 | @interface WebviewPictureViewCtrl () 16 | @property (nonatomic,strong) UIScrollView *scrollView; 17 | @property (nonatomic,strong) UIImageView *imageView; 18 | @property (nonatomic,strong) UIImage *webImg; 19 | @property (nonatomic,copy) NSString *strUrl; 20 | @property (nonatomic,strong) UIButton *btnShare; 21 | @property (nonatomic,strong) UIButton *btnSave; 22 | @end 23 | 24 | @implementation WebviewPictureViewCtrl 25 | 26 | -(void)setWebImg:(UIImage *)webImg andUrl:(NSString *)url{ 27 | _webImg = webImg; 28 | _strUrl = url; 29 | } 30 | 31 | - (void)viewWillAppear:(BOOL)animated{ 32 | [super viewWillAppear:animated]; 33 | self.navigationItem.title = @"图片预览"; 34 | [self setHidesBottomBarWhenPushed:YES]; 35 | } 36 | 37 | - (void)viewDidLoad { 38 | [super viewDidLoad]; 39 | 40 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(closeCtrl:)]; 41 | 42 | [self.view addSubview:self.scrollView]; 43 | [self.scrollView addSubview:self.imageView]; 44 | [self.view addSubview:self.btnShare]; 45 | [self.view addSubview:self.btnSave]; 46 | self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, self.imageView.frame.origin.y + self.imageView.frame.size.height - (KS_Heigth -30), 0); 47 | } 48 | 49 | #pragma mark - getter & setter 50 | - (UIScrollView*)scrollView{ 51 | if (!_scrollView) { 52 | _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 64 , KS_Width, KS_Heigth)]; 53 | [_scrollView setContentSize:CGSizeMake(KS_Width, KS_Heigth + 80)]; 54 | _scrollView.userInteractionEnabled = YES; 55 | _scrollView.scrollEnabled = YES; 56 | _scrollView.showsHorizontalScrollIndicator = NO; 57 | _scrollView.showsVerticalScrollIndicator = NO; 58 | } 59 | return _scrollView; 60 | } 61 | 62 | -(UIImageView*)imageView{ 63 | if (!_imageView) { 64 | CGRect rect = [UIScreen mainScreen].bounds; 65 | rect.origin.y -= 64; 66 | rect.size.height = _webImg.size.height; 67 | _imageView.backgroundColor = [UIColor whiteColor]; 68 | _imageView = [[UIImageView alloc] initWithFrame:rect]; 69 | _imageView.image = _webImg; 70 | } 71 | return _imageView; 72 | } 73 | 74 | -(UIButton*)btnShare{ 75 | if (!_btnShare) { 76 | _btnShare = [UIButton buttonWithType:UIButtonTypeCustom]; 77 | _btnShare.frame = CGRectMake(0, KS_Heigth - 52, KS_Width/2, 52); 78 | [_btnShare setImage:[UIImage imageNamed:@"icon-send"] forState:UIControlStateNormal]; 79 | [_btnShare setTitle:@"发送给朋友" forState:UIControlStateNormal]; 80 | [_btnShare setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 81 | [_btnShare setTitleColor:[UIColor blueColor] forState:UIControlStateSelected]; 82 | _btnShare.titleLabel.font = [UIFont systemFontOfSize:18.0f]; 83 | _btnShare.backgroundColor = [UIColor colorWithWhite:0.f alpha:0.7f]; 84 | 85 | _btnShare.imageEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 25); 86 | _btnShare.titleEdgeInsets = UIEdgeInsetsMake(0, _btnShare.imageView.frame.size.width+5, 0, 0); 87 | [_btnShare addTarget:self action:@selector(goShare:) forControlEvents:UIControlEventTouchUpInside]; 88 | 89 | UIButton *line = [[UIButton alloc] initWithFrame:CGRectMake(KS_Width/2-1, 10, 1, 32)]; 90 | line.backgroundColor = [UIColor lightGrayColor]; 91 | [_btnShare addSubview:line]; 92 | } 93 | return _btnShare; 94 | } 95 | 96 | -(UIButton*)btnSave{ 97 | if (!_btnSave) { 98 | _btnSave = [UIButton buttonWithType:UIButtonTypeCustom]; 99 | _btnSave.frame = CGRectMake(KS_Width/2, KS_Heigth - 52, KS_Width/2,52); 100 | [_btnSave setImage:[UIImage imageNamed:@"icon-save-local"] forState:UIControlStateNormal]; 101 | [_btnSave setTitle:@"保存到相册" forState:UIControlStateNormal]; 102 | [_btnSave setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 103 | [_btnSave setTitleColor:[UIColor blueColor] forState:UIControlStateSelected]; 104 | _btnSave.titleLabel.font = [UIFont systemFontOfSize:18.0f]; 105 | _btnSave.backgroundColor = [UIColor colorWithWhite:0.f alpha:0.7f]; 106 | 107 | _btnSave.imageEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 25); 108 | _btnSave.titleEdgeInsets = UIEdgeInsetsMake(0, _btnSave.imageView.frame.size.width + 5, 0, 0); 109 | [_btnSave addTarget:self action:@selector(goSave:) forControlEvents:UIControlEventTouchUpInside]; 110 | } 111 | return _btnSave; 112 | } 113 | 114 | -(void)cancelShare:(id)sender{ 115 | [self dismissViewControllerAnimated:YES completion:^{ 116 | NSLog(@"取消分享图片"); 117 | }]; 118 | } 119 | 120 | // 分享图片 121 | -(void)goShare:(id)sender{ 122 | NSLog(@"分享图片"); 123 | } 124 | 125 | // 保存图片 126 | -(void)goSave:(id)sender{ 127 | UIImageWriteToSavedPhotosAlbum(_webImg, self, NULL, NULL); 128 | if ([[[UIDevice currentDevice]systemVersion] floatValue] <= 8.0) { 129 | UIAlertView *tmpAlertView = [[UIAlertView alloc] initWithTitle:@"" message:@"保存到相册成功!" delegate:self cancelButtonTitle:@"知道了" otherButtonTitles:nil]; 130 | [tmpAlertView show]; 131 | }else { 132 | UIAlertController *alterVc = [UIAlertController alertControllerWithTitle:@"" message:@"保存到相册成功!" preferredStyle:UIAlertControllerStyleAlert]; 133 | UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]; 134 | [alterVc addAction:okAction]; 135 | [self presentViewController:alterVc animated:YES completion:nil]; 136 | } 137 | } 138 | 139 | - (void)closeCtrl:(id)sender{ 140 | [self dismissViewControllerAnimated:YES completion:nil]; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /WZTWebView/WebView/ZTWebView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZTWebView.h 3 | // WZTWebView 4 | // 5 | // Created by beck.wang on 17/6/6. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 集合类:综合iOS8之前的UIWebView和之后的WKWebView,并集成了H5页面截取长图和长按内容截取短图功能 8 | 9 | #import 10 | 11 | #define isWKWebView NSClassFromString(@"WKWebView") 12 | #define KS_Width [UIScreen mainScreen].bounds.size.width 13 | #define KS_Heigth [UIScreen mainScreen].bounds.size.height 14 | #define BIWeakObj(o) @autoreleasepool {} __weak typeof(o) o ## Weak = o; 15 | #define BIStrongObj(o) @autoreleasepool {} __strong typeof(o) o = o ## Weak; 16 | 17 | /** 18 | 导航栏菜单类型 19 | */ 20 | typedef NS_ENUM(NSInteger,ZTWebViewNavType) { 21 | ZTWebViewNavLinkClicked, 22 | ZTWebViewNavFormSubmitted, 23 | ZTWebViewNavBackForward, 24 | ZTWebViewNavReload, 25 | ZTWebViewNavResubmitted, 26 | ZTWebViewNavOther = -1 27 | }; 28 | 29 | @class ZTWebView; 30 | 31 | /** 32 | 定义ZTWebView协议 33 | */ 34 | @protocol ZTWebViewProtocol 35 | @optional 36 | @property (nonatomic, readonly, strong) UIScrollView *scrollView; 37 | @property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack; 38 | @property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward; 39 | @property (nonatomic, readonly, getter=isLoading) BOOL loading; 40 | // use KVO 41 | @property (nonatomic, readonly, copy) NSString *title; 42 | // use KVO 43 | @property (nonatomic, readonly) double estimatedProgress; 44 | // use KVO 45 | @property (nonatomic, readonly) float pageHeight; 46 | // webview's images (images = nil when captureImage is NO) 47 | @property (nonatomic, readonly, copy) NSArray * images; 48 | 49 | - (void)loadRequest:(NSURLRequest *)request; 50 | - (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL; 51 | - (void)reload; 52 | - (void)stopLoading; 53 | - (void)goBack; 54 | - (void)goForward; 55 | - (void)zt_evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^)(id, NSError*))completionHandler; 56 | @end 57 | 58 | /** 59 | 定义ZTWebView代理 60 | */ 61 | @protocol ZTWebViewDelegate 62 | @optional 63 | - (BOOL)zt_webView:(id)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(ZTWebViewNavType)navigationType; 64 | - (void)zt_webViewDidStartLoad:(id)webView; 65 | - (void)zt_webViewDidFinishLoad:(id)webView; 66 | - (void)zt_webView:(id)webView didFailLoadWithError:(NSError *)error; 67 | @end 68 | 69 | /** 70 | 定义ZTWebView配置选项 71 | */ 72 | @interface ZTWebViewConfiguration : NSObject 73 | @property (nonatomic,assign) BOOL allowsInlineMediaPlayback; // iPhone Safari defaults to NO. iPad Safari defaults to YES 74 | @property (nonatomic,assign) BOOL mediaPlaybackRequiresUserAction; // iPhone and iPad Safari both default to YES 75 | @property (nonatomic,assign) BOOL mediaPlaybackAllowsAirPlay; // iPhone and iPad Safari both default to YES 76 | @property (nonatomic,assign) BOOL suppressesIncrementalRendering; // iPhone and iPad Safari both default to NO 77 | @property (nonatomic,assign) BOOL scalesPageToFit; 78 | @property (nonatomic,assign) BOOL loadingHUD; //default NO ,if YES webview will add HUD when loading 79 | @property (nonatomic,assign) BOOL captureImage; //default NO ,if YES webview will capture all image in content; 80 | @property (nonatomic,strong) UIColor *progressColor; //default blue; 81 | @end 82 | 83 | 84 | @interface ZTWebView : UIView 85 | @property (nonatomic,weak) id delegate; 86 | @property (nonatomic,assign) BOOL canShowProgress; 87 | // WKWebView 初始化 88 | +(ZTWebView *)webViewWithFrame:(CGRect)frame configuration:(ZTWebViewConfiguration *)configuration; 89 | // WKWebView 滚屏生成长图 90 | - (void)ZTWKWebViewScrollCaptureCompletionHandler:(void(^)(UIImage *capturedImage))completionHandler; 91 | // UIWebView 滚屏生成长图 92 | - (void)ZTUIWebViewScrollCaptureCompletionHandler:(CGRect)rect withCapInsets:(UIEdgeInsets)capInsets completionHandler:(void(^)(UIImage *capturedImage))completionHandler; 93 | @end 94 | -------------------------------------------------------------------------------- /WZTWebView/WebView/ZTWebView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZTWebView.m 3 | // WZTWebView 4 | // 5 | // Created by beck.wang on 17/6/6. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 8 | 9 | #import "ZTWebView.h" 10 | #import 11 | #import "NJKWebViewProgress.h" 12 | 13 | #pragma mark - ZTWKWebView 14 | @interface ZTWKWebView : WKWebView 15 | 16 | @end 17 | 18 | #pragma mark - ZTUIWebView 19 | @interface ZTUIWebView : UIWebView 20 | 21 | @end 22 | 23 | #pragma mark - ZTWebViewJS 24 | @interface ZTWebViewJS : NSObject 25 | +(NSString *)scalesPageToFitJS; 26 | +(NSString *)imgsElement; 27 | @end 28 | 29 | #pragma mark - ZTWebView 30 | @interface ZTWebView () 31 | 32 | @property (nonatomic,strong) id webView; 33 | @property (nonatomic,strong) UIActivityIndicatorView *indicatorView; 34 | @property (nonatomic,strong) UIProgressView *progressView; 35 | @property (nonatomic,strong) ZTWebViewConfiguration *configuration; 36 | @property (nonatomic,copy) NSString *title; 37 | @property (nonatomic,assign) double estimatedProgress; 38 | @property (nonatomic,assign) float pageHeight; 39 | @property (nonatomic,copy) NJKWebViewProgress *webViewProgress; 40 | @property (nonatomic,copy) NSArray *images; 41 | 42 | @end 43 | 44 | @implementation ZTWebView 45 | 46 | // 初始化 47 | + (ZTWebView *)webViewWithFrame:(CGRect)frame configuration:(ZTWebViewConfiguration *)configuration{ 48 | return [[self alloc] initWithFrame:frame configuration:configuration]; 49 | } 50 | 51 | - (instancetype)initWithFrame:(CGRect)frame configuration:(ZTWebViewConfiguration *)configuration{ 52 | if (self = [super initWithFrame:frame]){ 53 | _configuration = configuration; 54 | if (isWKWebView) { // >=iOS8 WKWebView 55 | if (configuration){ 56 | WKWebViewConfiguration *webViewconfiguration = [[WKWebViewConfiguration alloc] init]; 57 | webViewconfiguration.allowsInlineMediaPlayback = configuration.allowsInlineMediaPlayback; 58 | webViewconfiguration.mediaTypesRequiringUserActionForPlayback = configuration.mediaPlaybackRequiresUserAction; 59 | webViewconfiguration.allowsAirPlayForMediaPlayback = configuration.mediaPlaybackAllowsAirPlay; 60 | webViewconfiguration.suppressesIncrementalRendering = configuration.suppressesIncrementalRendering; 61 | 62 | WKUserContentController *wkUController = [[WKUserContentController alloc] init]; 63 | 64 | if (!configuration.scalesPageToFit) { 65 | NSString *jScript = [ZTWebViewJS scalesPageToFitJS]; 66 | WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; 67 | [wkUController addUserScript:wkUScript]; 68 | WKUserScript *wkScript1 = [[WKUserScript alloc] initWithSource:[ZTWebViewJS imgsElement] injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; 69 | [wkUController addUserScript:wkScript1]; 70 | } 71 | 72 | if (configuration.captureImage) { 73 | NSString *jScript = [ZTWebViewJS imgsElement]; 74 | WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; 75 | [wkUController addUserScript:wkUScript]; 76 | } 77 | 78 | webViewconfiguration.userContentController = wkUController; 79 | _webView = (id)[[ZTWKWebView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height) configuration:webViewconfiguration]; 80 | } 81 | else{ 82 | _webView = (id)[[ZTWKWebView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)]; 83 | } 84 | 85 | [(ZTWKWebView *)_webView setNavigationDelegate:self]; 86 | 87 | // 添加KVO 88 | [(ZTWKWebView *)_webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL]; 89 | [(ZTWKWebView *)_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL]; 90 | } 91 | else{ // <=iOS7 UIWebView 92 | _webView = (id)[[ZTUIWebView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)]; 93 | 94 | if (configuration){ 95 | [(ZTUIWebView *)_webView setAllowsInlineMediaPlayback:configuration.allowsInlineMediaPlayback]; 96 | [(ZTUIWebView *)_webView setMediaPlaybackRequiresUserAction:configuration.mediaPlaybackRequiresUserAction]; 97 | [(ZTUIWebView *)_webView setMediaPlaybackAllowsAirPlay:configuration.mediaPlaybackAllowsAirPlay]; 98 | [(ZTUIWebView *)_webView setSuppressesIncrementalRendering:configuration.suppressesIncrementalRendering]; 99 | [(ZTUIWebView *)_webView setScalesPageToFit:configuration.scalesPageToFit]; 100 | } 101 | 102 | _webViewProgress = [[NJKWebViewProgress alloc] init]; 103 | [(ZTUIWebView *)_webView setDelegate:_webViewProgress]; 104 | _webViewProgress.webViewProxyDelegate = self; 105 | _webViewProgress.progressDelegate = self; 106 | } 107 | 108 | if (configuration.loadingHUD) { 109 | [(UIView *)_webView addSubview:self.indicatorView]; 110 | } 111 | 112 | [(UIView *)_webView setBackgroundColor:[UIColor clearColor]]; 113 | [self addSubview:(UIView *)_webView]; 114 | [self addSubview:self.progressView]; 115 | if (configuration) { 116 | self.progressView.tintColor = configuration.progressColor; 117 | } 118 | } 119 | return self; 120 | } 121 | 122 | #pragma mark - WKWebView 滚动生成长图 123 | - (void)ZTWKWebViewScrollCaptureCompletionHandler:(void(^)(UIImage *capturedImage))completionHandler{ 124 | // 制作了一个UIView的副本 125 | UIView *snapShotView = [self snapshotViewAfterScreenUpdates:YES]; 126 | 127 | snapShotView.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, snapShotView.frame.size.width, snapShotView.frame.size.height); 128 | 129 | [self.superview addSubview:snapShotView]; 130 | 131 | // 获取当前UIView可滚动的内容长度 132 | CGPoint scrollOffset = self.scrollView.contentOffset; 133 | 134 | // 向上取整数 - 可滚动长度与UIView本身屏幕边界坐标相差倍数 135 | float maxIndex = ceilf(self.scrollView.contentSize.height/self.bounds.size.height); 136 | 137 | // 保持清晰度 138 | UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, [UIScreen mainScreen].scale); 139 | 140 | // 滚动截图 141 | [self ZTContentScrollPageDraw:0 maxIndex:(int)maxIndex drawCallback:^{ 142 | UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext(); 143 | UIGraphicsEndImageContext(); 144 | 145 | // 恢复原UIView 146 | [self.scrollView setContentOffset:scrollOffset animated:NO]; 147 | [snapShotView removeFromSuperview]; 148 | 149 | completionHandler(capturedImage); 150 | }]; 151 | } 152 | 153 | // 滚动截图 154 | - (void)ZTContentScrollPageDraw:(int)index maxIndex:(int)maxIndex drawCallback:(void(^)(void))drawCallback{ 155 | [self.scrollView setContentOffset:CGPointMake(0, (float)index * self.frame.size.height)]; 156 | CGRect splitFrame = CGRectMake(0, (float)index * self.frame.size.height, self.bounds.size.width, self.bounds.size.height); 157 | 158 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 159 | [self drawViewHierarchyInRect:splitFrame afterScreenUpdates:YES]; 160 | if(index < maxIndex){ 161 | [self ZTContentScrollPageDraw: index + 1 maxIndex:maxIndex drawCallback:drawCallback]; 162 | }else{ 163 | drawCallback(); 164 | } 165 | }); 166 | } 167 | 168 | #pragma mark - UIWebview 滚动生成长图 169 | - (void)ZTUIWebViewScrollCaptureCompletionHandler:(CGRect)rect withCapInsets:(UIEdgeInsets)capInsets completionHandler:(void(^)(UIImage *capturedImage))completionHandler{ 170 | CGFloat scale = [UIScreen mainScreen].scale; 171 | CGFloat boundsWidth = self.bounds.size.width; 172 | CGFloat boundsHeight = self.bounds.size.height; 173 | CGFloat contentWidth = self.scrollView.contentSize.height; 174 | CGFloat contentHeight = self.scrollView.contentSize.height; 175 | CGPoint offset = self.scrollView.contentOffset; 176 | [self.scrollView setContentOffset:CGPointMake(0, 0)]; 177 | 178 | NSMutableArray *images = [NSMutableArray array]; 179 | while (contentHeight > 0) { 180 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, scale); 181 | [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 182 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 183 | UIGraphicsEndImageContext(); 184 | [images addObject:image]; 185 | 186 | CGFloat offsetY = self.scrollView.contentOffset.y; 187 | [self.scrollView setContentOffset:CGPointMake(0, offsetY + boundsHeight)]; 188 | contentHeight -= boundsHeight; 189 | } 190 | 191 | [self.scrollView setContentOffset:offset]; 192 | 193 | CGSize imageSize = CGSizeMake(contentWidth * scale, self.scrollView.contentSize.height * scale); 194 | UIGraphicsBeginImageContext(imageSize); 195 | [images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger idx, BOOL *stop) { 196 | [image drawInRect:CGRectMake(0,scale * boundsHeight * idx,scale * boundsWidth,scale * boundsHeight)]; 197 | }]; 198 | 199 | UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext(); 200 | UIGraphicsEndImageContext(); 201 | UIImageView * snapshotView = [[UIImageView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)]; 202 | 203 | snapshotView.image = [fullImage resizableImageWithCapInsets:capInsets]; 204 | completionHandler(snapshotView.image); 205 | } 206 | 207 | #pragma mark getter & setter 208 | - (UIProgressView*)progressView{ 209 | if (!_progressView) { 210 | _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 0.5)]; 211 | _progressView.tintColor = [UIColor blueColor]; 212 | _progressView.trackTintColor = [UIColor whiteColor]; 213 | } 214 | return _progressView; 215 | } 216 | 217 | - (void)setCanShowProgress:(BOOL)canShowProgress{ 218 | _canShowProgress = canShowProgress; 219 | } 220 | 221 | -(UIActivityIndicatorView *)indicatorView{ 222 | if (!_indicatorView) { 223 | _indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 224 | _indicatorView.hidesWhenStopped = YES; 225 | } 226 | return _indicatorView; 227 | } 228 | 229 | #pragma mark - ZTWebViewProtocol 230 | - (UIScrollView *)scrollView{ 231 | return _webView.scrollView; 232 | } 233 | 234 | - (void)loadRequest:(NSURLRequest *)request{ 235 | [_webView loadRequest:request]; 236 | } 237 | 238 | - (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL{ 239 | [_webView loadHTMLString:string baseURL:baseURL]; 240 | } 241 | 242 | - (void)reload{ 243 | [_webView reload]; 244 | } 245 | 246 | - (void)stopLoading{ 247 | [_webView stopLoading]; 248 | } 249 | 250 | - (void)goBack{ 251 | [_webView goBack]; 252 | } 253 | 254 | - (void)goForward{ 255 | [_webView goForward]; 256 | } 257 | 258 | - (BOOL)canGoBack{ 259 | return _webView.canGoBack; 260 | } 261 | 262 | - (BOOL)canGoForward{ 263 | return _webView.canGoForward; 264 | } 265 | 266 | - (BOOL)isLoading{ 267 | return _webView.isLoading; 268 | } 269 | 270 | - (void)zt_evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^)(id, NSError*))completionHandler{ 271 | [_webView zt_evaluateJavaScript:javaScriptString completionHandler:completionHandler]; 272 | } 273 | 274 | #pragma mark - NJKWebViewProgressDelegate 275 | - (void)webViewProgress:(NJKWebViewProgress *)webViewProgress updateProgress:(float)progress{ 276 | self.estimatedProgress = progress; 277 | } 278 | 279 | #pragma mark - KVO 280 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ 281 | 282 | if ([keyPath isEqualToString:@"title"]) { 283 | self.title = change[NSKeyValueChangeNewKey]; 284 | return; 285 | } 286 | 287 | if (self.canShowProgress && object == self.webView && [keyPath isEqualToString:@"estimatedProgress"]) { 288 | CGFloat newprogress = [[change objectForKey:NSKeyValueChangeNewKey] doubleValue]; 289 | self.estimatedProgress = newprogress; 290 | if (newprogress == 1) { 291 | self.progressView.hidden = YES; 292 | [self.progressView setProgress:0 animated:NO]; 293 | }else { 294 | self.progressView.hidden = NO; 295 | [self.progressView setProgress:newprogress animated:YES]; 296 | } 297 | return; 298 | } 299 | 300 | return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 301 | } 302 | 303 | #pragma mark - WKWebView Delegate 304 | - (void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ 305 | BOOL load = YES; 306 | if ([self.delegate respondsToSelector:@selector(zt_webView:shouldStartLoadWithRequest:navigationType:)]) { 307 | load = [self.delegate zt_webView:(ZTWebView*)self shouldStartLoadWithRequest:navigationAction.request navigationType:[self navigationTypeConvert:navigationAction.navigationType]]; 308 | } 309 | if (load) { 310 | decisionHandler(WKNavigationActionPolicyAllow); 311 | }else{ 312 | decisionHandler(WKNavigationActionPolicyCancel); 313 | } 314 | } 315 | 316 | - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{ 317 | [_indicatorView startAnimating]; 318 | if ([self.delegate respondsToSelector:@selector(zt_webViewDidStartLoad:)]) { 319 | [self.delegate zt_webViewDidStartLoad:(ZTWebView*)self]; 320 | } 321 | } 322 | 323 | - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{ 324 | [_indicatorView stopAnimating]; 325 | if ([self.delegate respondsToSelector:@selector(zt_webViewDidFinishLoad:)]) { 326 | [self.delegate zt_webViewDidFinishLoad:(ZTWebView*)self]; 327 | } 328 | 329 | [self zt_evaluateJavaScript:@"document.body.scrollHeight" completionHandler:^(id heitht, NSError *error) { 330 | if (!error) { 331 | self.pageHeight = [heitht floatValue]; 332 | } 333 | }]; 334 | 335 | if (_configuration.captureImage) { 336 | [self zt_evaluateJavaScript:@"imgsElement()" completionHandler:^(NSString * imgs, NSError *error) { 337 | if (!error && imgs.length) { 338 | _images = [imgs componentsSeparatedByString:@","]; 339 | } 340 | }]; 341 | } 342 | } 343 | 344 | - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{ 345 | [_indicatorView stopAnimating]; 346 | if ([self.delegate respondsToSelector:@selector(zt_webViewDidFinishLoad:)]) { 347 | [self.delegate zt_webView:(ZTWebView*)self didFailLoadWithError:error]; 348 | } 349 | } 350 | 351 | - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{ 352 | [_indicatorView stopAnimating]; 353 | if ([self.delegate respondsToSelector:@selector(zt_webView:didFailLoadWithError:)]) { 354 | [self.delegate zt_webView:(ZTWebView*)self didFailLoadWithError:error]; 355 | } 356 | } 357 | 358 | #pragma mark - UIWebView Delegate 359 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ 360 | BOOL isLoad = YES; 361 | if ([self.delegate respondsToSelector:@selector(zt_webView:shouldStartLoadWithRequest:navigationType:)]) { 362 | isLoad = [self.delegate zt_webView:(ZTWebView*)self shouldStartLoadWithRequest:request navigationType:[self navigationTypeConvert:navigationType]]; 363 | } 364 | return isLoad; 365 | } 366 | 367 | - (void)webViewDidStartLoad:(UIWebView *)webView{ 368 | [_indicatorView startAnimating]; 369 | if ([self.delegate respondsToSelector:@selector(zt_webViewDidStartLoad:)]) { 370 | [self.delegate zt_webViewDidStartLoad:(ZTWebView*)self]; 371 | } 372 | } 373 | 374 | - (void)webViewDidFinishLoad:(UIWebView *)webView{ 375 | [_indicatorView stopAnimating]; 376 | self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 377 | if ([self.delegate respondsToSelector:@selector(zt_webViewDidFinishLoad:)]) { 378 | [self.delegate zt_webViewDidFinishLoad:(ZTWebView *)self]; 379 | } 380 | 381 | [self zt_evaluateJavaScript:@"document.body.scrollHeight" completionHandler:^(id heitht, NSError *error) { 382 | if (!error) { 383 | self.pageHeight = [heitht floatValue]; 384 | } 385 | }]; 386 | 387 | if (_configuration.captureImage) { 388 | [self zt_evaluateJavaScript:[ZTWebViewJS imgsElement] completionHandler:nil]; 389 | [self zt_evaluateJavaScript:@"imgsElement()" completionHandler:^(NSString * imgs, NSError *error) { 390 | if (!error && imgs.length) { 391 | _images = [imgs componentsSeparatedByString:@","]; 392 | } 393 | }]; 394 | } 395 | } 396 | 397 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{ 398 | [_indicatorView stopAnimating]; 399 | if ([self.delegate respondsToSelector:@selector(zt_webView:didFailLoadWithError:)]) { 400 | [self.delegate zt_webView:(ZTWebView*)self didFailLoadWithError:error]; 401 | } 402 | } 403 | 404 | #pragma mark -Privity 405 | -(NSInteger)navigationTypeConvert:(NSInteger)type;{ 406 | NSInteger navigationType; 407 | if (isWKWebView) { 408 | switch (type) { 409 | case WKNavigationTypeLinkActivated: 410 | navigationType = ZTWebViewNavLinkClicked; 411 | break; 412 | case WKNavigationTypeFormSubmitted: 413 | navigationType = ZTWebViewNavFormSubmitted; 414 | break; 415 | case WKNavigationTypeBackForward: 416 | navigationType = ZTWebViewNavBackForward; 417 | break; 418 | case WKNavigationTypeReload: 419 | navigationType = ZTWebViewNavReload; 420 | break; 421 | case WKNavigationTypeFormResubmitted: 422 | navigationType = ZTWebViewNavResubmitted; 423 | break; 424 | case WKNavigationTypeOther: 425 | navigationType = ZTWebViewNavOther; 426 | break; 427 | default: 428 | navigationType = ZTWebViewNavOther; 429 | break; 430 | } 431 | }else{ 432 | switch (type) { 433 | case UIWebViewNavigationTypeLinkClicked: 434 | navigationType = ZTWebViewNavLinkClicked; 435 | break; 436 | case UIWebViewNavigationTypeFormSubmitted: 437 | navigationType = ZTWebViewNavFormSubmitted; 438 | break; 439 | case UIWebViewNavigationTypeBackForward: 440 | navigationType = ZTWebViewNavBackForward; 441 | break; 442 | case UIWebViewNavigationTypeReload: 443 | navigationType = ZTWebViewNavReload; 444 | break; 445 | case UIWebViewNavigationTypeFormResubmitted: 446 | navigationType = ZTWebViewNavResubmitted; 447 | break; 448 | case UIWebViewNavigationTypeOther: 449 | navigationType = ZTWebViewNavOther; 450 | break; 451 | default: 452 | navigationType = ZTWebViewNavOther; 453 | break; 454 | } 455 | } 456 | return navigationType; 457 | } 458 | 459 | - (void)layoutSubviews{ 460 | [super layoutSubviews]; 461 | [(UIView *)_webView setFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; 462 | _indicatorView.frame = CGRectMake(0, 0, 20, 20); 463 | _indicatorView.center = CGPointMake(self.frame.size.width/2, self.frame.size.height/2); 464 | } 465 | 466 | - (void)setNeedsLayout{ 467 | [super setNeedsLayout]; 468 | [(UIView *)_webView setNeedsLayout]; 469 | } 470 | 471 | - (void)dealloc{ 472 | if (isWKWebView) { 473 | [(ZTWebView *)_webView removeObserver:self forKeyPath:@"title"]; 474 | [(ZTWebView *)_webView removeObserver:self forKeyPath:@"estimatedProgress"]; 475 | } 476 | } 477 | 478 | @end 479 | 480 | @implementation ZTWKWebView 481 | 482 | - (void)zt_evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler{ 483 | [self evaluateJavaScript:javaScriptString completionHandler:completionHandler]; 484 | } 485 | 486 | @end 487 | 488 | @implementation ZTUIWebView 489 | 490 | - (void)zt_evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler{ 491 | NSString* result = [self stringByEvaluatingJavaScriptFromString:javaScriptString]; 492 | if (completionHandler) { 493 | completionHandler(result,nil); 494 | } 495 | } 496 | 497 | @end 498 | 499 | #pragma mark - ZTWebViewConfiguration 500 | @implementation ZTWebViewConfiguration 501 | 502 | - (instancetype)init{ 503 | if (self = [super init]) { 504 | _allowsInlineMediaPlayback = NO; 505 | _mediaPlaybackRequiresUserAction = YES; 506 | _suppressesIncrementalRendering = NO; 507 | } 508 | return self; 509 | } 510 | @end 511 | 512 | #pragma mark - ZTWebViewJS 513 | @implementation ZTWebViewJS 514 | 515 | + (NSString *)scalesPageToFitJS{ 516 | return @"var meta = document.createElement('meta'); \ 517 | meta.name = 'viewport'; \ 518 | meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'; \ 519 | var head = document.getElementsByTagName('head')[0];\ 520 | head.appendChild(meta);"; 521 | } 522 | 523 | +(NSString *)imgsElement{ 524 | return @"function imgsElement(){\ 525 | var imgs = document.getElementsByTagName(\"img\");\ 526 | var imgScr = '';\ 527 | for(var i=0;i 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /WZTWebViewTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /WZTWebViewTests/WZTWebViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WZTWebViewTests.m 3 | // WZTWebViewTests 4 | // 5 | // Created by BY-iMac on 17/6/6. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WZTWebViewTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation WZTWebViewTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /WZTWebViewUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /WZTWebViewUITests/WZTWebViewUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WZTWebViewUITests.m 3 | // WZTWebViewUITests 4 | // 5 | // Created by BY-iMac on 17/6/6. 6 | // Copyright © 2017年 beck.wang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WZTWebViewUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation WZTWebViewUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------