├── .gitignore ├── README.md ├── WeAppTongCeng.xcodeproj └── project.pbxproj ├── WeAppTongCeng ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── LVTongCeng │ ├── LVApiHandleDelegate-Protocol.h │ ├── LVApiHandler.h │ ├── LVApiHandler.m │ ├── LVContainerView.h │ ├── LVContainerView.m │ ├── LVTongCengWebDelegate-Protocol.h │ ├── LVTongCengWebMgr.h │ ├── LVTongCengWebMgr.m │ ├── LVTongCengWebView.h │ ├── LVTongCengWebView.m │ ├── LVWeakProxy.h │ ├── LVWeakProxy.m │ ├── LVWebViewBridge.h │ ├── LVWebViewBridge.m │ └── YYWAWebView.cpp ├── ViewController.h ├── ViewController.m ├── main.m └── test │ ├── LVTongCengWebController.h │ ├── LVTongCengWebController.m │ ├── LVWKWebController.h │ ├── LVWKWebController.m │ ├── LVWebViewBridge │ ├── LVBridge.h │ └── LVTongCengWebManager.h │ └── tongceng.html ├── WeAppTongCengTests ├── Info.plist └── WeAppTongCengTests.m └── WeAppTongCengUITests ├── Info.plist └── WeAppTongCengUITests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | .idea/ 22 | *.xccheckout 23 | *.xcworkspace 24 | 25 | #CocoaPods 26 | Pods 27 | Manifest.lock 28 | Podfile.lock 29 | # We recommend against adding the Pods directory to your .gitignore. However 30 | # you should judge for yourself, the pros and cons are mentioned at: 31 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 32 | # 33 | # Note: if you ignore the Pods directory, make sure to uncomment 34 | # `pod install` in .travis.yml 35 | # 36 | # Pods/ 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > 自微信小程序火了后, 个大头部厂商也纷纷效仿, 给自己的客户端提供了小程序框架支持. 然而小程序框架为了提升小程序页面的极致用户体验, 做了**同层渲染**. 2 | 3 | ​ **抛砖引玉:** 4 | 5 | ​ **我们今天的目标就是: 从0到1实现iOS端`WKWebView`同层渲染, 文章末尾有DEMO演示, 也有源码地址.** 6 | 7 | ![weapp.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cd50590ab184460883662eb935017b1d~tplv-k3u1fbpfcp-watermark.image) 8 | 9 | 10 | 11 | 12 | 13 | # 同层渲染原理 14 | 15 | 「同层渲染」就是把原生组件绘制在 `WebView` 所渲染的页面中,与其他 `HTML` 控件在同一层级,效果如下图所示: 16 | ![同层渲染原理.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f137ab9d5bd84220a1ffdef641912882~tplv-k3u1fbpfcp-watermark.image) 17 | 18 | 这里我给大家找到三篇比较官方的文章, 建议跳过看总结: 19 | 20 | - 腾讯 [微信小程序同层渲染原理剖析](https://developers.weixin.qq.com/community/develop/article/doc/000c4e433707c072c1793e56f5c813?page=1) 21 | - 百度[【走进小程序原理】揭秘组件同层渲染](https://blog.csdn.net/Smartprogram/article/details/108124407) **(推荐)** 22 | - 阿里 [亿级用户高稳定性视频播放器养成计划|618淘系前端技术分享](https://mp.weixin.qq.com/s/jgsG-XrAKV6AHSrUCRhKtQ) 23 | 24 | 25 | > 总结 26 | > 27 | > 这些文章只阐述了基本实现思路, 并没有教你怎么实现, 然并卵. 28 | > 29 | > 看完之后各大技术论坛的“同层渲染原理分析”的你, 还是实现不了. 30 | > **下面我们一起来实现一个.** 31 | 32 | 33 | 34 | # 实现同层渲染 35 | 36 | ## 1. 我们在前端H5页面上创建一个需要生成原生组件的`DOM`节点. 37 | 38 | ```html 39 |
40 |
 
41 |
42 | 43 | 50 | ``` 51 | 52 | ## 2. 前端会把原生组件类型如`cid`、`position`等信息通过`window.webkit.messageHandlers.handle.postMessage`发送给iOS端. 53 | 54 | ```javascript 55 | 109 | ``` 110 | 111 | ## 3. iOS端WKWebView消息代理接收到前端发来的消息 112 | 113 | ```objective-c 114 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { 115 | // 收到前端的消息 116 | // message.name: handle 117 | // message.body: {"api":"insertInput","args":{"cid":"1","position":{"top":30,"width":200,"left":87.5,"height":40}}} 118 | } 119 | ``` 120 | 121 | ## 4. iOS端可以通过消息中的`cid`匹配查找相应`WKChildScrollView`,并将原生组件挂载到该节点上作为其子View. 122 | 123 | ![图片](https://note.youdao.com/yws/api/personal/file/WEB5d27628125737e47e5547909cf3f6c90?method=download&shareKey=bcf19a277ca029ac0169efcd1c4a32a2) 124 | 125 | 126 | 127 | ## 5. 最后我们发现WKWebView中插入的原生组件是无法响应交互事件的,需要特殊处理. 128 | 129 | 这里我们需要处理两件事,一是屏蔽`WKContentView`手势拦截事件响应链的传递,让事件可以传递到原生组件中,命令如下: 130 | 131 | ```objective-c 132 | @implementation LVTongCengWebView 133 | 134 | - (void)_handleWKContentGestrues { 135 | UIScrollView *webViewScrollView = self.scrollView; 136 | if ([webViewScrollView isKindOfClass:NSClassFromString(@"WKScrollView")]) { 137 | UIView *_WKContentView = webViewScrollView.subviews.firstObject; 138 | if (![_WKContentView isKindOfClass:NSClassFromString(@"WKContentView")]) return; 139 | NSArray *gestrues = _WKContentView.gestureRecognizers; 140 | for (UIGestureRecognizer *gesture in gestrues) { 141 | gesture.cancelsTouchesInView = NO; 142 | gesture.delaysTouchesBegan = NO; 143 | gesture.delaysTouchesEnded = NO; 144 | } 145 | } 146 | } 147 | 148 | @end 149 | ``` 150 | 151 | 二是重写iOS端`原生组件`容器`LVContainerView`协议检测方法`-conformsToProtocol:`,目的是绕过`WKNativelyInteractible`协议检测,从而在点击WKWebView的时候,容器`LVContainerView`及其子view(原生组件)可以正常进行事件响应。 152 | 153 | ```objective-c 154 | @implementation LVContainerView 155 | 156 | - (BOOL)conformsToProtocol:(Protocol *)aProtocol { 157 | if (aProtocol == NSProtocolFromString(@"WKNativelyInteractible")) { 158 | return YES; 159 | } 160 | return [super conformsToProtocol:aProtocol]; 161 | } 162 | 163 | @end 164 | ``` 165 | 166 | 167 | 168 | 到这里,经过前面这些步骤,我们就实现了iOS端`WKWebView`的`同层渲染`,下面让我们来看看实现效果: 169 | 170 | 1. 我们可以看到原生组件`UITextField`、`UITextView`响应聚焦输入,`UIButton`可以点击变颜色,实现了正常交互; 171 | 2. H5蒙层可以遮挡住`原生组件`,解决了`WKWebView`内嵌原生组件的层级限制问题,完美体现了`同层`的含义。 172 | 173 |
174 | 175 |
176 | 177 | **Talk is cheap. Show me the code.** 178 | DEMO地址: [https://github.com/lionvoom/WeAppTongCeng](https://github.com/lionvoom/WeAppTongCeng) 179 | 180 | **极客时间-每日一课** 181 | [iOS小程序同层渲染到底怎么实现?](https://time.geekbang.org/dailylesson/detail/100075719) 182 | -------------------------------------------------------------------------------- /WeAppTongCeng.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 090C2CF925FA0F4100330C86 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 090C2CF825FA0F4100330C86 /* AppDelegate.m */; }; 11 | 090C2CFF25FA0F4100330C86 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 090C2CFE25FA0F4100330C86 /* ViewController.m */; }; 12 | 090C2D0225FA0F4100330C86 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 090C2D0025FA0F4100330C86 /* Main.storyboard */; }; 13 | 090C2D0425FA0F4400330C86 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 090C2D0325FA0F4400330C86 /* Assets.xcassets */; }; 14 | 090C2D0725FA0F4400330C86 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 090C2D0525FA0F4400330C86 /* LaunchScreen.storyboard */; }; 15 | 090C2D0A25FA0F4400330C86 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 090C2D0925FA0F4400330C86 /* main.m */; }; 16 | 090C2D1425FA0F4400330C86 /* WeAppTongCengTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 090C2D1325FA0F4400330C86 /* WeAppTongCengTests.m */; }; 17 | 090C2D1F25FA0F4400330C86 /* WeAppTongCengUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 090C2D1E25FA0F4400330C86 /* WeAppTongCengUITests.m */; }; 18 | 090C2D3325FA108100330C86 /* LVTongCengWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 090C2D3225FA108100330C86 /* LVTongCengWebView.m */; }; 19 | 090C2D5625FB1CD800330C86 /* LVTongCengWebController.m in Sources */ = {isa = PBXBuildFile; fileRef = 090C2D5525FB1CD800330C86 /* LVTongCengWebController.m */; }; 20 | 090C2D7025FB1E0400330C86 /* LVContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 090C2D6F25FB1E0400330C86 /* LVContainerView.m */; }; 21 | 090C2D8625FB793000330C86 /* tongceng.html in Resources */ = {isa = PBXBuildFile; fileRef = 090C2D8525FB793000330C86 /* tongceng.html */; }; 22 | 0936DCC525FF32AD007A65EB /* LVWeakProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0936DCC425FF32AD007A65EB /* LVWeakProxy.m */; }; 23 | 094273D92600919100B26EB8 /* LVWKWebController.m in Sources */ = {isa = PBXBuildFile; fileRef = 094273D82600919100B26EB8 /* LVWKWebController.m */; }; 24 | 094273E42600967600B26EB8 /* LVWebViewBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 094273E32600967600B26EB8 /* LVWebViewBridge.m */; }; 25 | 094273F3260096BD00B26EB8 /* LVApiHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 094273F2260096BD00B26EB8 /* LVApiHandler.m */; }; 26 | 094273F92600993200B26EB8 /* LVTongCengWebMgr.m in Sources */ = {isa = PBXBuildFile; fileRef = 094273F82600993200B26EB8 /* LVTongCengWebMgr.m */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 090C2D1025FA0F4400330C86 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 090C2CEC25FA0F4100330C86 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 090C2CF325FA0F4100330C86; 35 | remoteInfo = WeAppTongCeng; 36 | }; 37 | 090C2D1B25FA0F4400330C86 /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = 090C2CEC25FA0F4100330C86 /* Project object */; 40 | proxyType = 1; 41 | remoteGlobalIDString = 090C2CF325FA0F4100330C86; 42 | remoteInfo = WeAppTongCeng; 43 | }; 44 | /* End PBXContainerItemProxy section */ 45 | 46 | /* Begin PBXFileReference section */ 47 | 090C2CF425FA0F4100330C86 /* WeAppTongCeng.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeAppTongCeng.app; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 090C2CF725FA0F4100330C86 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 49 | 090C2CF825FA0F4100330C86 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 50 | 090C2CFD25FA0F4100330C86 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 51 | 090C2CFE25FA0F4100330C86 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 52 | 090C2D0125FA0F4100330C86 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 53 | 090C2D0325FA0F4400330C86 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 54 | 090C2D0625FA0F4400330C86 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 55 | 090C2D0825FA0F4400330C86 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 090C2D0925FA0F4400330C86 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 57 | 090C2D0F25FA0F4400330C86 /* WeAppTongCengTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WeAppTongCengTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 090C2D1325FA0F4400330C86 /* WeAppTongCengTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WeAppTongCengTests.m; sourceTree = ""; }; 59 | 090C2D1525FA0F4400330C86 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | 090C2D1A25FA0F4400330C86 /* WeAppTongCengUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WeAppTongCengUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | 090C2D1E25FA0F4400330C86 /* WeAppTongCengUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WeAppTongCengUITests.m; sourceTree = ""; }; 62 | 090C2D2025FA0F4400330C86 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | 090C2D3125FA108100330C86 /* LVTongCengWebView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LVTongCengWebView.h; sourceTree = ""; }; 64 | 090C2D3225FA108100330C86 /* LVTongCengWebView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LVTongCengWebView.m; sourceTree = ""; }; 65 | 090C2D3825FA141700330C86 /* YYWAWebView.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = YYWAWebView.cpp; sourceTree = ""; }; 66 | 090C2D5425FB1CD800330C86 /* LVTongCengWebController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LVTongCengWebController.h; sourceTree = ""; }; 67 | 090C2D5525FB1CD800330C86 /* LVTongCengWebController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LVTongCengWebController.m; sourceTree = ""; }; 68 | 090C2D6E25FB1E0400330C86 /* LVContainerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LVContainerView.h; sourceTree = ""; }; 69 | 090C2D6F25FB1E0400330C86 /* LVContainerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LVContainerView.m; sourceTree = ""; }; 70 | 090C2D8525FB793000330C86 /* tongceng.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = tongceng.html; sourceTree = ""; }; 71 | 0936DCC325FF32AD007A65EB /* LVWeakProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LVWeakProxy.h; sourceTree = ""; }; 72 | 0936DCC425FF32AD007A65EB /* LVWeakProxy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LVWeakProxy.m; sourceTree = ""; }; 73 | 094273D72600919100B26EB8 /* LVWKWebController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LVWKWebController.h; sourceTree = ""; }; 74 | 094273D82600919100B26EB8 /* LVWKWebController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LVWKWebController.m; sourceTree = ""; }; 75 | 094273E22600967600B26EB8 /* LVWebViewBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LVWebViewBridge.h; sourceTree = ""; }; 76 | 094273E32600967600B26EB8 /* LVWebViewBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LVWebViewBridge.m; sourceTree = ""; }; 77 | 094273F1260096BD00B26EB8 /* LVApiHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LVApiHandler.h; sourceTree = ""; }; 78 | 094273F2260096BD00B26EB8 /* LVApiHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LVApiHandler.m; sourceTree = ""; }; 79 | 094273F72600993200B26EB8 /* LVTongCengWebMgr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LVTongCengWebMgr.h; sourceTree = ""; }; 80 | 094273F82600993200B26EB8 /* LVTongCengWebMgr.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LVTongCengWebMgr.m; sourceTree = ""; }; 81 | 0942740026009B9600B26EB8 /* LVApiHandleDelegate-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LVApiHandleDelegate-Protocol.h"; sourceTree = ""; }; 82 | 0942740426009FCD00B26EB8 /* LVTongCengWebDelegate-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LVTongCengWebDelegate-Protocol.h"; sourceTree = ""; }; 83 | /* End PBXFileReference section */ 84 | 85 | /* Begin PBXFrameworksBuildPhase section */ 86 | 090C2CF125FA0F4100330C86 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | 090C2D0C25FA0F4400330C86 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | 090C2D1725FA0F4400330C86 /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | /* End PBXFrameworksBuildPhase section */ 108 | 109 | /* Begin PBXGroup section */ 110 | 090C2CEB25FA0F4100330C86 = { 111 | isa = PBXGroup; 112 | children = ( 113 | 090C2CF625FA0F4100330C86 /* WeAppTongCeng */, 114 | 090C2D1225FA0F4400330C86 /* WeAppTongCengTests */, 115 | 090C2D1D25FA0F4400330C86 /* WeAppTongCengUITests */, 116 | 090C2CF525FA0F4100330C86 /* Products */, 117 | ); 118 | sourceTree = ""; 119 | }; 120 | 090C2CF525FA0F4100330C86 /* Products */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 090C2CF425FA0F4100330C86 /* WeAppTongCeng.app */, 124 | 090C2D0F25FA0F4400330C86 /* WeAppTongCengTests.xctest */, 125 | 090C2D1A25FA0F4400330C86 /* WeAppTongCengUITests.xctest */, 126 | ); 127 | name = Products; 128 | sourceTree = ""; 129 | }; 130 | 090C2CF625FA0F4100330C86 /* WeAppTongCeng */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 0942741B2600A4BF00B26EB8 /* AppDelegate */, 134 | 0936DDAC26006C5C007A65EB /* LVTongCeng */, 135 | 090C2D4D25FB1C6400330C86 /* test */, 136 | ); 137 | path = WeAppTongCeng; 138 | sourceTree = ""; 139 | }; 140 | 090C2D1225FA0F4400330C86 /* WeAppTongCengTests */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 090C2D1325FA0F4400330C86 /* WeAppTongCengTests.m */, 144 | 090C2D1525FA0F4400330C86 /* Info.plist */, 145 | ); 146 | path = WeAppTongCengTests; 147 | sourceTree = ""; 148 | }; 149 | 090C2D1D25FA0F4400330C86 /* WeAppTongCengUITests */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 090C2D1E25FA0F4400330C86 /* WeAppTongCengUITests.m */, 153 | 090C2D2025FA0F4400330C86 /* Info.plist */, 154 | ); 155 | path = WeAppTongCengUITests; 156 | sourceTree = ""; 157 | }; 158 | 090C2D4D25FB1C6400330C86 /* test */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 090C2D5425FB1CD800330C86 /* LVTongCengWebController.h */, 162 | 090C2D5525FB1CD800330C86 /* LVTongCengWebController.m */, 163 | 094273D72600919100B26EB8 /* LVWKWebController.h */, 164 | 094273D82600919100B26EB8 /* LVWKWebController.m */, 165 | 090C2D8525FB793000330C86 /* tongceng.html */, 166 | ); 167 | path = test; 168 | sourceTree = ""; 169 | }; 170 | 0936DDAC26006C5C007A65EB /* LVTongCeng */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 090C2D3125FA108100330C86 /* LVTongCengWebView.h */, 174 | 090C2D3225FA108100330C86 /* LVTongCengWebView.m */, 175 | 090C2D6E25FB1E0400330C86 /* LVContainerView.h */, 176 | 090C2D6F25FB1E0400330C86 /* LVContainerView.m */, 177 | 094273E22600967600B26EB8 /* LVWebViewBridge.h */, 178 | 094273E32600967600B26EB8 /* LVWebViewBridge.m */, 179 | 0942740026009B9600B26EB8 /* LVApiHandleDelegate-Protocol.h */, 180 | 094273F1260096BD00B26EB8 /* LVApiHandler.h */, 181 | 094273F2260096BD00B26EB8 /* LVApiHandler.m */, 182 | 094273F72600993200B26EB8 /* LVTongCengWebMgr.h */, 183 | 094273F82600993200B26EB8 /* LVTongCengWebMgr.m */, 184 | 0942740426009FCD00B26EB8 /* LVTongCengWebDelegate-Protocol.h */, 185 | 0936DCC325FF32AD007A65EB /* LVWeakProxy.h */, 186 | 0936DCC425FF32AD007A65EB /* LVWeakProxy.m */, 187 | 090C2D3825FA141700330C86 /* YYWAWebView.cpp */, 188 | ); 189 | path = LVTongCeng; 190 | sourceTree = ""; 191 | }; 192 | 0942741B2600A4BF00B26EB8 /* AppDelegate */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 090C2CF725FA0F4100330C86 /* AppDelegate.h */, 196 | 090C2CF825FA0F4100330C86 /* AppDelegate.m */, 197 | 090C2CFD25FA0F4100330C86 /* ViewController.h */, 198 | 090C2CFE25FA0F4100330C86 /* ViewController.m */, 199 | 090C2D0025FA0F4100330C86 /* Main.storyboard */, 200 | 090C2D0325FA0F4400330C86 /* Assets.xcassets */, 201 | 090C2D0525FA0F4400330C86 /* LaunchScreen.storyboard */, 202 | 090C2D0825FA0F4400330C86 /* Info.plist */, 203 | 090C2D0925FA0F4400330C86 /* main.m */, 204 | ); 205 | name = AppDelegate; 206 | sourceTree = ""; 207 | }; 208 | /* End PBXGroup section */ 209 | 210 | /* Begin PBXNativeTarget section */ 211 | 090C2CF325FA0F4100330C86 /* WeAppTongCeng */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = 090C2D2325FA0F4400330C86 /* Build configuration list for PBXNativeTarget "WeAppTongCeng" */; 214 | buildPhases = ( 215 | 090C2CF025FA0F4100330C86 /* Sources */, 216 | 090C2CF125FA0F4100330C86 /* Frameworks */, 217 | 090C2CF225FA0F4100330C86 /* Resources */, 218 | ); 219 | buildRules = ( 220 | ); 221 | dependencies = ( 222 | ); 223 | name = WeAppTongCeng; 224 | productName = WeAppTongCeng; 225 | productReference = 090C2CF425FA0F4100330C86 /* WeAppTongCeng.app */; 226 | productType = "com.apple.product-type.application"; 227 | }; 228 | 090C2D0E25FA0F4400330C86 /* WeAppTongCengTests */ = { 229 | isa = PBXNativeTarget; 230 | buildConfigurationList = 090C2D2625FA0F4400330C86 /* Build configuration list for PBXNativeTarget "WeAppTongCengTests" */; 231 | buildPhases = ( 232 | 090C2D0B25FA0F4400330C86 /* Sources */, 233 | 090C2D0C25FA0F4400330C86 /* Frameworks */, 234 | 090C2D0D25FA0F4400330C86 /* Resources */, 235 | ); 236 | buildRules = ( 237 | ); 238 | dependencies = ( 239 | 090C2D1125FA0F4400330C86 /* PBXTargetDependency */, 240 | ); 241 | name = WeAppTongCengTests; 242 | productName = WeAppTongCengTests; 243 | productReference = 090C2D0F25FA0F4400330C86 /* WeAppTongCengTests.xctest */; 244 | productType = "com.apple.product-type.bundle.unit-test"; 245 | }; 246 | 090C2D1925FA0F4400330C86 /* WeAppTongCengUITests */ = { 247 | isa = PBXNativeTarget; 248 | buildConfigurationList = 090C2D2925FA0F4400330C86 /* Build configuration list for PBXNativeTarget "WeAppTongCengUITests" */; 249 | buildPhases = ( 250 | 090C2D1625FA0F4400330C86 /* Sources */, 251 | 090C2D1725FA0F4400330C86 /* Frameworks */, 252 | 090C2D1825FA0F4400330C86 /* Resources */, 253 | ); 254 | buildRules = ( 255 | ); 256 | dependencies = ( 257 | 090C2D1C25FA0F4400330C86 /* PBXTargetDependency */, 258 | ); 259 | name = WeAppTongCengUITests; 260 | productName = WeAppTongCengUITests; 261 | productReference = 090C2D1A25FA0F4400330C86 /* WeAppTongCengUITests.xctest */; 262 | productType = "com.apple.product-type.bundle.ui-testing"; 263 | }; 264 | /* End PBXNativeTarget section */ 265 | 266 | /* Begin PBXProject section */ 267 | 090C2CEC25FA0F4100330C86 /* Project object */ = { 268 | isa = PBXProject; 269 | attributes = { 270 | LastUpgradeCheck = 1240; 271 | TargetAttributes = { 272 | 090C2CF325FA0F4100330C86 = { 273 | CreatedOnToolsVersion = 12.4; 274 | }; 275 | 090C2D0E25FA0F4400330C86 = { 276 | CreatedOnToolsVersion = 12.4; 277 | TestTargetID = 090C2CF325FA0F4100330C86; 278 | }; 279 | 090C2D1925FA0F4400330C86 = { 280 | CreatedOnToolsVersion = 12.4; 281 | TestTargetID = 090C2CF325FA0F4100330C86; 282 | }; 283 | }; 284 | }; 285 | buildConfigurationList = 090C2CEF25FA0F4100330C86 /* Build configuration list for PBXProject "WeAppTongCeng" */; 286 | compatibilityVersion = "Xcode 9.3"; 287 | developmentRegion = en; 288 | hasScannedForEncodings = 0; 289 | knownRegions = ( 290 | en, 291 | Base, 292 | ); 293 | mainGroup = 090C2CEB25FA0F4100330C86; 294 | productRefGroup = 090C2CF525FA0F4100330C86 /* Products */; 295 | projectDirPath = ""; 296 | projectRoot = ""; 297 | targets = ( 298 | 090C2CF325FA0F4100330C86 /* WeAppTongCeng */, 299 | 090C2D0E25FA0F4400330C86 /* WeAppTongCengTests */, 300 | 090C2D1925FA0F4400330C86 /* WeAppTongCengUITests */, 301 | ); 302 | }; 303 | /* End PBXProject section */ 304 | 305 | /* Begin PBXResourcesBuildPhase section */ 306 | 090C2CF225FA0F4100330C86 /* Resources */ = { 307 | isa = PBXResourcesBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | 090C2D8625FB793000330C86 /* tongceng.html in Resources */, 311 | 090C2D0725FA0F4400330C86 /* LaunchScreen.storyboard in Resources */, 312 | 090C2D0425FA0F4400330C86 /* Assets.xcassets in Resources */, 313 | 090C2D0225FA0F4100330C86 /* Main.storyboard in Resources */, 314 | ); 315 | runOnlyForDeploymentPostprocessing = 0; 316 | }; 317 | 090C2D0D25FA0F4400330C86 /* Resources */ = { 318 | isa = PBXResourcesBuildPhase; 319 | buildActionMask = 2147483647; 320 | files = ( 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | 090C2D1825FA0F4400330C86 /* Resources */ = { 325 | isa = PBXResourcesBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | ); 329 | runOnlyForDeploymentPostprocessing = 0; 330 | }; 331 | /* End PBXResourcesBuildPhase section */ 332 | 333 | /* Begin PBXSourcesBuildPhase section */ 334 | 090C2CF025FA0F4100330C86 /* Sources */ = { 335 | isa = PBXSourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | 090C2CFF25FA0F4100330C86 /* ViewController.m in Sources */, 339 | 090C2CF925FA0F4100330C86 /* AppDelegate.m in Sources */, 340 | 094273F3260096BD00B26EB8 /* LVApiHandler.m in Sources */, 341 | 090C2D3325FA108100330C86 /* LVTongCengWebView.m in Sources */, 342 | 0936DCC525FF32AD007A65EB /* LVWeakProxy.m in Sources */, 343 | 094273D92600919100B26EB8 /* LVWKWebController.m in Sources */, 344 | 090C2D7025FB1E0400330C86 /* LVContainerView.m in Sources */, 345 | 090C2D0A25FA0F4400330C86 /* main.m in Sources */, 346 | 094273F92600993200B26EB8 /* LVTongCengWebMgr.m in Sources */, 347 | 090C2D5625FB1CD800330C86 /* LVTongCengWebController.m in Sources */, 348 | 094273E42600967600B26EB8 /* LVWebViewBridge.m in Sources */, 349 | ); 350 | runOnlyForDeploymentPostprocessing = 0; 351 | }; 352 | 090C2D0B25FA0F4400330C86 /* Sources */ = { 353 | isa = PBXSourcesBuildPhase; 354 | buildActionMask = 2147483647; 355 | files = ( 356 | 090C2D1425FA0F4400330C86 /* WeAppTongCengTests.m in Sources */, 357 | ); 358 | runOnlyForDeploymentPostprocessing = 0; 359 | }; 360 | 090C2D1625FA0F4400330C86 /* Sources */ = { 361 | isa = PBXSourcesBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | 090C2D1F25FA0F4400330C86 /* WeAppTongCengUITests.m in Sources */, 365 | ); 366 | runOnlyForDeploymentPostprocessing = 0; 367 | }; 368 | /* End PBXSourcesBuildPhase section */ 369 | 370 | /* Begin PBXTargetDependency section */ 371 | 090C2D1125FA0F4400330C86 /* PBXTargetDependency */ = { 372 | isa = PBXTargetDependency; 373 | target = 090C2CF325FA0F4100330C86 /* WeAppTongCeng */; 374 | targetProxy = 090C2D1025FA0F4400330C86 /* PBXContainerItemProxy */; 375 | }; 376 | 090C2D1C25FA0F4400330C86 /* PBXTargetDependency */ = { 377 | isa = PBXTargetDependency; 378 | target = 090C2CF325FA0F4100330C86 /* WeAppTongCeng */; 379 | targetProxy = 090C2D1B25FA0F4400330C86 /* PBXContainerItemProxy */; 380 | }; 381 | /* End PBXTargetDependency section */ 382 | 383 | /* Begin PBXVariantGroup section */ 384 | 090C2D0025FA0F4100330C86 /* Main.storyboard */ = { 385 | isa = PBXVariantGroup; 386 | children = ( 387 | 090C2D0125FA0F4100330C86 /* Base */, 388 | ); 389 | name = Main.storyboard; 390 | sourceTree = ""; 391 | }; 392 | 090C2D0525FA0F4400330C86 /* LaunchScreen.storyboard */ = { 393 | isa = PBXVariantGroup; 394 | children = ( 395 | 090C2D0625FA0F4400330C86 /* Base */, 396 | ); 397 | name = LaunchScreen.storyboard; 398 | sourceTree = ""; 399 | }; 400 | /* End PBXVariantGroup section */ 401 | 402 | /* Begin XCBuildConfiguration section */ 403 | 090C2D2125FA0F4400330C86 /* Debug */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | ALWAYS_SEARCH_USER_PATHS = NO; 407 | CLANG_ANALYZER_NONNULL = YES; 408 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 409 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 410 | CLANG_CXX_LIBRARY = "libc++"; 411 | CLANG_ENABLE_MODULES = YES; 412 | CLANG_ENABLE_OBJC_ARC = YES; 413 | CLANG_ENABLE_OBJC_WEAK = YES; 414 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 415 | CLANG_WARN_BOOL_CONVERSION = YES; 416 | CLANG_WARN_COMMA = YES; 417 | CLANG_WARN_CONSTANT_CONVERSION = YES; 418 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 419 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 420 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 421 | CLANG_WARN_EMPTY_BODY = YES; 422 | CLANG_WARN_ENUM_CONVERSION = YES; 423 | CLANG_WARN_INFINITE_RECURSION = YES; 424 | CLANG_WARN_INT_CONVERSION = YES; 425 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 426 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 427 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 428 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 429 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 430 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 431 | CLANG_WARN_STRICT_PROTOTYPES = YES; 432 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 433 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 434 | CLANG_WARN_UNREACHABLE_CODE = YES; 435 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 436 | COPY_PHASE_STRIP = NO; 437 | DEBUG_INFORMATION_FORMAT = dwarf; 438 | ENABLE_STRICT_OBJC_MSGSEND = YES; 439 | ENABLE_TESTABILITY = YES; 440 | GCC_C_LANGUAGE_STANDARD = gnu11; 441 | GCC_DYNAMIC_NO_PIC = NO; 442 | GCC_NO_COMMON_BLOCKS = YES; 443 | GCC_OPTIMIZATION_LEVEL = 0; 444 | GCC_PREPROCESSOR_DEFINITIONS = ( 445 | "DEBUG=1", 446 | "$(inherited)", 447 | ); 448 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 449 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 450 | GCC_WARN_UNDECLARED_SELECTOR = YES; 451 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 452 | GCC_WARN_UNUSED_FUNCTION = YES; 453 | GCC_WARN_UNUSED_VARIABLE = YES; 454 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 455 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 456 | MTL_FAST_MATH = YES; 457 | ONLY_ACTIVE_ARCH = YES; 458 | SDKROOT = iphoneos; 459 | }; 460 | name = Debug; 461 | }; 462 | 090C2D2225FA0F4400330C86 /* Release */ = { 463 | isa = XCBuildConfiguration; 464 | buildSettings = { 465 | ALWAYS_SEARCH_USER_PATHS = NO; 466 | CLANG_ANALYZER_NONNULL = YES; 467 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 468 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 469 | CLANG_CXX_LIBRARY = "libc++"; 470 | CLANG_ENABLE_MODULES = YES; 471 | CLANG_ENABLE_OBJC_ARC = YES; 472 | CLANG_ENABLE_OBJC_WEAK = YES; 473 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 474 | CLANG_WARN_BOOL_CONVERSION = YES; 475 | CLANG_WARN_COMMA = YES; 476 | CLANG_WARN_CONSTANT_CONVERSION = YES; 477 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 478 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 479 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 480 | CLANG_WARN_EMPTY_BODY = YES; 481 | CLANG_WARN_ENUM_CONVERSION = YES; 482 | CLANG_WARN_INFINITE_RECURSION = YES; 483 | CLANG_WARN_INT_CONVERSION = YES; 484 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 485 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 486 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 487 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 488 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 489 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 490 | CLANG_WARN_STRICT_PROTOTYPES = YES; 491 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 492 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 493 | CLANG_WARN_UNREACHABLE_CODE = YES; 494 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 495 | COPY_PHASE_STRIP = NO; 496 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 497 | ENABLE_NS_ASSERTIONS = NO; 498 | ENABLE_STRICT_OBJC_MSGSEND = YES; 499 | GCC_C_LANGUAGE_STANDARD = gnu11; 500 | GCC_NO_COMMON_BLOCKS = YES; 501 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 502 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 503 | GCC_WARN_UNDECLARED_SELECTOR = YES; 504 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 505 | GCC_WARN_UNUSED_FUNCTION = YES; 506 | GCC_WARN_UNUSED_VARIABLE = YES; 507 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 508 | MTL_ENABLE_DEBUG_INFO = NO; 509 | MTL_FAST_MATH = YES; 510 | SDKROOT = iphoneos; 511 | VALIDATE_PRODUCT = YES; 512 | }; 513 | name = Release; 514 | }; 515 | 090C2D2425FA0F4400330C86 /* Debug */ = { 516 | isa = XCBuildConfiguration; 517 | buildSettings = { 518 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 519 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 520 | CODE_SIGN_STYLE = Automatic; 521 | DEVELOPMENT_TEAM = 9PJ22KK765; 522 | INFOPLIST_FILE = WeAppTongCeng/Info.plist; 523 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 524 | LD_RUNPATH_SEARCH_PATHS = ( 525 | "$(inherited)", 526 | "@executable_path/Frameworks", 527 | ); 528 | PRODUCT_BUNDLE_IDENTIFIER = com.lionvoom.WeAppTongCeng.1; 529 | PRODUCT_NAME = "$(TARGET_NAME)"; 530 | TARGETED_DEVICE_FAMILY = "1,2"; 531 | }; 532 | name = Debug; 533 | }; 534 | 090C2D2525FA0F4400330C86 /* Release */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 538 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 539 | CODE_SIGN_STYLE = Automatic; 540 | DEVELOPMENT_TEAM = 9PJ22KK765; 541 | INFOPLIST_FILE = WeAppTongCeng/Info.plist; 542 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 543 | LD_RUNPATH_SEARCH_PATHS = ( 544 | "$(inherited)", 545 | "@executable_path/Frameworks", 546 | ); 547 | PRODUCT_BUNDLE_IDENTIFIER = com.lionvoom.WeAppTongCeng.1; 548 | PRODUCT_NAME = "$(TARGET_NAME)"; 549 | TARGETED_DEVICE_FAMILY = "1,2"; 550 | }; 551 | name = Release; 552 | }; 553 | 090C2D2725FA0F4400330C86 /* Debug */ = { 554 | isa = XCBuildConfiguration; 555 | buildSettings = { 556 | BUNDLE_LOADER = "$(TEST_HOST)"; 557 | CODE_SIGN_STYLE = Automatic; 558 | DEVELOPMENT_TEAM = 9PJ22KK765; 559 | INFOPLIST_FILE = WeAppTongCengTests/Info.plist; 560 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 561 | LD_RUNPATH_SEARCH_PATHS = ( 562 | "$(inherited)", 563 | "@executable_path/Frameworks", 564 | "@loader_path/Frameworks", 565 | ); 566 | PRODUCT_BUNDLE_IDENTIFIER = com.lionvoom.WeAppTongCengTests; 567 | PRODUCT_NAME = "$(TARGET_NAME)"; 568 | TARGETED_DEVICE_FAMILY = "1,2"; 569 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WeAppTongCeng.app/WeAppTongCeng"; 570 | }; 571 | name = Debug; 572 | }; 573 | 090C2D2825FA0F4400330C86 /* Release */ = { 574 | isa = XCBuildConfiguration; 575 | buildSettings = { 576 | BUNDLE_LOADER = "$(TEST_HOST)"; 577 | CODE_SIGN_STYLE = Automatic; 578 | DEVELOPMENT_TEAM = 9PJ22KK765; 579 | INFOPLIST_FILE = WeAppTongCengTests/Info.plist; 580 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 581 | LD_RUNPATH_SEARCH_PATHS = ( 582 | "$(inherited)", 583 | "@executable_path/Frameworks", 584 | "@loader_path/Frameworks", 585 | ); 586 | PRODUCT_BUNDLE_IDENTIFIER = com.lionvoom.WeAppTongCengTests; 587 | PRODUCT_NAME = "$(TARGET_NAME)"; 588 | TARGETED_DEVICE_FAMILY = "1,2"; 589 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WeAppTongCeng.app/WeAppTongCeng"; 590 | }; 591 | name = Release; 592 | }; 593 | 090C2D2A25FA0F4400330C86 /* Debug */ = { 594 | isa = XCBuildConfiguration; 595 | buildSettings = { 596 | CODE_SIGN_STYLE = Automatic; 597 | DEVELOPMENT_TEAM = 9PJ22KK765; 598 | INFOPLIST_FILE = WeAppTongCengUITests/Info.plist; 599 | LD_RUNPATH_SEARCH_PATHS = ( 600 | "$(inherited)", 601 | "@executable_path/Frameworks", 602 | "@loader_path/Frameworks", 603 | ); 604 | PRODUCT_BUNDLE_IDENTIFIER = com.lionvoom.WeAppTongCengUITests; 605 | PRODUCT_NAME = "$(TARGET_NAME)"; 606 | TARGETED_DEVICE_FAMILY = "1,2"; 607 | TEST_TARGET_NAME = WeAppTongCeng; 608 | }; 609 | name = Debug; 610 | }; 611 | 090C2D2B25FA0F4400330C86 /* Release */ = { 612 | isa = XCBuildConfiguration; 613 | buildSettings = { 614 | CODE_SIGN_STYLE = Automatic; 615 | DEVELOPMENT_TEAM = 9PJ22KK765; 616 | INFOPLIST_FILE = WeAppTongCengUITests/Info.plist; 617 | LD_RUNPATH_SEARCH_PATHS = ( 618 | "$(inherited)", 619 | "@executable_path/Frameworks", 620 | "@loader_path/Frameworks", 621 | ); 622 | PRODUCT_BUNDLE_IDENTIFIER = com.lionvoom.WeAppTongCengUITests; 623 | PRODUCT_NAME = "$(TARGET_NAME)"; 624 | TARGETED_DEVICE_FAMILY = "1,2"; 625 | TEST_TARGET_NAME = WeAppTongCeng; 626 | }; 627 | name = Release; 628 | }; 629 | /* End XCBuildConfiguration section */ 630 | 631 | /* Begin XCConfigurationList section */ 632 | 090C2CEF25FA0F4100330C86 /* Build configuration list for PBXProject "WeAppTongCeng" */ = { 633 | isa = XCConfigurationList; 634 | buildConfigurations = ( 635 | 090C2D2125FA0F4400330C86 /* Debug */, 636 | 090C2D2225FA0F4400330C86 /* Release */, 637 | ); 638 | defaultConfigurationIsVisible = 0; 639 | defaultConfigurationName = Release; 640 | }; 641 | 090C2D2325FA0F4400330C86 /* Build configuration list for PBXNativeTarget "WeAppTongCeng" */ = { 642 | isa = XCConfigurationList; 643 | buildConfigurations = ( 644 | 090C2D2425FA0F4400330C86 /* Debug */, 645 | 090C2D2525FA0F4400330C86 /* Release */, 646 | ); 647 | defaultConfigurationIsVisible = 0; 648 | defaultConfigurationName = Release; 649 | }; 650 | 090C2D2625FA0F4400330C86 /* Build configuration list for PBXNativeTarget "WeAppTongCengTests" */ = { 651 | isa = XCConfigurationList; 652 | buildConfigurations = ( 653 | 090C2D2725FA0F4400330C86 /* Debug */, 654 | 090C2D2825FA0F4400330C86 /* Release */, 655 | ); 656 | defaultConfigurationIsVisible = 0; 657 | defaultConfigurationName = Release; 658 | }; 659 | 090C2D2925FA0F4400330C86 /* Build configuration list for PBXNativeTarget "WeAppTongCengUITests" */ = { 660 | isa = XCConfigurationList; 661 | buildConfigurations = ( 662 | 090C2D2A25FA0F4400330C86 /* Debug */, 663 | 090C2D2B25FA0F4400330C86 /* Release */, 664 | ); 665 | defaultConfigurationIsVisible = 0; 666 | defaultConfigurationName = Release; 667 | }; 668 | /* End XCConfigurationList section */ 669 | }; 670 | rootObject = 090C2CEC25FA0F4100330C86 /* Project object */; 671 | } 672 | -------------------------------------------------------------------------------- /WeAppTongCeng/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/11. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | @property (nonatomic, strong) UIWindow *window; 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /WeAppTongCeng/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/11. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | @interface AppDelegate () 10 | 11 | @end 12 | 13 | @implementation AppDelegate 14 | 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 17 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 18 | self.window.backgroundColor = [UIColor whiteColor]; 19 | UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; 20 | UIViewController *vc = sb.instantiateInitialViewController; 21 | self.window.rootViewController = vc; 22 | [self.window makeKeyAndVisible]; 23 | return YES; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /WeAppTongCeng/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /WeAppTongCeng/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /WeAppTongCeng/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WeAppTongCeng/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 | -------------------------------------------------------------------------------- /WeAppTongCeng/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /WeAppTongCeng/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSupportsIndirectInputEvents 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVApiHandleDelegate-Protocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVApiHandleDelegate-Protocol.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @protocol LVApiHandleDelegate 13 | 14 | - (void)webView:(WKWebView *)webView invoke:(NSDictionary *)jsonData; 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVApiHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVApiHandler.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import 9 | #import "LVApiHandleDelegate-Protocol.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface LVApiHandler : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVApiHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVApiHandler.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import "LVApiHandler.h" 9 | #import 10 | #import "LVContainerView.h" 11 | 12 | @implementation LVApiHandler 13 | 14 | #pragma mark - LVApiHandleDelegate 15 | - (void)webView:(WKWebView *)webView invoke:(NSDictionary *)jsonData { 16 | NSString *api = jsonData[@"api"]; 17 | NSDictionary *args = jsonData[@"args"]; 18 | UIView *containerSuperview = [self dfsSearch:webView.scrollView containerId:args[@"cid"]]; 19 | if (!containerSuperview) return; 20 | LVContainerView *containerView = [self insertContainerView:containerSuperview]; 21 | if ([api isEqualToString:@"insertInput"]) { 22 | [self insertInput:containerView]; 23 | } else if ([api isEqualToString:@"insertTextArea"]) { 24 | [self insertTextArea:containerView]; 25 | } else if ([api isEqualToString:@"insertVideo"]) { 26 | [self insertVideo:containerView]; 27 | } 28 | } 29 | 30 | - (UIView *)dfsSearch:(UIView *)view containerId:(NSNumber *)containerId { 31 | //TODO: optimize 32 | NSString *str = [NSString stringWithFormat:@"class='container cid_%@'", containerId]; 33 | Class clz = NSClassFromString(@"WKCompositingView"); 34 | if ([view isKindOfClass:clz] && [[[view layer] name] hasSuffix:str]) { 35 | UIView *_WKChildScrollView = view.subviews.firstObject; 36 | if (![_WKChildScrollView isKindOfClass:NSClassFromString(@"WKChildScrollView")]) return nil; 37 | for (UIGestureRecognizer *ges in _WKChildScrollView.gestureRecognizers) { 38 | [_WKChildScrollView removeGestureRecognizer:ges]; 39 | } 40 | return _WKChildScrollView; 41 | } 42 | for (UIView *subView in view.subviews) { 43 | UIView *findView = [self dfsSearch:subView containerId:containerId]; 44 | if (findView) return findView; 45 | } 46 | return nil; 47 | } 48 | 49 | - (void)insertInput:(LVContainerView *)containerView { 50 | UITextField *textField = [[UITextField alloc] initWithFrame:containerView.bounds]; 51 | textField.backgroundColor = UIColor.lightGrayColor; 52 | textField.font = [UIFont systemFontOfSize:20]; 53 | textField.text = @"微信小程序同层渲染"; 54 | [containerView addSubview:textField]; 55 | } 56 | 57 | - (void)insertTextArea:(LVContainerView *)containerView { 58 | UITextView *textView = [[UITextView alloc] initWithFrame:containerView.bounds]; 59 | textView.backgroundColor = UIColor.brownColor; 60 | textView.font = [UIFont systemFontOfSize:30]; 61 | textView.text = @"微信小程序同层渲染效果实现"; 62 | [containerView addSubview:textView]; 63 | } 64 | 65 | - (void)insertVideo:(LVContainerView *)containerView { 66 | UIButton *btn = [[UIButton alloc] initWithFrame:containerView.bounds]; 67 | btn.backgroundColor = UIColor.blueColor; 68 | [btn setTitle:@"请替换成video组件" forState:UIControlStateNormal]; 69 | [containerView addSubview:btn]; 70 | [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside]; 71 | } 72 | 73 | - (LVContainerView *)insertContainerView:(UIView *)containerSuperview { 74 | LVContainerView *containerView = [[LVContainerView alloc] initWithFrame:containerSuperview.bounds]; 75 | [containerSuperview addSubview:containerView]; 76 | return containerView; 77 | } 78 | 79 | - (void)btnAction:(UIView *)view { 80 | UIColor * randomColor= [UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0]; 81 | view.backgroundColor = randomColor; 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVContainerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVContainerView.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface LVContainerView : UIView 13 | @end 14 | 15 | NS_ASSUME_NONNULL_END 16 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVContainerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVContainerView.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/12. 6 | // 7 | 8 | #import "LVContainerView.h" 9 | 10 | @implementation LVContainerView 11 | 12 | // LVContainerView conforms protocol WKNativelyInteractible 13 | - (BOOL)conformsToProtocol:(Protocol *)aProtocol { 14 | if (aProtocol == NSProtocolFromString(@"WKNativelyInteractible")) { 15 | return YES; 16 | } 17 | return [super conformsToProtocol:aProtocol]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVTongCengWebDelegate-Protocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVTongCengWebDelegate-Protocol.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import 9 | #import "LVApiHandleDelegate-Protocol.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol LVTongCengWebDelegate 14 | @property (readonly) id apiHandler; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVTongCengWebMgr.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVTongCengWebMgr.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import 9 | #import "LVTongCengWebDelegate-Protocol.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface LVTongCengWebMgr : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVTongCengWebMgr.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVTongCengWebMgr.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import "LVTongCengWebMgr.h" 9 | #import "LVApiHandler.h" 10 | 11 | @interface LVTongCengWebMgr() 12 | @property (nonatomic, strong) LVApiHandler *apiHandler; 13 | @end 14 | 15 | @implementation LVTongCengWebMgr 16 | 17 | - (instancetype)init { 18 | self = [super init]; 19 | if (self) { 20 | self.apiHandler = [[LVApiHandler alloc] init]; 21 | } 22 | return self; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVTongCengWebView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVTongCengWebView.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/11. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface LVTongCengWebView : WKWebView 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVTongCengWebView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVTongCengWebView.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/11. 6 | // 7 | 8 | #import "LVTongCengWebView.h" 9 | 10 | @interface LVTongCengWebView() 11 | @property (nonatomic, assign) BOOL didHandleWKContentGestrues; 12 | @end 13 | 14 | @implementation LVTongCengWebView 15 | 16 | - (void)_handleWKContentGestrues { 17 | UIScrollView *webViewScrollView = self.scrollView; 18 | if ([webViewScrollView isKindOfClass:NSClassFromString(@"WKScrollView")]) { 19 | UIView *_WKContentView = webViewScrollView.subviews.firstObject; 20 | if (![_WKContentView isKindOfClass:NSClassFromString(@"WKContentView")]) return; 21 | NSArray *gestrues = _WKContentView.gestureRecognizers; 22 | Class clz = NSClassFromString(@"UITextTapRecognizer"); 23 | for (UIGestureRecognizer *gesture in gestrues) { 24 | // fix: 原生输入框聚焦时, 再次点击会失焦 25 | if ([gesture isKindOfClass:clz]) { 26 | gesture.enabled = NO; 27 | continue; 28 | } 29 | gesture.cancelsTouchesInView = NO; 30 | gesture.delaysTouchesBegan = NO; 31 | gesture.delaysTouchesEnded = NO; 32 | } 33 | } 34 | } 35 | 36 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 37 | UIView *wkHitView = [super hitTest:point withEvent:event]; 38 | 39 | // Handling the WKContentView GestureRecognizers interception event causing the inserted native control to fail to respond 40 | if (!self.didHandleWKContentGestrues) { 41 | [self _handleWKContentGestrues]; 42 | self.didHandleWKContentGestrues = YES; 43 | } 44 | 45 | // Plan A: LVContainerView conforms to protocol WKNativelyInteractible 46 | 47 | // Plan B: WKChildScrollView -[hitTest:withEvent:] 48 | // if ([wkHitView isKindOfClass:NSClassFromString(@"WKChildScrollView")]) { 49 | // for (UIView *subview in [wkHitView.subviews reverseObjectEnumerator]) { 50 | // CGPoint convertedPoint = [subview convertPoint:point fromView:self]; 51 | // UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event]; 52 | // if (hitTestView) { 53 | // wkHitView = hitTestView; 54 | // break; 55 | // } 56 | // } 57 | // } 58 | 59 | // NSLog(@"hitTest: %@", wkHitView); 60 | return wkHitView; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVWeakProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVWeakProxy.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/15. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface LVWeakProxy : NSProxy 13 | @property (nullable, nonatomic, weak, readonly) id target; 14 | 15 | - (instancetype)initWithTarget:(id)target; 16 | 17 | + (instancetype)proxyWithTarget:(id)target; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVWeakProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVWeakProxy.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/15. 6 | // 7 | 8 | #import "LVWeakProxy.h" 9 | 10 | @implementation LVWeakProxy 11 | 12 | - (instancetype)initWithTarget:(id)target { 13 | _target = target; 14 | return self; 15 | } 16 | 17 | + (instancetype)proxyWithTarget:(id)target { 18 | return [[self alloc] initWithTarget:target]; 19 | } 20 | 21 | - (id)forwardingTargetForSelector:(SEL)selector { 22 | return _target; 23 | } 24 | 25 | - (void)forwardInvocation:(NSInvocation *)invocation { 26 | void *null = NULL; 27 | [invocation setReturnValue:&null]; 28 | } 29 | 30 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 31 | return [NSObject instanceMethodSignatureForSelector:@selector(init)]; 32 | } 33 | 34 | - (BOOL)respondsToSelector:(SEL)aSelector { 35 | return [_target respondsToSelector:aSelector]; 36 | } 37 | 38 | - (BOOL)isEqual:(id)object { 39 | return [_target isEqual:object]; 40 | } 41 | 42 | - (NSUInteger)hash { 43 | return [_target hash]; 44 | } 45 | 46 | - (Class)superclass { 47 | return [_target superclass]; 48 | } 49 | 50 | - (Class)class { 51 | return [_target class]; 52 | } 53 | 54 | - (BOOL)isKindOfClass:(Class)aClass { 55 | return [_target isKindOfClass:aClass]; 56 | } 57 | 58 | - (BOOL)isMemberOfClass:(Class)aClass { 59 | return [_target isMemberOfClass:aClass]; 60 | } 61 | 62 | - (BOOL)conformsToProtocol:(Protocol *)aProtocol { 63 | return [_target conformsToProtocol:aProtocol]; 64 | } 65 | 66 | - (BOOL)isProxy { 67 | return YES; 68 | } 69 | 70 | - (NSString *)description { 71 | return [_target description]; 72 | } 73 | 74 | - (NSString *)debugDescription { 75 | return [_target debugDescription]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVWebViewBridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVWebViewBridge.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import 9 | #import "LVTongCengWebDelegate-Protocol.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface LVWebViewBridge : NSObject 14 | 15 | - (instancetype)initWithDelegate:(id)delegate; 16 | 17 | - (WKWebViewConfiguration *)genBridgeOfWKUserContentController; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/LVWebViewBridge.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVWebViewBridge.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import "LVWebViewBridge.h" 9 | #import "LVWeakProxy.h" 10 | 11 | @interface LVWebViewBridge() 12 | @property (nonatomic, weak) id delegate; 13 | @end 14 | 15 | @implementation LVWebViewBridge 16 | 17 | - (instancetype)initWithDelegate:(id)delegate { 18 | if (self = [super init]) { 19 | self.delegate = delegate; 20 | } 21 | return self; 22 | } 23 | 24 | - (WKWebViewConfiguration *)genBridgeOfWKUserContentController { 25 | WKUserContentController *userContentController = WKUserContentController.new; 26 | LVWeakProxy *weakProxy = [LVWeakProxy proxyWithTarget:self]; 27 | [userContentController addScriptMessageHandler:(id)weakProxy name:@"handle"]; 28 | WKWebViewConfiguration *configuration = [WKWebViewConfiguration new]; 29 | configuration.userContentController = userContentController; 30 | return configuration; 31 | } 32 | 33 | #pragma mark - WKScriptMessageHandler 34 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { 35 | NSString *name = message.name; 36 | id body = message.body; 37 | NSString *selName = [NSString stringWithFormat:@"webView:%@:", name]; 38 | SEL selector = NSSelectorFromString(selName); 39 | #pragma clang diagnostic push 40 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 41 | if (![self respondsToSelector:selector]) { 42 | NSLog(@"[NO IMP]:%@", selName); 43 | return; 44 | } 45 | [self performSelector:selector withObject:message.webView withObject:body]; 46 | #pragma clang diagnostic pop 47 | } 48 | 49 | - (void)webView:(WKWebView *)webView handle:(NSDictionary *)jsonData { 50 | [self.delegate.apiHandler webView:webView invoke:jsonData]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /WeAppTongCeng/LVTongCeng/YYWAWebView.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | wx小程序同层渲染伪代码 -[YYWAWebView hitTest:withEvent:] 3 | */ 4 | 5 | // local variable allocation has failed, the output may be wrong! 6 | id __cdecl -[YYWAWebView hitTest:withEvent:](YYWAWebView *self, SEL a2, CGPoint a3, id a4) 7 | { 8 | double v4; // d8 9 | double v5; // d9 10 | YYWAWebView *v6; // x20 11 | struct objc_object *v7; // x19 12 | __int64 v8; // x22 13 | Class v9; // x23 14 | void *v10; // x24 15 | __int64 v11; // x25 16 | __int64 v12; // x23 17 | __int64 v13; // x0 18 | __int64 v14; // x23 19 | __int64 v15; // x24 20 | YYWAWebView *v17; // [xsp+10h] [xbp-60h] 21 | __objc2_class *v18; // [xsp+18h] [xbp-58h] 22 | CGPoint v19; // 0:d0.8,8:d1.8 23 | 24 | v4 = a3.var1; 25 | v5 = a3.var0; 26 | v6 = self; 27 | v7 = (struct objc_object *)objc_retain(); 28 | -[YYWAWebView setIsHitSelectableText:](v6, "setIsHitSelectableText:", 0LL); 29 | -[YYWAWebView setIsHitTongCeng:](v6, "setIsHitTongCeng:", 0LL); 30 | v17 = v6; 31 | v18 = &OBJC_CLASS___YYWAWebView; 32 | objc_msgSendSuper2(&v17, "hitTest:withEvent:", v7, v5, v4); 33 | v8 = objc_retainAutoreleasedReturnValue(); 34 | -[YYWAWebView setWkHitView:](v6, "setWkHitView:", v8); 35 | objc_release(v8); 36 | v9 = ((Class (__cdecl *)(WALogService_meta *, SEL))objc_msgSend)( 37 | (WALogService_meta *)&OBJC_CLASS___WALogService, 38 | "externalIMP"); 39 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v6, "wkHitView"); 40 | v10 = (void *)objc_retainAutoreleasedReturnValue(); 41 | objc_msgSend(v10, "clsptr"); 42 | v11 = objc_retainAutoreleasedReturnValue(); 43 | objc_msgSend( 44 | v9, 45 | "logWithLevel:module:errorCode:file:line:func:format:", 46 | 2LL, 47 | "WeApp", 48 | 0LL, 49 | "YYWAWebView.mm", 50 | 617LL, 51 | "-[YYWAWebView hitTest:withEvent:]", 52 | CFSTR("WKTongCeng wkHitView=%@"), 53 | v11); 54 | objc_release(v11); 55 | objc_release(v10); 56 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v6, "nativeDelegate"); 57 | v12 = objc_retainAutoreleasedReturnValue(); 58 | objc_release(v12); 59 | if ( !v12 60 | || (v19.var0 = v5, 61 | v19.var1 = v4, 62 | ((void (__cdecl *)(YYWAWebView *, SEL, id, CGPoint, id))objc_msgSend)( 63 | v6, 64 | "hitWKWebNativeTest:point:withEvent:", 65 | (id)v6, 66 | v19, 67 | v7), 68 | (v13 = objc_retainAutoreleasedReturnValue()) == 0) ) 69 | { 70 | v15 = 0LL; 71 | LABEL_7: 72 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v6, "wkHitView"); 73 | v14 = objc_retainAutoreleasedReturnValue(); 74 | objc_release(v15); 75 | goto LABEL_8; 76 | } 77 | v14 = v13; 78 | v15 = v13; 79 | if ( !(unsigned int)-[YYWAWebView isHitTongCeng](v6, "isHitTongCeng") ) 80 | goto LABEL_7; 81 | v15 = v14; 82 | if ( (unsigned int)-[YYWAWebView isHitSelectableText](v6, "isHitSelectableText") ) 83 | goto LABEL_7; 84 | LABEL_8: 85 | -[YYWAWebView setWkHitView:](v6, "setWkHitView:", 0LL); 86 | objc_release(v7); 87 | return (id)objc_autoreleaseReturnValue(v14); 88 | } 89 | 90 | 91 | // local variable allocation has failed, the output may be wrong! 92 | id __cdecl -[YYWAWebView hitWKWebNativeTest:point:withEvent:](YYWAWebView *self, SEL a2, id a3, CGPoint a4, id a5) 93 | { 94 | double v5; // d8 95 | double v6; // d9 96 | YYWAWebView *v7; // x22 97 | void *v8; // x26 98 | struct objc_object *v9; // x27 99 | void *v10; // x20 100 | int v11; // w21 101 | __int64 v12; // x25 102 | const char *v13; // x1 103 | void *v14; // x19 104 | __int64 v15; // x20 105 | void *v16; // x20 106 | char *v17; // x23 107 | signed __int64 v18; // x23 108 | void *v19; // x25 109 | struct objc_object *v20; // x24 110 | __int64 v21; // x19 111 | __int64 v22; // x19 112 | __int64 v23; // x0 113 | __int64 v24; // x21 114 | void *v25; // x0 115 | void *v26; // x22 116 | int v27; // w23 117 | __int64 v28; // x1 118 | YYWAWebView *v29; // x26 119 | unsigned __int64 v30; // x24 120 | void *v31; // x20 121 | __int64 v32; // x28 122 | void *v33; // x0 123 | __int64 v34; // x22 124 | void *v35; // x26 125 | void *v36; // x19 126 | void *v37; // x0 127 | void *v38; // x26 128 | void *v39; // x0 129 | void *v40; // x0 130 | void *v41; // x26 131 | void *v42; // x0 132 | void *v43; // x0 133 | void *v44; // x20 134 | void *v45; // x26 135 | void *v46; // x0 136 | id result; // x0 137 | __int64 v48; // [xsp+0h] [xbp-1E0h] 138 | struct objc_object *v49; // [xsp+8h] [xbp-1D8h] 139 | void *v50; // [xsp+10h] [xbp-1D0h] 140 | void *v51; // [xsp+40h] [xbp-1A0h] 141 | void *v52; // [xsp+50h] [xbp-190h] 142 | __int64 v53; // [xsp+60h] [xbp-180h] 143 | __int64 v54; // [xsp+68h] [xbp-178h] 144 | void *v55; // [xsp+A0h] [xbp-140h] 145 | YYWAWebView *v56; // [xsp+A8h] [xbp-138h] 146 | __int128 v57; // [xsp+B0h] [xbp-130h] 147 | __int128 v58; // [xsp+C0h] [xbp-120h] 148 | __int128 v59; // [xsp+D0h] [xbp-110h] 149 | __int128 v60; // [xsp+E0h] [xbp-100h] 150 | char v61; // [xsp+F0h] [xbp-F0h] 151 | __int64 v62; // [xsp+170h] [xbp-70h] 152 | CGPoint v63; // 0:d0.8,8:d1.8 153 | 154 | v5 = a4.var1; 155 | v6 = a4.var0; 156 | v7 = self; 157 | v8 = (void *)objc_retain(); 158 | v9 = (struct objc_object *)objc_retain(); 159 | v56 = v7; 160 | if ( (unsigned int)objc_msgSend(v8, "isHidden") ) 161 | goto LABEL_15; 162 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v7, "nativeDelegate"); 163 | v10 = (void *)objc_retainAutoreleasedReturnValue(); 164 | v11 = (unsigned __int64)objc_msgSend(v10, "isSelectableText:", v8); 165 | objc_release(v10); 166 | if ( v11 ) 167 | { 168 | if ( (unsigned int)objc_msgSend(v8, "pointInside:withEvent:", v9, v6, v5) ) 169 | { 170 | v12 = objc_retain(); 171 | v13 = "setIsHitSelectableText:"; 172 | LABEL_8: 173 | objc_msgSend(v7, v13, 1LL); 174 | goto LABEL_16; 175 | } 176 | goto LABEL_15; 177 | } 178 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v7, "nativeDelegate"); 179 | v14 = (void *)objc_retainAutoreleasedReturnValue(); 180 | objc_msgSend(v14, "getContainerViewFromNative:", v8); 181 | v15 = objc_retainAutoreleasedReturnValue(); 182 | objc_release(v15); 183 | objc_release(v14); 184 | if ( v15 ) 185 | { 186 | objc_msgSend(v8, "hitTest:withEvent:", v9, v6, v5); 187 | v12 = objc_retainAutoreleasedReturnValue(); 188 | if ( !v12 ) 189 | goto LABEL_16; 190 | v13 = "setIsHitTongCeng:"; 191 | goto LABEL_8; 192 | } 193 | objc_msgSend(v8, "subviews"); 194 | v16 = (void *)objc_retainAutoreleasedReturnValue(); 195 | v17 = (char *)objc_msgSend(v16, "count"); 196 | objc_release(v16); 197 | if ( !v17 ) 198 | { 199 | LABEL_14: 200 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v7, "wkHitView"); 201 | v21 = objc_retainAutoreleasedReturnValue(); 202 | objc_release(v21); 203 | if ( (void *)v21 == v8 ) 204 | { 205 | v12 = objc_retain(); 206 | goto LABEL_16; 207 | } 208 | LABEL_15: 209 | v12 = 0LL; 210 | goto LABEL_16; 211 | } 212 | v18 = (signed __int64)(v17 - 1); 213 | while ( 1 ) 214 | { 215 | objc_msgSend(v8, "subviews"); 216 | v19 = (void *)objc_retainAutoreleasedReturnValue(); 217 | objc_msgSend(v19, "objectAtIndex:", v18); 218 | v20 = (struct objc_object *)objc_retainAutoreleasedReturnValue(); 219 | objc_release(v19); 220 | if ( v20 ) 221 | { 222 | objc_msgSend(v20, "convertPoint:fromView:", v8, v6, v5); 223 | ((void (__cdecl *)(YYWAWebView *, SEL, id, CGPoint, id))objc_msgSend)( 224 | v7, 225 | "hitWKWebNativeTest:point:withEvent:", 226 | v20, 227 | v63, 228 | v9); 229 | v12 = objc_retainAutoreleasedReturnValue(); 230 | objc_release(v20); 231 | if ( v12 ) 232 | break; 233 | } 234 | if ( --v18 == -1 ) 235 | goto LABEL_14; 236 | } 237 | LABEL_16: 238 | objc_msgSend(v8, "superview"); 239 | v22 = objc_retainAutoreleasedReturnValue(); 240 | objc_msgSend(v7, "scrollView"); 241 | v23 = objc_retainAutoreleasedReturnValue(); 242 | v24 = v23; 243 | if ( v22 == v23 ) 244 | { 245 | v25 = objc_msgSend(v8, "class"); 246 | NSStringFromClass(v25); 247 | v26 = (void *)objc_retainAutoreleasedReturnValue(); 248 | v27 = (unsigned __int64)objc_msgSend(v26, "isEqualToString:", CFSTR("WKContentView")); 249 | objc_release(v26); 250 | objc_release(v24); 251 | objc_release(v22); 252 | if ( v27 ) 253 | { 254 | v54 = NSClassFromString(CFSTR("UITapAndAHalfRecognizer"), v28); 255 | v51 = objc_msgSend(&OBJC_CLASS___UILongPressGestureRecognizer, "class", v12, v9); 256 | v57 = 0u; 257 | v58 = 0u; 258 | v59 = 0u; 259 | v60 = 0u; 260 | v50 = v8; 261 | objc_msgSend(v8, "gestureRecognizers"); 262 | v52 = (void *)objc_retainAutoreleasedReturnValue(); 263 | v29 = v56; 264 | v55 = objc_msgSend(v52, "countByEnumeratingWithState:objects:count:", &v57, &v61, 16LL); 265 | if ( v55 ) 266 | { 267 | v53 = *(_QWORD *)v58; 268 | do 269 | { 270 | v30 = 0LL; 271 | do 272 | { 273 | if ( *(_QWORD *)v58 != v53 ) 274 | objc_enumerationMutation(v52); 275 | v31 = *(void **)(*((_QWORD *)&v57 + 1) + 8 * v30); 276 | objc_msgSend(*(void **)(*((_QWORD *)&v57 + 1) + 8 * v30), "setCancelsTouchesInView:", 0LL); 277 | objc_msgSend(v31, "setDelaysTouchesBegan:", 0LL); 278 | objc_msgSend(v31, "setDelaysTouchesEnded:", 0LL); 279 | objc_msgSend(&OBJC_CLASS___NSValue, "valueWithNonretainedObject:", v31); 280 | v32 = objc_retainAutoreleasedReturnValue(); 281 | v33 = objc_msgSend(v31, "isEnabled"); 282 | objc_msgSend(&OBJC_CLASS___NSNumber, "numberWithBool:", v33); 283 | v34 = objc_retainAutoreleasedReturnValue(); 284 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v29, "gestureEnableDic"); 285 | v35 = (void *)objc_retainAutoreleasedReturnValue(); 286 | objc_msgSend(v35, "objectForKey:", v32); 287 | v36 = (void *)objc_retainAutoreleasedReturnValue(); 288 | v37 = v35; 289 | v29 = v56; 290 | objc_release(v37); 291 | if ( (unsigned int)objc_msgSend(v31, "isKindOfClass:", v54) ) 292 | { 293 | if ( (unsigned int)-[YYWAWebView isHitTongCeng](v56, "isHitTongCeng") ) 294 | { 295 | if ( !v36 ) 296 | { 297 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v56, "gestureEnableDic"); 298 | v38 = (void *)objc_retainAutoreleasedReturnValue(); 299 | objc_msgSend(v38, "setObject:forKey:", v34, v32); 300 | v39 = v38; 301 | v29 = v56; 302 | objc_release(v39); 303 | } 304 | objc_msgSend(v31, "setEnabled:", 0LL); 305 | } 306 | else if ( v36 ) 307 | { 308 | v40 = objc_msgSend(v36, "boolValue"); 309 | objc_msgSend(v31, "setEnabled:", v40); 310 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v56, "gestureEnableDic"); 311 | v41 = (void *)objc_retainAutoreleasedReturnValue(); 312 | objc_msgSend(v41, "removeObjectForKey:", v32); 313 | v42 = v41; 314 | v29 = v56; 315 | objc_release(v42); 316 | } 317 | } 318 | if ( (unsigned int)+[DeviceInfo isiOS11plus](&OBJC_CLASS___DeviceInfo, "isiOS11plus") 319 | && (unsigned int)objc_msgSend(v31, "isKindOfClass:", v51) ) 320 | { 321 | if ( (unsigned int)-[YYWAWebView isHitSelectableText](v29, "isHitSelectableText") ) 322 | { 323 | if ( v36 ) 324 | { 325 | v43 = objc_msgSend(v36, "boolValue"); 326 | objc_msgSend(v31, "setEnabled:", v43); 327 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v29, "gestureEnableDic"); 328 | v44 = (void *)objc_retainAutoreleasedReturnValue(); 329 | objc_msgSend(v44, "removeObjectForKey:", v32); 330 | objc_release(v44); 331 | } 332 | } 333 | else 334 | { 335 | if ( !v36 ) 336 | { 337 | ((void (__cdecl *)(YYWAWebView *, SEL))objc_msgSend)(v29, "gestureEnableDic"); 338 | v45 = (void *)objc_retainAutoreleasedReturnValue(); 339 | objc_msgSend(v45, "setObject:forKey:", v34, v32); 340 | v46 = v45; 341 | v29 = v56; 342 | objc_release(v46); 343 | } 344 | objc_msgSend(v31, "setEnabled:", 0LL); 345 | } 346 | } 347 | objc_release(v36); 348 | objc_release(v34); 349 | objc_release(v32); 350 | ++v30; 351 | } 352 | while ( v30 < (unsigned __int64)v55 ); 353 | v55 = objc_msgSend(v52, "countByEnumeratingWithState:objects:count:", &v57, &v61, 16LL); 354 | } 355 | while ( v55 ); 356 | } 357 | objc_release(v52); 358 | v9 = v49; 359 | v8 = v50; 360 | v12 = v48; 361 | } 362 | } 363 | else 364 | { 365 | objc_release(v23); 366 | objc_release(v22); 367 | } 368 | objc_release(v9); 369 | result = (id)objc_release(v8); 370 | if ( __stack_chk_guard == v62 ) 371 | result = (id)objc_autoreleaseReturnValue(v12); 372 | return result; 373 | } 374 | -------------------------------------------------------------------------------- /WeAppTongCeng/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/11. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /WeAppTongCeng/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/11. 6 | // 7 | 8 | #import "ViewController.h" 9 | 10 | @interface ViewController () 11 | 12 | @end 13 | 14 | @implementation ViewController 15 | 16 | - (void)viewDidLoad { 17 | [super viewDidLoad]; 18 | // Do any additional setup after loading the view. 19 | } 20 | 21 | - (IBAction)toWKWebViewVC:(id)sender { 22 | [self.navigationController pushViewController:NSClassFromString(@"LVWKWebController").new animated:YES]; 23 | } 24 | 25 | - (IBAction)toTongCengWebViewVC:(id)sender { 26 | [self.navigationController pushViewController:NSClassFromString(@"LVTongCengWebController").new animated:YES]; 27 | } 28 | 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /WeAppTongCeng/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WeAppTongCeng 4 | // 5 | // Created by wulinfeng on 2021/3/11. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /WeAppTongCeng/test/LVTongCengWebController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVTongCengWebController.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/12. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface LVTongCengWebController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /WeAppTongCeng/test/LVTongCengWebController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVTongCengWebController.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/12. 6 | // 7 | 8 | #import "LVTongCengWebController.h" 9 | #import "LVTongCengWebView.h" 10 | #import "LVWebViewBridge.h" 11 | #import "LVTongCengWebMgr.h" 12 | 13 | @interface LVTongCengWebController () 14 | @property (strong, nonatomic) WKWebView *webView; 15 | @property (strong, nonatomic) LVTongCengWebMgr *tongCengWebMgr; 16 | @property (strong, nonatomic) LVWebViewBridge *webBridge; 17 | @end 18 | 19 | @implementation LVTongCengWebController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | self.view.backgroundColor = UIColor.whiteColor; 24 | self.title = @"TongCeng WebView"; 25 | 26 | self.tongCengWebMgr = [[LVTongCengWebMgr alloc] init]; 27 | self.webBridge = [[LVWebViewBridge alloc] initWithDelegate:self.tongCengWebMgr]; 28 | 29 | WKWebView *webView = [[[self webViewClass] alloc] initWithFrame:self.view.bounds configuration:[self.webBridge genBridgeOfWKUserContentController]]; 30 | [self.view addSubview:webView]; 31 | NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"tongceng.html" ofType:nil]; 32 | NSString *html = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil]; 33 | [webView loadHTMLString:html baseURL:nil]; 34 | self.webView = webView; 35 | } 36 | 37 | - (Class)webViewClass { 38 | return LVTongCengWebView.class; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /WeAppTongCeng/test/LVWKWebController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVWKWebController.h 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import "LVTongCengWebController.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface LVWKWebController : LVTongCengWebController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /WeAppTongCeng/test/LVWKWebController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LVWKWebController.m 3 | // WeAppTongCeng 4 | // 5 | // Created by lionvoom on 2021/3/16. 6 | // 7 | 8 | #import "LVWKWebController.h" 9 | 10 | @interface LVWKWebController () 11 | 12 | @end 13 | 14 | @implementation LVWKWebController 15 | 16 | - (void)viewDidLoad { 17 | [super viewDidLoad]; 18 | self.title = @"WKWebView(native no respond)"; 19 | } 20 | 21 | - (Class)webViewClass { 22 | return NSClassFromString(@"WKWebView"); 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /WeAppTongCeng/test/LVWebViewBridge/LVBridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVBridge.h 3 | // WeAppTongCeng 4 | // 5 | // Created by wulinfeng on 2021/3/16. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface LVWebViewBridge : NSObject 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /WeAppTongCeng/test/LVWebViewBridge/LVTongCengWebManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // LVTongCengWebManager.h 3 | // WeAppTongCeng 4 | // 5 | // Created by wulinfeng on 2021/3/16. 6 | // 7 | 8 | #import 9 | #import "LVTongCengWebDelegate-Protocol.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface LVTongCengWebMgr : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /WeAppTongCeng/test/tongceng.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 同层渲染测试 7 | 8 | 65 | 66 | 67 | 68 | 69 | 70 |
71 |
 
72 |
73 |
74 |
 
75 |
76 |
77 |
 
78 |
79 |
more content
80 |
81 | 82 | 83 | 84 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /WeAppTongCengTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /WeAppTongCengTests/WeAppTongCengTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WeAppTongCengTests.m 3 | // WeAppTongCengTests 4 | // 5 | // Created by lionvoom on 2021/3/11. 6 | // 7 | 8 | #import 9 | 10 | @interface WeAppTongCengTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation WeAppTongCengTests 15 | 16 | - (void)setUp { 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | - (void)tearDown { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | } 23 | 24 | - (void)testExample { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | - (void)testPerformanceExample { 30 | // This is an example of a performance test case. 31 | [self measureBlock:^{ 32 | // Put the code you want to measure the time of here. 33 | }]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /WeAppTongCengUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /WeAppTongCengUITests/WeAppTongCengUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WeAppTongCengUITests.m 3 | // WeAppTongCengUITests 4 | // 5 | // Created by lionvoom on 2021/3/11. 6 | // 7 | 8 | #import 9 | 10 | @interface WeAppTongCengUITests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation WeAppTongCengUITests 15 | 16 | - (void)setUp { 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | 19 | // In UI tests it is usually best to stop immediately when a failure occurs. 20 | self.continueAfterFailure = NO; 21 | 22 | // 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. 23 | } 24 | 25 | - (void)tearDown { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | - (void)testExample { 30 | // UI tests must launch the application that they test. 31 | XCUIApplication *app = [[XCUIApplication alloc] init]; 32 | [app launch]; 33 | 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | - (void)testLaunchPerformance { 39 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 40 | // This measures how long it takes to launch your application. 41 | [self measureWithMetrics:@[[[XCTApplicationLaunchMetric alloc] init]] block:^{ 42 | [[[XCUIApplication alloc] init] launch]; 43 | }]; 44 | } 45 | } 46 | 47 | @end 48 | --------------------------------------------------------------------------------