├── .DS_Store ├── README.md ├── WKWebView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── jney.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── jney.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── WKWebView.xcscheme │ └── xcschememanagement.plist ├── WKWebView ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── SecondViewController.h ├── SecondViewController.m ├── ViewController.h ├── ViewController.m ├── WKWebViewManager │ ├── HXWKWebView.h │ ├── HXWKWebView.m │ ├── HXWebModel.h │ ├── HXWebModel.m │ └── WebViewJavascriptBridge │ │ ├── WKWebViewJavascriptBridge.h │ │ ├── WKWebViewJavascriptBridge.m │ │ ├── WebViewJavascriptBridge.h │ │ ├── WebViewJavascriptBridge.m │ │ ├── WebViewJavascriptBridgeBase.h │ │ ├── WebViewJavascriptBridgeBase.m │ │ ├── WebViewJavascriptBridge_JS.h │ │ └── WebViewJavascriptBridge_JS.m ├── WKWebViewText.html └── main.m ├── WKWebViewTests ├── Info.plist └── WKWebViewTests.m └── WKWebViewUITests ├── Info.plist └── WKWebViewUITests.m /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengweizhen/WKWebView/c766ca2fbe6dc7d61c33eff649ae9a251fcb1f51/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WKWebView 2 | 自定义封装WKWebView,JS和OC交互(OC调用JS方法,JS调用OC方法和JS与OC之间的传值) 3 | 4 | 5 | 适用于WKWebView和UIWebView,根据系统版本创建对应的webView 6 | 7 | 8 | 使用方法 9 | 1.遵守协议 10 | 2.初始化并加载本地HTML 11 | - (void) initWKWebview { 12 | 13 | self.wkWebView = [[HXWKWebView alloc]initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 100 - 64)]; 14 | NSURL *path = [[NSBundle mainBundle] URLForResource:@"WKWebViewText" withExtension:@"html"]; 15 | [self.wkWebView hx_loadRequest:[NSURLRequest requestWithURL:path]]; 16 | [self.view addSubview:self.wkWebView]; 17 | self.wkWebView.delegate = self; 18 | 19 | } 20 | 21 | 3.实现代理 22 | /** 23 | webView加载完成 24 | 25 | @param hxWebview webView管理工具 26 | @param URL 加载url 27 | */ 28 | - (void)hx_webView:(HXWKWebView *)hxWebview didFinishLoadingURL:(NSURL *)URL; 29 | 30 | /** 31 | webView加载失败 32 | 33 | @param hxWebview webView管理工具 34 | @param URL 加载url 35 | @param error 错误信息 36 | */ 37 | - (void)hx_webView:(HXWKWebView *)hxWebview didFailToLoadURL:(NSURL *)URL error:(NSError *)error; 38 | 39 | /** 40 | webView即将加载 41 | 42 | @param hxWebview webView管理工具 43 | @param URL 加载url 44 | @return 返回是否加载出页面 45 | */ 46 | - (BOOL)hx_webView:(HXWKWebView *)hxWebview shouldStartLoadWithURL:(NSURL *)URL; 47 | 48 | /** 49 | webView开始加载 50 | 51 | @param hxWebview webView管理工具 52 | */ 53 | - (void)hx_webViewDidStartLoad:(HXWKWebView *)hxWebview; 54 | 55 | 56 | /** 57 | WKWebView与JS 交互代理 58 | 59 | @param userContentController userContentController description 60 | @param message message description 61 | */ 62 | - (void) hx_userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(HXWebModel *)message; 63 | -------------------------------------------------------------------------------- /WKWebView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B4455DE41F29C7CF00CD3053 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B4455DE31F29C7CF00CD3053 /* main.m */; }; 11 | B4455DE71F29C7CF00CD3053 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B4455DE61F29C7CF00CD3053 /* AppDelegate.m */; }; 12 | B4455DEA1F29C7CF00CD3053 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B4455DE91F29C7CF00CD3053 /* ViewController.m */; }; 13 | B4455DED1F29C7CF00CD3053 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B4455DEB1F29C7CF00CD3053 /* Main.storyboard */; }; 14 | B4455DEF1F29C7CF00CD3053 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B4455DEE1F29C7CF00CD3053 /* Assets.xcassets */; }; 15 | B4455DF21F29C7CF00CD3053 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B4455DF01F29C7CF00CD3053 /* LaunchScreen.storyboard */; }; 16 | B4455DFD1F29C7D000CD3053 /* WKWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B4455DFC1F29C7D000CD3053 /* WKWebViewTests.m */; }; 17 | B4455E081F29C7D000CD3053 /* WKWebViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = B4455E071F29C7D000CD3053 /* WKWebViewUITests.m */; }; 18 | B4455E181F29C83300CD3053 /* HXWKWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = B4455E171F29C83300CD3053 /* HXWKWebView.m */; }; 19 | B4455E1A1F29C96600CD3053 /* WKWebViewText.html in Resources */ = {isa = PBXBuildFile; fileRef = B4455E191F29C96600CD3053 /* WKWebViewText.html */; }; 20 | B4455E1D1F29D2ED00CD3053 /* SecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B4455E1C1F29D2ED00CD3053 /* SecondViewController.m */; }; 21 | B4F47E091F3175550003541C /* WebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = B4F47E021F3175550003541C /* WebViewJavascriptBridge.m */; }; 22 | B4F47E0A1F3175550003541C /* WebViewJavascriptBridge_JS.m in Sources */ = {isa = PBXBuildFile; fileRef = B4F47E041F3175550003541C /* WebViewJavascriptBridge_JS.m */; }; 23 | B4F47E0B1F3175550003541C /* WebViewJavascriptBridgeBase.m in Sources */ = {isa = PBXBuildFile; fileRef = B4F47E061F3175550003541C /* WebViewJavascriptBridgeBase.m */; }; 24 | B4F47E0C1F3175550003541C /* WKWebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = B4F47E081F3175550003541C /* WKWebViewJavascriptBridge.m */; }; 25 | B4F47E0F1F31B2BF0003541C /* HXWebModel.m in Sources */ = {isa = PBXBuildFile; fileRef = B4F47E0E1F31B2BF0003541C /* HXWebModel.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | B4455DF91F29C7D000CD3053 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = B4455DD71F29C7CF00CD3053 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = B4455DDE1F29C7CF00CD3053; 34 | remoteInfo = WKWebView; 35 | }; 36 | B4455E041F29C7D000CD3053 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = B4455DD71F29C7CF00CD3053 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = B4455DDE1F29C7CF00CD3053; 41 | remoteInfo = WKWebView; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | B4455DDF1F29C7CF00CD3053 /* WKWebView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WKWebView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | B4455DE31F29C7CF00CD3053 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 48 | B4455DE51F29C7CF00CD3053 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 49 | B4455DE61F29C7CF00CD3053 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 50 | B4455DE81F29C7CF00CD3053 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 51 | B4455DE91F29C7CF00CD3053 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 52 | B4455DEC1F29C7CF00CD3053 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 53 | B4455DEE1F29C7CF00CD3053 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 54 | B4455DF11F29C7CF00CD3053 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 55 | B4455DF31F29C7CF00CD3053 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | B4455DF81F29C7D000CD3053 /* WKWebViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WKWebViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | B4455DFC1F29C7D000CD3053 /* WKWebViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WKWebViewTests.m; sourceTree = ""; }; 58 | B4455DFE1F29C7D000CD3053 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | B4455E031F29C7D000CD3053 /* WKWebViewUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WKWebViewUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | B4455E071F29C7D000CD3053 /* WKWebViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WKWebViewUITests.m; sourceTree = ""; }; 61 | B4455E091F29C7D000CD3053 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | B4455E161F29C83300CD3053 /* HXWKWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HXWKWebView.h; sourceTree = ""; }; 63 | B4455E171F29C83300CD3053 /* HXWKWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HXWKWebView.m; sourceTree = ""; }; 64 | B4455E191F29C96600CD3053 /* WKWebViewText.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = WKWebViewText.html; sourceTree = ""; }; 65 | B4455E1B1F29D2ED00CD3053 /* SecondViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecondViewController.h; sourceTree = ""; }; 66 | B4455E1C1F29D2ED00CD3053 /* SecondViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecondViewController.m; sourceTree = ""; }; 67 | B4F47E011F3175550003541C /* WebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge.h; sourceTree = ""; }; 68 | B4F47E021F3175550003541C /* WebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge.m; sourceTree = ""; }; 69 | B4F47E031F3175550003541C /* WebViewJavascriptBridge_JS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge_JS.h; sourceTree = ""; }; 70 | B4F47E041F3175550003541C /* WebViewJavascriptBridge_JS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge_JS.m; sourceTree = ""; }; 71 | B4F47E051F3175550003541C /* WebViewJavascriptBridgeBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridgeBase.h; sourceTree = ""; }; 72 | B4F47E061F3175550003541C /* WebViewJavascriptBridgeBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridgeBase.m; sourceTree = ""; }; 73 | B4F47E071F3175550003541C /* WKWebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKWebViewJavascriptBridge.h; sourceTree = ""; }; 74 | B4F47E081F3175550003541C /* WKWebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKWebViewJavascriptBridge.m; sourceTree = ""; }; 75 | B4F47E0D1F31B2BF0003541C /* HXWebModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HXWebModel.h; sourceTree = ""; }; 76 | B4F47E0E1F31B2BF0003541C /* HXWebModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HXWebModel.m; sourceTree = ""; }; 77 | /* End PBXFileReference section */ 78 | 79 | /* Begin PBXFrameworksBuildPhase section */ 80 | B4455DDC1F29C7CF00CD3053 /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | B4455DF51F29C7D000CD3053 /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | B4455E001F29C7D000CD3053 /* Frameworks */ = { 95 | isa = PBXFrameworksBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | /* End PBXFrameworksBuildPhase section */ 102 | 103 | /* Begin PBXGroup section */ 104 | B4455DD61F29C7CF00CD3053 = { 105 | isa = PBXGroup; 106 | children = ( 107 | B4455DE11F29C7CF00CD3053 /* WKWebView */, 108 | B4455DFB1F29C7D000CD3053 /* WKWebViewTests */, 109 | B4455E061F29C7D000CD3053 /* WKWebViewUITests */, 110 | B4455DE01F29C7CF00CD3053 /* Products */, 111 | ); 112 | sourceTree = ""; 113 | }; 114 | B4455DE01F29C7CF00CD3053 /* Products */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | B4455DDF1F29C7CF00CD3053 /* WKWebView.app */, 118 | B4455DF81F29C7D000CD3053 /* WKWebViewTests.xctest */, 119 | B4455E031F29C7D000CD3053 /* WKWebViewUITests.xctest */, 120 | ); 121 | name = Products; 122 | sourceTree = ""; 123 | }; 124 | B4455DE11F29C7CF00CD3053 /* WKWebView */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | B4455E151F29C7F200CD3053 /* WKWebViewManager */, 128 | B4455DE51F29C7CF00CD3053 /* AppDelegate.h */, 129 | B4455DE61F29C7CF00CD3053 /* AppDelegate.m */, 130 | B4455DE81F29C7CF00CD3053 /* ViewController.h */, 131 | B4455DE91F29C7CF00CD3053 /* ViewController.m */, 132 | B4455E1B1F29D2ED00CD3053 /* SecondViewController.h */, 133 | B4455E1C1F29D2ED00CD3053 /* SecondViewController.m */, 134 | B4455DEB1F29C7CF00CD3053 /* Main.storyboard */, 135 | B4455DEE1F29C7CF00CD3053 /* Assets.xcassets */, 136 | B4455DF01F29C7CF00CD3053 /* LaunchScreen.storyboard */, 137 | B4455DF31F29C7CF00CD3053 /* Info.plist */, 138 | B4455DE21F29C7CF00CD3053 /* Supporting Files */, 139 | ); 140 | path = WKWebView; 141 | sourceTree = ""; 142 | }; 143 | B4455DE21F29C7CF00CD3053 /* Supporting Files */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | B4455E191F29C96600CD3053 /* WKWebViewText.html */, 147 | B4455DE31F29C7CF00CD3053 /* main.m */, 148 | ); 149 | name = "Supporting Files"; 150 | sourceTree = ""; 151 | }; 152 | B4455DFB1F29C7D000CD3053 /* WKWebViewTests */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | B4455DFC1F29C7D000CD3053 /* WKWebViewTests.m */, 156 | B4455DFE1F29C7D000CD3053 /* Info.plist */, 157 | ); 158 | path = WKWebViewTests; 159 | sourceTree = ""; 160 | }; 161 | B4455E061F29C7D000CD3053 /* WKWebViewUITests */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | B4455E071F29C7D000CD3053 /* WKWebViewUITests.m */, 165 | B4455E091F29C7D000CD3053 /* Info.plist */, 166 | ); 167 | path = WKWebViewUITests; 168 | sourceTree = ""; 169 | }; 170 | B4455E151F29C7F200CD3053 /* WKWebViewManager */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | B4F47E001F3175550003541C /* WebViewJavascriptBridge */, 174 | B4455E161F29C83300CD3053 /* HXWKWebView.h */, 175 | B4455E171F29C83300CD3053 /* HXWKWebView.m */, 176 | B4F47E0D1F31B2BF0003541C /* HXWebModel.h */, 177 | B4F47E0E1F31B2BF0003541C /* HXWebModel.m */, 178 | ); 179 | path = WKWebViewManager; 180 | sourceTree = ""; 181 | }; 182 | B4F47E001F3175550003541C /* WebViewJavascriptBridge */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | B4F47E011F3175550003541C /* WebViewJavascriptBridge.h */, 186 | B4F47E021F3175550003541C /* WebViewJavascriptBridge.m */, 187 | B4F47E031F3175550003541C /* WebViewJavascriptBridge_JS.h */, 188 | B4F47E041F3175550003541C /* WebViewJavascriptBridge_JS.m */, 189 | B4F47E051F3175550003541C /* WebViewJavascriptBridgeBase.h */, 190 | B4F47E061F3175550003541C /* WebViewJavascriptBridgeBase.m */, 191 | B4F47E071F3175550003541C /* WKWebViewJavascriptBridge.h */, 192 | B4F47E081F3175550003541C /* WKWebViewJavascriptBridge.m */, 193 | ); 194 | path = WebViewJavascriptBridge; 195 | sourceTree = ""; 196 | }; 197 | /* End PBXGroup section */ 198 | 199 | /* Begin PBXNativeTarget section */ 200 | B4455DDE1F29C7CF00CD3053 /* WKWebView */ = { 201 | isa = PBXNativeTarget; 202 | buildConfigurationList = B4455E0C1F29C7D000CD3053 /* Build configuration list for PBXNativeTarget "WKWebView" */; 203 | buildPhases = ( 204 | B4455DDB1F29C7CF00CD3053 /* Sources */, 205 | B4455DDC1F29C7CF00CD3053 /* Frameworks */, 206 | B4455DDD1F29C7CF00CD3053 /* Resources */, 207 | ); 208 | buildRules = ( 209 | ); 210 | dependencies = ( 211 | ); 212 | name = WKWebView; 213 | productName = WKWebView; 214 | productReference = B4455DDF1F29C7CF00CD3053 /* WKWebView.app */; 215 | productType = "com.apple.product-type.application"; 216 | }; 217 | B4455DF71F29C7D000CD3053 /* WKWebViewTests */ = { 218 | isa = PBXNativeTarget; 219 | buildConfigurationList = B4455E0F1F29C7D000CD3053 /* Build configuration list for PBXNativeTarget "WKWebViewTests" */; 220 | buildPhases = ( 221 | B4455DF41F29C7D000CD3053 /* Sources */, 222 | B4455DF51F29C7D000CD3053 /* Frameworks */, 223 | B4455DF61F29C7D000CD3053 /* Resources */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | B4455DFA1F29C7D000CD3053 /* PBXTargetDependency */, 229 | ); 230 | name = WKWebViewTests; 231 | productName = WKWebViewTests; 232 | productReference = B4455DF81F29C7D000CD3053 /* WKWebViewTests.xctest */; 233 | productType = "com.apple.product-type.bundle.unit-test"; 234 | }; 235 | B4455E021F29C7D000CD3053 /* WKWebViewUITests */ = { 236 | isa = PBXNativeTarget; 237 | buildConfigurationList = B4455E121F29C7D000CD3053 /* Build configuration list for PBXNativeTarget "WKWebViewUITests" */; 238 | buildPhases = ( 239 | B4455DFF1F29C7D000CD3053 /* Sources */, 240 | B4455E001F29C7D000CD3053 /* Frameworks */, 241 | B4455E011F29C7D000CD3053 /* Resources */, 242 | ); 243 | buildRules = ( 244 | ); 245 | dependencies = ( 246 | B4455E051F29C7D000CD3053 /* PBXTargetDependency */, 247 | ); 248 | name = WKWebViewUITests; 249 | productName = WKWebViewUITests; 250 | productReference = B4455E031F29C7D000CD3053 /* WKWebViewUITests.xctest */; 251 | productType = "com.apple.product-type.bundle.ui-testing"; 252 | }; 253 | /* End PBXNativeTarget section */ 254 | 255 | /* Begin PBXProject section */ 256 | B4455DD71F29C7CF00CD3053 /* Project object */ = { 257 | isa = PBXProject; 258 | attributes = { 259 | LastUpgradeCheck = 0830; 260 | ORGANIZATIONNAME = Jney; 261 | TargetAttributes = { 262 | B4455DDE1F29C7CF00CD3053 = { 263 | CreatedOnToolsVersion = 8.3.2; 264 | DevelopmentTeam = 7ASPEP8SM6; 265 | ProvisioningStyle = Automatic; 266 | }; 267 | B4455DF71F29C7D000CD3053 = { 268 | CreatedOnToolsVersion = 8.3.2; 269 | DevelopmentTeam = 4Z2CM944BG; 270 | ProvisioningStyle = Automatic; 271 | TestTargetID = B4455DDE1F29C7CF00CD3053; 272 | }; 273 | B4455E021F29C7D000CD3053 = { 274 | CreatedOnToolsVersion = 8.3.2; 275 | DevelopmentTeam = 4Z2CM944BG; 276 | ProvisioningStyle = Automatic; 277 | TestTargetID = B4455DDE1F29C7CF00CD3053; 278 | }; 279 | }; 280 | }; 281 | buildConfigurationList = B4455DDA1F29C7CF00CD3053 /* Build configuration list for PBXProject "WKWebView" */; 282 | compatibilityVersion = "Xcode 3.2"; 283 | developmentRegion = English; 284 | hasScannedForEncodings = 0; 285 | knownRegions = ( 286 | en, 287 | Base, 288 | ); 289 | mainGroup = B4455DD61F29C7CF00CD3053; 290 | productRefGroup = B4455DE01F29C7CF00CD3053 /* Products */; 291 | projectDirPath = ""; 292 | projectRoot = ""; 293 | targets = ( 294 | B4455DDE1F29C7CF00CD3053 /* WKWebView */, 295 | B4455DF71F29C7D000CD3053 /* WKWebViewTests */, 296 | B4455E021F29C7D000CD3053 /* WKWebViewUITests */, 297 | ); 298 | }; 299 | /* End PBXProject section */ 300 | 301 | /* Begin PBXResourcesBuildPhase section */ 302 | B4455DDD1F29C7CF00CD3053 /* Resources */ = { 303 | isa = PBXResourcesBuildPhase; 304 | buildActionMask = 2147483647; 305 | files = ( 306 | B4455DF21F29C7CF00CD3053 /* LaunchScreen.storyboard in Resources */, 307 | B4455E1A1F29C96600CD3053 /* WKWebViewText.html in Resources */, 308 | B4455DEF1F29C7CF00CD3053 /* Assets.xcassets in Resources */, 309 | B4455DED1F29C7CF00CD3053 /* Main.storyboard in Resources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | B4455DF61F29C7D000CD3053 /* Resources */ = { 314 | isa = PBXResourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | }; 320 | B4455E011F29C7D000CD3053 /* Resources */ = { 321 | isa = PBXResourcesBuildPhase; 322 | buildActionMask = 2147483647; 323 | files = ( 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | /* End PBXResourcesBuildPhase section */ 328 | 329 | /* Begin PBXSourcesBuildPhase section */ 330 | B4455DDB1F29C7CF00CD3053 /* Sources */ = { 331 | isa = PBXSourcesBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | B4F47E0B1F3175550003541C /* WebViewJavascriptBridgeBase.m in Sources */, 335 | B4455E1D1F29D2ED00CD3053 /* SecondViewController.m in Sources */, 336 | B4F47E091F3175550003541C /* WebViewJavascriptBridge.m in Sources */, 337 | B4455DEA1F29C7CF00CD3053 /* ViewController.m in Sources */, 338 | B4F47E0A1F3175550003541C /* WebViewJavascriptBridge_JS.m in Sources */, 339 | B4F47E0C1F3175550003541C /* WKWebViewJavascriptBridge.m in Sources */, 340 | B4F47E0F1F31B2BF0003541C /* HXWebModel.m in Sources */, 341 | B4455DE71F29C7CF00CD3053 /* AppDelegate.m in Sources */, 342 | B4455DE41F29C7CF00CD3053 /* main.m in Sources */, 343 | B4455E181F29C83300CD3053 /* HXWKWebView.m in Sources */, 344 | ); 345 | runOnlyForDeploymentPostprocessing = 0; 346 | }; 347 | B4455DF41F29C7D000CD3053 /* Sources */ = { 348 | isa = PBXSourcesBuildPhase; 349 | buildActionMask = 2147483647; 350 | files = ( 351 | B4455DFD1F29C7D000CD3053 /* WKWebViewTests.m in Sources */, 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | }; 355 | B4455DFF1F29C7D000CD3053 /* Sources */ = { 356 | isa = PBXSourcesBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | B4455E081F29C7D000CD3053 /* WKWebViewUITests.m in Sources */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | /* End PBXSourcesBuildPhase section */ 364 | 365 | /* Begin PBXTargetDependency section */ 366 | B4455DFA1F29C7D000CD3053 /* PBXTargetDependency */ = { 367 | isa = PBXTargetDependency; 368 | target = B4455DDE1F29C7CF00CD3053 /* WKWebView */; 369 | targetProxy = B4455DF91F29C7D000CD3053 /* PBXContainerItemProxy */; 370 | }; 371 | B4455E051F29C7D000CD3053 /* PBXTargetDependency */ = { 372 | isa = PBXTargetDependency; 373 | target = B4455DDE1F29C7CF00CD3053 /* WKWebView */; 374 | targetProxy = B4455E041F29C7D000CD3053 /* PBXContainerItemProxy */; 375 | }; 376 | /* End PBXTargetDependency section */ 377 | 378 | /* Begin PBXVariantGroup section */ 379 | B4455DEB1F29C7CF00CD3053 /* Main.storyboard */ = { 380 | isa = PBXVariantGroup; 381 | children = ( 382 | B4455DEC1F29C7CF00CD3053 /* Base */, 383 | ); 384 | name = Main.storyboard; 385 | sourceTree = ""; 386 | }; 387 | B4455DF01F29C7CF00CD3053 /* LaunchScreen.storyboard */ = { 388 | isa = PBXVariantGroup; 389 | children = ( 390 | B4455DF11F29C7CF00CD3053 /* Base */, 391 | ); 392 | name = LaunchScreen.storyboard; 393 | sourceTree = ""; 394 | }; 395 | /* End PBXVariantGroup section */ 396 | 397 | /* Begin XCBuildConfiguration section */ 398 | B4455E0A1F29C7D000CD3053 /* Debug */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | CLANG_ANALYZER_NONNULL = YES; 403 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 404 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 405 | CLANG_CXX_LIBRARY = "libc++"; 406 | CLANG_ENABLE_MODULES = YES; 407 | CLANG_ENABLE_OBJC_ARC = YES; 408 | CLANG_WARN_BOOL_CONVERSION = YES; 409 | CLANG_WARN_CONSTANT_CONVERSION = YES; 410 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 411 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 412 | CLANG_WARN_EMPTY_BODY = YES; 413 | CLANG_WARN_ENUM_CONVERSION = YES; 414 | CLANG_WARN_INFINITE_RECURSION = YES; 415 | CLANG_WARN_INT_CONVERSION = YES; 416 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 417 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 418 | CLANG_WARN_UNREACHABLE_CODE = YES; 419 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 420 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 421 | COPY_PHASE_STRIP = NO; 422 | DEBUG_INFORMATION_FORMAT = dwarf; 423 | ENABLE_STRICT_OBJC_MSGSEND = YES; 424 | ENABLE_TESTABILITY = YES; 425 | GCC_C_LANGUAGE_STANDARD = gnu99; 426 | GCC_DYNAMIC_NO_PIC = NO; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_OPTIMIZATION_LEVEL = 0; 429 | GCC_PREPROCESSOR_DEFINITIONS = ( 430 | "DEBUG=1", 431 | "$(inherited)", 432 | ); 433 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 434 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 435 | GCC_WARN_UNDECLARED_SELECTOR = YES; 436 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 437 | GCC_WARN_UNUSED_FUNCTION = YES; 438 | GCC_WARN_UNUSED_VARIABLE = YES; 439 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 440 | MTL_ENABLE_DEBUG_INFO = YES; 441 | ONLY_ACTIVE_ARCH = YES; 442 | SDKROOT = iphoneos; 443 | }; 444 | name = Debug; 445 | }; 446 | B4455E0B1F29C7D000CD3053 /* Release */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | ALWAYS_SEARCH_USER_PATHS = NO; 450 | CLANG_ANALYZER_NONNULL = YES; 451 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 452 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 453 | CLANG_CXX_LIBRARY = "libc++"; 454 | CLANG_ENABLE_MODULES = YES; 455 | CLANG_ENABLE_OBJC_ARC = YES; 456 | CLANG_WARN_BOOL_CONVERSION = YES; 457 | CLANG_WARN_CONSTANT_CONVERSION = YES; 458 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 459 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 460 | CLANG_WARN_EMPTY_BODY = YES; 461 | CLANG_WARN_ENUM_CONVERSION = YES; 462 | CLANG_WARN_INFINITE_RECURSION = YES; 463 | CLANG_WARN_INT_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 466 | CLANG_WARN_UNREACHABLE_CODE = YES; 467 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 468 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 469 | COPY_PHASE_STRIP = NO; 470 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 471 | ENABLE_NS_ASSERTIONS = NO; 472 | ENABLE_STRICT_OBJC_MSGSEND = YES; 473 | GCC_C_LANGUAGE_STANDARD = gnu99; 474 | GCC_NO_COMMON_BLOCKS = YES; 475 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 476 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 477 | GCC_WARN_UNDECLARED_SELECTOR = YES; 478 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 479 | GCC_WARN_UNUSED_FUNCTION = YES; 480 | GCC_WARN_UNUSED_VARIABLE = YES; 481 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 482 | MTL_ENABLE_DEBUG_INFO = NO; 483 | SDKROOT = iphoneos; 484 | VALIDATE_PRODUCT = YES; 485 | }; 486 | name = Release; 487 | }; 488 | B4455E0D1F29C7D000CD3053 /* Debug */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | DEVELOPMENT_TEAM = 7ASPEP8SM6; 493 | INFOPLIST_FILE = WKWebView/Info.plist; 494 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 495 | PRODUCT_BUNDLE_IDENTIFIER = com.huaxia.creditor.app.WKWebView; 496 | PRODUCT_NAME = "$(TARGET_NAME)"; 497 | }; 498 | name = Debug; 499 | }; 500 | B4455E0E1F29C7D000CD3053 /* Release */ = { 501 | isa = XCBuildConfiguration; 502 | buildSettings = { 503 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 504 | DEVELOPMENT_TEAM = 7ASPEP8SM6; 505 | INFOPLIST_FILE = WKWebView/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | PRODUCT_BUNDLE_IDENTIFIER = com.huaxia.creditor.app.WKWebView; 508 | PRODUCT_NAME = "$(TARGET_NAME)"; 509 | }; 510 | name = Release; 511 | }; 512 | B4455E101F29C7D000CD3053 /* Debug */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | BUNDLE_LOADER = "$(TEST_HOST)"; 516 | DEVELOPMENT_TEAM = 4Z2CM944BG; 517 | INFOPLIST_FILE = WKWebViewTests/Info.plist; 518 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 519 | PRODUCT_BUNDLE_IDENTIFIER = com.huaxia.creditor.app.WKWebViewTests; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WKWebView.app/WKWebView"; 522 | }; 523 | name = Debug; 524 | }; 525 | B4455E111F29C7D000CD3053 /* Release */ = { 526 | isa = XCBuildConfiguration; 527 | buildSettings = { 528 | BUNDLE_LOADER = "$(TEST_HOST)"; 529 | DEVELOPMENT_TEAM = 4Z2CM944BG; 530 | INFOPLIST_FILE = WKWebViewTests/Info.plist; 531 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 532 | PRODUCT_BUNDLE_IDENTIFIER = com.huaxia.creditor.app.WKWebViewTests; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WKWebView.app/WKWebView"; 535 | }; 536 | name = Release; 537 | }; 538 | B4455E131F29C7D000CD3053 /* Debug */ = { 539 | isa = XCBuildConfiguration; 540 | buildSettings = { 541 | DEVELOPMENT_TEAM = 4Z2CM944BG; 542 | INFOPLIST_FILE = WKWebViewUITests/Info.plist; 543 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 544 | PRODUCT_BUNDLE_IDENTIFIER = com.huaxia.creditor.app.WKWebViewUITests; 545 | PRODUCT_NAME = "$(TARGET_NAME)"; 546 | TEST_TARGET_NAME = WKWebView; 547 | }; 548 | name = Debug; 549 | }; 550 | B4455E141F29C7D000CD3053 /* Release */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | DEVELOPMENT_TEAM = 4Z2CM944BG; 554 | INFOPLIST_FILE = WKWebViewUITests/Info.plist; 555 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 556 | PRODUCT_BUNDLE_IDENTIFIER = com.huaxia.creditor.app.WKWebViewUITests; 557 | PRODUCT_NAME = "$(TARGET_NAME)"; 558 | TEST_TARGET_NAME = WKWebView; 559 | }; 560 | name = Release; 561 | }; 562 | /* End XCBuildConfiguration section */ 563 | 564 | /* Begin XCConfigurationList section */ 565 | B4455DDA1F29C7CF00CD3053 /* Build configuration list for PBXProject "WKWebView" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | B4455E0A1F29C7D000CD3053 /* Debug */, 569 | B4455E0B1F29C7D000CD3053 /* Release */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | B4455E0C1F29C7D000CD3053 /* Build configuration list for PBXNativeTarget "WKWebView" */ = { 575 | isa = XCConfigurationList; 576 | buildConfigurations = ( 577 | B4455E0D1F29C7D000CD3053 /* Debug */, 578 | B4455E0E1F29C7D000CD3053 /* Release */, 579 | ); 580 | defaultConfigurationIsVisible = 0; 581 | defaultConfigurationName = Release; 582 | }; 583 | B4455E0F1F29C7D000CD3053 /* Build configuration list for PBXNativeTarget "WKWebViewTests" */ = { 584 | isa = XCConfigurationList; 585 | buildConfigurations = ( 586 | B4455E101F29C7D000CD3053 /* Debug */, 587 | B4455E111F29C7D000CD3053 /* Release */, 588 | ); 589 | defaultConfigurationIsVisible = 0; 590 | defaultConfigurationName = Release; 591 | }; 592 | B4455E121F29C7D000CD3053 /* Build configuration list for PBXNativeTarget "WKWebViewUITests" */ = { 593 | isa = XCConfigurationList; 594 | buildConfigurations = ( 595 | B4455E131F29C7D000CD3053 /* Debug */, 596 | B4455E141F29C7D000CD3053 /* Release */, 597 | ); 598 | defaultConfigurationIsVisible = 0; 599 | defaultConfigurationName = Release; 600 | }; 601 | /* End XCConfigurationList section */ 602 | }; 603 | rootObject = B4455DD71F29C7CF00CD3053 /* Project object */; 604 | } 605 | -------------------------------------------------------------------------------- /WKWebView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WKWebView.xcodeproj/project.xcworkspace/xcuserdata/jney.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengweizhen/WKWebView/c766ca2fbe6dc7d61c33eff649ae9a251fcb1f51/WKWebView.xcodeproj/project.xcworkspace/xcuserdata/jney.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /WKWebView.xcodeproj/xcuserdata/jney.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /WKWebView.xcodeproj/xcuserdata/jney.xcuserdatad/xcschemes/WKWebView.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 | -------------------------------------------------------------------------------- /WKWebView.xcodeproj/xcuserdata/jney.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WKWebView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B4455DDE1F29C7CF00CD3053 16 | 17 | primary 18 | 19 | 20 | B4455DF71F29C7D000CD3053 21 | 22 | primary 23 | 24 | 25 | B4455E021F29C7D000CD3053 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /WKWebView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /WKWebView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /WKWebView/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /WKWebView/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 | -------------------------------------------------------------------------------- /WKWebView/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 | 33 | 34 | 35 | 36 | 37 | 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 | -------------------------------------------------------------------------------- /WKWebView/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 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | NSAppTransportSecurity 38 | 39 | NSAllowsArbitraryLoads 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /WKWebView/SecondViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.h 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SecondViewController : UIViewController 12 | 13 | @property (nonatomic, strong) NSString *htmlText; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /WKWebView/SecondViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.m 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import "SecondViewController.h" 10 | 11 | @interface SecondViewController () 12 | 13 | @end 14 | 15 | @implementation SecondViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | self.view.backgroundColor = [UIColor cyanColor]; 20 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 100)]; 21 | [self.view addSubview:label]; 22 | label.text = self.htmlText; 23 | label.textAlignment = NSTextAlignmentCenter; 24 | label.center = self.view.center; 25 | 26 | } 27 | 28 | - (void)didReceiveMemoryWarning { 29 | [super didReceiveMemoryWarning]; 30 | // Dispose of any resources that can be recreated. 31 | } 32 | 33 | /* 34 | #pragma mark - Navigation 35 | 36 | // In a storyboard-based application, you will often want to do a little preparation before navigation 37 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 38 | // Get the new view controller using [segue destinationViewController]. 39 | // Pass the selected object to the new view controller. 40 | } 41 | */ 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /WKWebView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /WKWebView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SecondViewController.h" 11 | #import "HXWKWebView.h" 12 | 13 | @interface ViewController () 14 | 15 | @property (nonatomic, strong)HXWKWebView *wkWebView; 16 | ///用来存放HTML页面上文本款的字符串 17 | @property (nonatomic, strong)__block NSString *htmlStr; 18 | 19 | @end 20 | 21 | @implementation ViewController 22 | @synthesize wkWebView; 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | 27 | //初始化wkWebView并加载本地HTML 28 | [self initWKWebview]; 29 | 30 | } 31 | - (IBAction)goBack:(UIBarButtonItem *)sender { 32 | 33 | if ([self.wkWebView hx_canGoBack]) { 34 | ///如果WKWebViewk可以返回上一个页面 35 | [self.wkWebView hx_goBack]; 36 | } 37 | 38 | } 39 | 40 | - (IBAction)clickButton:(UIButton *)sender { 41 | 42 | [self.wkWebView hx_stringByEvaluatingSendMessageToJavaScript:@"setValue" paremeter:@"在这里通过OC给JS赋值" completionHandler:^(id object) { 43 | NSLog(@"%@",object); 44 | }]; 45 | } 46 | 47 | /** 48 | 初始化并加载本地HTML 49 | */ 50 | - (void) initWKWebview { 51 | 52 | self.wkWebView = [[HXWKWebView alloc]initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 100 - 64)]; 53 | NSURL *path = [[NSBundle mainBundle] URLForResource:@"WKWebViewText" withExtension:@"html"]; 54 | [self.wkWebView hx_loadURL:path]; 55 | [self.view addSubview:self.wkWebView]; 56 | self.wkWebView.delegate = self; 57 | 58 | } 59 | 60 | #pragma mark - HXWKWebViewDelegate 61 | 62 | /** 63 | HTML开始加载 64 | */ 65 | - (void) hx_webViewDidStartLoad:(HXWKWebView *)hxWebview{ 66 | 67 | NSLog(@"The page starts loading..."); 68 | 69 | } 70 | 71 | /** 72 | HTML加载完毕 73 | */ 74 | - (void) hx_webView:(HXWKWebView *)hxWebview didFinishLoadingURL:(NSURL *)URL{ 75 | 76 | NSLog(@"The page is loaded!"); 77 | self.title = self.wkWebView.titleString; 78 | 79 | } 80 | 81 | /** 82 | HTML加载失败 83 | */ 84 | - (void) hx_webView:(HXWKWebView *)hxWebview didFailToLoadURL:(NSURL *)URL error:(NSError *)error{ 85 | 86 | NSLog(@"Loading error!"); 87 | 88 | } 89 | 90 | 91 | /** 92 | 在url变化或网页内容变化的时候也能调用 93 | */ 94 | - (BOOL) hx_webView:(HXWKWebView *)hxWebview shouldStartLoadWithURL:(NSURL *)URL{ 95 | NSLog(@"Intercept to URL:%@",URL); 96 | return YES; 97 | } 98 | 99 | /** 100 | WKWebView和JS交互代理 101 | */ 102 | - (void)hx_userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(HXWebModel *)message{ 103 | 104 | 105 | NSLog(@"JS交互:%@",message.body); 106 | ///点击网页上的按钮 获取文本款的值 107 | NSString *textString = @"document.getElementById('nameID').value"; 108 | 109 | [self.wkWebView hx_stringByEvaluatingJavaScriptFromString:textString completionHandler:^(id object) { 110 | self.htmlStr = object; 111 | NSLog(@"获取html文本框上的值:%@",object); 112 | }]; 113 | 114 | ///JS调用OC方法 115 | //body只支持NSNumber, NSString, NSDate, NSArray,NSDictionary 和 NSNull类型 116 | if ([message.body isKindOfClass:[NSString class]]) { 117 | //在点击HTML里面的"点击跳转到下一个页面"的时候会发送一个包含"pushNextVC"的message 118 | if ([message.body isEqualToString:@"pushNextVC"]) { 119 | SecondViewController *secondVC = [SecondViewController new]; 120 | secondVC.htmlText = self.htmlStr; 121 | [self.navigationController pushViewController:secondVC animated:YES]; 122 | 123 | } 124 | } 125 | 126 | 127 | } 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/HXWKWebView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXWKWebView.h 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "HXWebModel.h" 12 | 13 | @class HXWKWebView; 14 | @protocol HXWKWebViewDelegate 15 | @required 16 | 17 | /** 18 | webView加载完成 19 | 20 | @param hxWebview webView管理工具 21 | @param URL 加载url 22 | */ 23 | - (void)hx_webView:(HXWKWebView *)hxWebview didFinishLoadingURL:(NSURL *)URL; 24 | 25 | /** 26 | webView加载失败 27 | 28 | @param hxWebview webView管理工具 29 | @param URL 加载url 30 | @param error 错误信息 31 | */ 32 | - (void)hx_webView:(HXWKWebView *)hxWebview didFailToLoadURL:(NSURL *)URL error:(NSError *)error; 33 | 34 | /** 35 | webView即将加载 36 | 37 | @param hxWebview webView管理工具 38 | @param URL 加载url 39 | @return 返回是否加载出页面 40 | */ 41 | - (BOOL)hx_webView:(HXWKWebView *)hxWebview shouldStartLoadWithURL:(NSURL *)URL; 42 | 43 | /** 44 | webView开始加载 45 | 46 | @param hxWebview webView管理工具 47 | */ 48 | - (void)hx_webViewDidStartLoad:(HXWKWebView *)hxWebview; 49 | 50 | @optional 51 | /** 52 | WKWebView与JS 交互代理 53 | 54 | @param userContentController userContentController description 55 | @param message message description 56 | */ 57 | - (void) hx_userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(HXWebModel *)message; 58 | 59 | @end 60 | 61 | typedef void(^CompletionHandler)(id object); 62 | 63 | @interface HXWKWebView : UIView 64 | /* 65 | * webView的标题 66 | */ 67 | @property (nonatomic, strong) NSString *titleString; 68 | /* 69 | * webView进度条 70 | */ 71 | @property (nonatomic, strong) UIProgressView *progressView; 72 | /* 73 | * webView进度条颜色 74 | */ 75 | @property (nonatomic, strong) UIColor *progressColor; 76 | 77 | @property (nonatomic, copy ) CompletionHandler completionHandler; 78 | 79 | @property (nonatomic, strong) WKWebView *wkWebView; 80 | @property (nonatomic, strong) UIWebView *uiWebView; 81 | @property (nonatomic, weak ) id delegate; 82 | 83 | - (instancetype) initWithFrame:(CGRect)frame; 84 | 85 | 86 | /** 87 | 是否可以返回 88 | 89 | @return 是否可以返回 90 | */ 91 | - (BOOL) hx_canGoBack; 92 | 93 | /** 94 | 刷新页面 95 | */ 96 | - (void) hx_reload; 97 | 98 | /** 99 | 返回 100 | */ 101 | - (void) hx_goBack; 102 | 103 | /** 104 | 加载网络请求 105 | 106 | @param request request 107 | */ 108 | - (void)hx_loadRequest:(NSURLRequest *)request; 109 | 110 | /** 111 | 加载url 112 | 113 | @param URL url 114 | */ 115 | - (void)hx_loadURL:(NSURL *)URL; 116 | 117 | /** 118 | 加载URLString字符串 119 | 120 | @param URLString 字符串 121 | */ 122 | - (void)hx_loadURLString:(NSString *)URLString; 123 | 124 | /** 125 | 加载html字符串 126 | 127 | @param HTMLString html字符串 128 | */ 129 | - (void)hx_loadHTMLString:(NSString *)HTMLString; 130 | 131 | 132 | /** 133 | HTML给OC发送消息 134 | 135 | @param scriptString 脚本字符串 136 | @param handlerBlock 回调 137 | */ 138 | - (void)hx_stringByEvaluatingJavaScriptFromString:(NSString *)scriptString completionHandler:(CompletionHandler)handlerBlock; 139 | 140 | 141 | /** 142 | OC给HTML页面发送消息 143 | 144 | @param name 函数名 145 | @param paremeter 参数 146 | @param handlerBlock 回调 147 | */ 148 | - (void)hx_stringByEvaluatingSendMessageToJavaScript:(NSString *)name paremeter:(NSString *) paremeter completionHandler:(CompletionHandler)handlerBlock; 149 | @end 150 | 151 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/HXWKWebView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXWKWebView.m 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import "HXWKWebView.h" 10 | #import "WebViewJavascriptBridge.h" 11 | 12 | #define isiOS8 [[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0 13 | 14 | static void *HXWebBrowserContext = &HXWebBrowserContext; 15 | 16 | @interface HXWKWebView () 17 | 18 | @property WebViewJavascriptBridge* bridge; 19 | 20 | /* 21 | * webView进度条定时器 22 | */ 23 | @property (nonatomic, strong) NSTimer *fakeProgressTimer; 24 | @property (nonatomic, assign) BOOL uiWebViewIsLoading; 25 | @property (nonatomic, strong) NSURL *uiWebViewCurrentURL; 26 | @property (nonatomic, strong) NSURL *URLToLaunchWithPermission; 27 | @property (nonatomic, strong) UIAlertView *externalAppPermissionAlertView; 28 | 29 | @end 30 | 31 | @implementation HXWKWebView 32 | 33 | - (instancetype)initWithFrame:(CGRect)frame{ 34 | 35 | self = [super initWithFrame:frame]; 36 | if (self) { 37 | 38 | if(isiOS8) { 39 | WKWebViewConfiguration *config = [WKWebViewConfiguration new]; 40 | //初始化偏好设置属性:preferences 41 | config.preferences = [WKPreferences new]; 42 | //The minimum font size in points default is 0; 43 | config.preferences.minimumFontSize = 10; 44 | //是否支持JavaScript 45 | config.preferences.javaScriptEnabled = YES; 46 | //不通过用户交互,是否可以打开窗口 47 | config.preferences.javaScriptCanOpenWindowsAutomatically = NO; 48 | //通过JS与webView内容交互 49 | config.userContentController = [WKUserContentController new]; 50 | 51 | //注入JS对象名称senderModel,当JS通过senderModel来调用时,我们可以在WKScriptMessageHandler代理中接收到 52 | [config.userContentController addScriptMessageHandler:self name:@"senderModel"]; 53 | 54 | self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height) configuration:config]; 55 | [self.wkWebView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 56 | [self.wkWebView setNavigationDelegate:self]; 57 | [self.wkWebView setUIDelegate:self]; 58 | [self.wkWebView setMultipleTouchEnabled:YES]; 59 | [self.wkWebView setAutoresizesSubviews:YES]; 60 | [self.wkWebView.scrollView setAlwaysBounceVertical:YES]; 61 | [self addSubview:self.wkWebView]; 62 | self.wkWebView.scrollView.bounces = NO; 63 | [self.wkWebView addObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress)) options:0 context:HXWebBrowserContext]; 64 | }else { 65 | self.uiWebView = [[UIWebView alloc] init]; 66 | [self.uiWebView setFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)]; 67 | [self.uiWebView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 68 | [self.uiWebView setDelegate:self]; 69 | [self.uiWebView setMultipleTouchEnabled:YES]; 70 | [self.uiWebView setAutoresizesSubviews:YES]; 71 | [self.uiWebView setScalesPageToFit:YES]; 72 | [self.uiWebView.scrollView setAlwaysBounceVertical:YES]; 73 | self.uiWebView.scrollView.bounces = NO; 74 | self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.uiWebView]; 75 | 76 | [WebViewJavascriptBridge enableLogging]; //开启调试模式 77 | //响应JS通过callhandler发送给OC的消息 78 | [self.bridge registerHandler:@"testJavascriptSendMessage" handler:^(id data, WVJBResponseCallback responseCallback) { 79 | responseCallback(data); 80 | if ([self.delegate respondsToSelector:@selector(hx_userContentController:didReceiveScriptMessage:)]) { 81 | HXWebModel *message = [[HXWebModel alloc] init]; 82 | message.body = data; 83 | [self.delegate hx_userContentController:nil didReceiveScriptMessage:message]; 84 | } 85 | 86 | }]; 87 | [self addSubview:self.uiWebView]; 88 | } 89 | self.backgroundColor = [UIColor whiteColor]; 90 | self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; 91 | [self.progressView setTrackTintColor:[UIColor colorWithWhite:1.0f alpha:0.0f]]; 92 | [self.progressView setFrame:CGRectMake(0, 0, self.frame.size.width, self.progressView.frame.size.height)]; 93 | [self setTintColor:[UIColor colorWithRed:0.400 green:0.863 blue:0.133 alpha:1.000]]; 94 | [self addSubview:self.progressView]; 95 | } 96 | return self; 97 | } 98 | 99 | - (BOOL) hx_canGoBack{ 100 | if (self.wkWebView) { 101 | return [self.wkWebView canGoBack]; 102 | }else{ 103 | return [self.uiWebView canGoBack]; 104 | 105 | } 106 | } 107 | 108 | - (void)hx_reload { 109 | if(self.wkWebView) { 110 | [self.wkWebView reload]; 111 | } else { 112 | [self.uiWebView reload]; 113 | } 114 | } 115 | 116 | 117 | - (void) hx_goBack{ 118 | 119 | if (self.wkWebView) { 120 | [self.wkWebView goBack]; 121 | }else{ 122 | [self.uiWebView goBack]; 123 | 124 | } 125 | } 126 | 127 | - (void)hx_loadRequest:(NSURLRequest *)request { 128 | if(self.wkWebView) { 129 | [self.wkWebView loadRequest:request]; 130 | } 131 | else { 132 | [self.uiWebView loadRequest:request]; 133 | } 134 | } 135 | 136 | - (void)hx_loadURL:(NSURL *)URL { 137 | [self hx_loadRequest:[NSURLRequest requestWithURL:URL]]; 138 | } 139 | 140 | - (void)hx_loadURLString:(NSString *)URLString { 141 | 142 | NSString *urlString = [URLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 143 | NSURL *URL = [NSURL URLWithString:urlString]; 144 | [self hx_loadURL:URL]; 145 | } 146 | 147 | - (void)hx_loadHTMLString:(NSString *)HTMLString { 148 | if(self.wkWebView) { 149 | [self.wkWebView loadHTMLString:HTMLString baseURL:nil]; 150 | } 151 | else if(self.uiWebView) { 152 | [self.uiWebView loadHTMLString:HTMLString baseURL:nil]; 153 | } 154 | } 155 | 156 | - (void)setProgressColor:(UIColor *)progressColor{ 157 | _progressColor = progressColor ?: [UIColor clearColor]; 158 | [self.progressView setTintColor:progressColor]; 159 | } 160 | 161 | 162 | /** 163 | 获取view所在的控制器 164 | 165 | @return 控制器 166 | */ 167 | - (UIViewController *)viewController { 168 | for (UIView* next = [self superview]; next; next = next.superview) { 169 | UIResponder *nextResponder = [next nextResponder]; 170 | if ([nextResponder isKindOfClass:[UIViewController class]]) { 171 | return (UIViewController *)nextResponder; 172 | } 173 | } 174 | return nil; 175 | } 176 | 177 | ///这里是UIWebView代理 178 | #pragma mark - UIWebViewDelegate 179 | - (void)webViewDidStartLoad:(UIWebView *)webView{ 180 | 181 | webView == self.uiWebView ? [self.delegate hx_webViewDidStartLoad:self] : nil; 182 | } 183 | 184 | #pragma mark 监听请求 185 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { 186 | if(webView == self.uiWebView) { 187 | if(![self externalAppRequiredToOpenURL:request.URL]) { 188 | self.uiWebViewCurrentURL = request.URL; 189 | self.uiWebViewIsLoading = YES; 190 | [self fakeProgressViewStartLoading]; 191 | //代理 192 | 193 | return [self.delegate hx_webView:self shouldStartLoadWithURL:request.URL]; 194 | 195 | } 196 | else { 197 | [self launchExternalAppWithURL:request.URL]; 198 | return NO; 199 | } 200 | } 201 | return NO; 202 | } 203 | 204 | - (void)webViewDidFinishLoad:(UIWebView *)webView { 205 | if(webView == self.uiWebView) { 206 | self.titleString = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 207 | if(!self.uiWebView.isLoading) { 208 | self.uiWebViewIsLoading = NO; 209 | [self fakeProgressBarStopLoading]; 210 | } 211 | //代理 212 | [self.delegate hx_webView:self didFinishLoadingURL:self.uiWebView.request.URL]; 213 | } 214 | } 215 | 216 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { 217 | if(webView == self.uiWebView) { 218 | if(!self.uiWebView.isLoading) { 219 | self.uiWebViewIsLoading = NO; 220 | [self fakeProgressBarStopLoading]; 221 | } 222 | //代理 223 | [self.delegate hx_webView:self didFailToLoadURL:self.uiWebView.request.URL error:error]; 224 | } 225 | } 226 | 227 | ///WKWebView代理 228 | #pragma mark - WKNavigationDelegate 229 | 230 | // 当main frame的导航开始请求时,会调用此方法 231 | - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { 232 | if(webView == self.wkWebView) { 233 | //代理 234 | [self.delegate hx_webViewDidStartLoad:self]; 235 | 236 | 237 | } 238 | } 239 | 240 | // 当内容开始返回时调用 241 | - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{ 242 | NSLog(@"当内容开始返回时调用"); 243 | } 244 | 245 | - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { 246 | if(webView == self.wkWebView) { 247 | [webView evaluateJavaScript:@"document.title" completionHandler:^(id _Nullable obj, NSError * _Nullable error) { 248 | self.titleString = obj; 249 | //代理 250 | [self.delegate hx_webView:self didFinishLoadingURL:self.wkWebView.URL]; 251 | }]; 252 | 253 | } 254 | } 255 | 256 | // 当main frame开始加载数据失败时,会回调 257 | - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation 258 | withError:(NSError *)error { 259 | webView == self.wkWebView ? [self.delegate hx_webView:self didFailToLoadURL:self.wkWebView.URL error:error] : nil; 260 | } 261 | 262 | // 当main frame最后下载数据失败时,会回调 263 | - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation 264 | withError:(NSError *)error { 265 | 266 | if(webView == self.wkWebView) { 267 | //代理 268 | [self.delegate hx_webView:self didFailToLoadURL:self.wkWebView.URL error:error]; 269 | } 270 | } 271 | 272 | // 当main frame接收到服务重定向时,会回调此方法 接收到服务器跳转请求之后调用 273 | - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{ 274 | NSLog(@"重定向"); 275 | } 276 | 277 | // 在发送请求之前,决定是否跳转 278 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { 279 | 280 | if(![self callback_webViewShouldStartLoadWithRequest:navigationAction.request navigationType:navigationAction.navigationType]){ 281 | decisionHandler(WKNavigationActionPolicyCancel); 282 | return; 283 | } 284 | if(webView == self.wkWebView) { 285 | NSURL *URL = navigationAction.request.URL; 286 | ///加载失败 287 | NSError *error = nil; 288 | NSHTTPURLResponse *response = nil; 289 | [NSURLConnection sendSynchronousRequest:navigationAction.request returningResponse:&response error:&error]; 290 | ///加载的本地URL(加载本地url的时候成功是不会出现statusCode状态) 291 | if (([response.URL.absoluteString rangeOfString:@"file:"].location == NSNotFound) && ([response.URL.absoluteString rangeOfString:@"about:blank"].location == NSNotFound) && response.statusCode != 200) { 292 | //状态码不是200就是失败 空白页面不算失败 293 | decisionHandler(WKNavigationActionPolicyCancel); 294 | [self.delegate hx_webView:self didFailToLoadURL:self.wkWebView.URL error:nil]; 295 | return ; 296 | } 297 | 298 | if(![self externalAppRequiredToOpenURL:URL]) { 299 | if(!navigationAction.targetFrame) { 300 | //表示webview新开启一个页面 301 | [self hx_loadURL:URL]; 302 | decisionHandler(WKNavigationActionPolicyCancel); 303 | return; 304 | } 305 | [self callback_webViewShouldStartLoadWithRequest:navigationAction.request navigationType:navigationAction.navigationType]; 306 | }else if([[UIApplication sharedApplication] canOpenURL:URL]) { 307 | [self launchExternalAppWithURL:URL]; 308 | decisionHandler(WKNavigationActionPolicyCancel); 309 | return; 310 | } 311 | } 312 | decisionHandler(WKNavigationActionPolicyAllow); 313 | } 314 | 315 | // 在收到响应后,决定是否跳转 316 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ 317 | // 318 | decisionHandler(WKNavigationResponsePolicyAllow); 319 | } 320 | 321 | -(BOOL)callback_webViewShouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(NSInteger)navigationType 322 | { 323 | //代理 324 | return [self.delegate hx_webView:self shouldStartLoadWithURL:request.URL]; 325 | 326 | 327 | } 328 | 329 | #pragma mark - WKUIDelegate 330 | 331 | - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{ 332 | if (!navigationAction.targetFrame.isMainFrame) { 333 | [webView loadRequest:navigationAction.request]; 334 | } 335 | return nil; 336 | } 337 | ///如果需要显示提示框,则需要实现以下代理 338 | 339 | //alert 警告框 340 | - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{ 341 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:message preferredStyle:UIAlertControllerStyleAlert]; 342 | [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 343 | completionHandler(); 344 | }]]; 345 | [[self viewController] presentViewController:alert animated:YES completion:nil]; 346 | 347 | } 348 | 349 | //confirm 确认框 350 | - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{ 351 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:message preferredStyle:UIAlertControllerStyleAlert]; 352 | [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 353 | completionHandler(YES); 354 | }]]; 355 | [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 356 | completionHandler(NO); 357 | }]]; 358 | [[self viewController] presentViewController:alert animated:YES completion:NULL]; 359 | 360 | NSLog(@"confirm message:%@", message); 361 | 362 | } 363 | 364 | - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler { 365 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"输入框" message:@"调用输入框" preferredStyle:UIAlertControllerStyleAlert]; 366 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { 367 | textField.textColor = [UIColor blackColor]; 368 | }]; 369 | 370 | [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 371 | completionHandler([[alert.textFields lastObject] text]); 372 | }]]; 373 | 374 | [[self viewController] presentViewController:alert animated:YES completion:NULL]; 375 | } 376 | 377 | 378 | #pragma mark - Estimated Progress KVO (WKWebView) 379 | 380 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 381 | if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))] && object == self.wkWebView) { 382 | [self.progressView setAlpha:1.0f]; 383 | BOOL animated = self.wkWebView.estimatedProgress > self.progressView.progress; 384 | [self.progressView setProgress:self.wkWebView.estimatedProgress animated:animated]; 385 | 386 | // Once complete, fade out UIProgressView 387 | if(self.wkWebView.estimatedProgress >= 1.0f) { 388 | [UIView animateWithDuration:0.3f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{ 389 | [self.progressView setAlpha:0.0f]; 390 | } completion:^(BOOL finished) { 391 | [self.progressView setProgress:0.0f animated:NO]; 392 | }]; 393 | } 394 | } 395 | else { 396 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 397 | } 398 | } 399 | 400 | #pragma mark - Fake Progress Bar Control (UIWebView) 401 | 402 | - (void)fakeProgressViewStartLoading { 403 | [self.progressView setProgress:0.0f animated:NO]; 404 | [self.progressView setAlpha:1.0f]; 405 | 406 | if(!self.fakeProgressTimer) { 407 | self.fakeProgressTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/60.0f target:self selector:@selector(fakeProgressTimerDidFire:) userInfo:nil repeats:YES]; 408 | } 409 | } 410 | 411 | - (void)fakeProgressBarStopLoading { 412 | if(self.fakeProgressTimer) { 413 | [self.fakeProgressTimer invalidate]; 414 | } 415 | if(self.progressView) { 416 | [self.progressView setProgress:1.0f animated:YES]; 417 | [UIView animateWithDuration:0.3f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{ 418 | [self.progressView setAlpha:0.0f]; 419 | } completion:^(BOOL finished) { 420 | [self.progressView setProgress:0.0f animated:NO]; 421 | }]; 422 | } 423 | } 424 | 425 | - (void)fakeProgressTimerDidFire:(id)sender { 426 | CGFloat increment = 0.005/(self.progressView.progress + 0.2); 427 | if([self.uiWebView isLoading]) { 428 | CGFloat progress = (self.progressView.progress < 0.75f) ? self.progressView.progress + increment : self.progressView.progress + 0.0005; 429 | if(self.progressView.progress < 0.95) { 430 | [self.progressView setProgress:progress animated:YES]; 431 | } 432 | } 433 | } 434 | 435 | #pragma mark - External App Support 436 | 437 | - (BOOL)externalAppRequiredToOpenURL:(NSURL *)URL { 438 | /* 439 | 若需要限制只允许某些前缀的scheme通过请求,则取消下述注释,并在数组内添加自己需要放行的前缀 440 | NSSet *validSchemes = [NSSet setWithArray:@[@"http", @"https",@"file"]]; 441 | return ![validSchemes containsObject:URL.scheme]; 442 | */ 443 | 444 | return !URL; 445 | } 446 | 447 | - (void)launchExternalAppWithURL:(NSURL *)URL { 448 | self.URLToLaunchWithPermission = URL; 449 | if (![self.externalAppPermissionAlertView isVisible]) { 450 | [self.externalAppPermissionAlertView show]; 451 | } 452 | 453 | } 454 | 455 | #pragma mark - UIAlertViewDelegate 456 | 457 | - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { 458 | if(alertView == self.externalAppPermissionAlertView) { 459 | if(buttonIndex != alertView.cancelButtonIndex) { 460 | [[UIApplication sharedApplication] openURL:self.URLToLaunchWithPermission]; 461 | } 462 | self.URLToLaunchWithPermission = nil; 463 | } 464 | } 465 | 466 | #pragma mark - self Life cycle 467 | 468 | - (void)dealloc { 469 | [self.uiWebView setDelegate:nil]; 470 | [self.wkWebView setNavigationDelegate:nil]; 471 | [self.wkWebView setUIDelegate:nil]; 472 | [self.wkWebView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))]; 473 | } 474 | 475 | #pragma mark - WKScriptMessageHandler 476 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ 477 | 478 | if ([self.delegate respondsToSelector:@selector(hx_userContentController:didReceiveScriptMessage:)]) { 479 | HXWebModel *messageModel = [[HXWebModel alloc] init]; 480 | messageModel.name = message.name; 481 | messageModel.body = message.body; 482 | [self.delegate hx_userContentController:userContentController didReceiveScriptMessage:messageModel]; 483 | } 484 | } 485 | 486 | /** 487 | HTML和OC交互 488 | 489 | @param scriptString 脚本字符串 490 | */ 491 | - (void)hx_stringByEvaluatingJavaScriptFromString:(NSString *)scriptString completionHandler:(CompletionHandler)handlerBlock{ 492 | 493 | if (self.wkWebView) { 494 | [self.wkWebView evaluateJavaScript:scriptString completionHandler:^(id _Nullable object, NSError * _Nullable error) { 495 | handlerBlock(object); 496 | 497 | }]; 498 | }else{ 499 | id object = [self.uiWebView stringByEvaluatingJavaScriptFromString:scriptString]; 500 | handlerBlock(object); 501 | } 502 | } 503 | 504 | - (void)hx_stringByEvaluatingSendMessageToJavaScript:(NSString *)name paremeter:(NSString *) paremeter completionHandler:(CompletionHandler)handlerBlock{ 505 | 506 | if (self.wkWebView) { 507 | NSString *scriptString = [NSString stringWithFormat:@"%@('%@')",name,paremeter]; 508 | [self.wkWebView evaluateJavaScript:scriptString completionHandler:^(id _Nullable object, NSError * _Nullable error) { 509 | handlerBlock(object); 510 | }]; 511 | 512 | }else{ 513 | ///给JS发送消息 514 | [self.bridge callHandler:name data:paremeter responseCallback:^(id responseData) { 515 | handlerBlock(responseData); 516 | }]; 517 | 518 | } 519 | } 520 | 521 | @end 522 | 523 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/HXWebModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // HXWebModel.h 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/8/2. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface HXWebModel : NSObject 13 | 14 | @property (nonatomic, copy) id body; 15 | @property (nonatomic, copy) NSString *name; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/HXWebModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // HXWebModel.m 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/8/2. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import "HXWebModel.h" 10 | 11 | @implementation HXWebModel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/WebViewJavascriptBridge/WKWebViewJavascriptBridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // WKWebViewJavascriptBridge.h 3 | // 4 | // Created by @LokiMeyburg on 10/15/14. 5 | // Copyright (c) 2014 @LokiMeyburg. All rights reserved. 6 | // 7 | 8 | #if (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_1) 9 | #define supportsWKWebView 10 | #endif 11 | 12 | #if defined supportsWKWebView 13 | 14 | #import 15 | #import "WebViewJavascriptBridgeBase.h" 16 | #import 17 | 18 | @interface WKWebViewJavascriptBridge : NSObject 19 | 20 | + (instancetype)bridgeForWebView:(WKWebView*)webView; 21 | + (void)enableLogging; 22 | 23 | - (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler; 24 | - (void)removeHandler:(NSString*)handlerName; 25 | - (void)callHandler:(NSString*)handlerName; 26 | - (void)callHandler:(NSString*)handlerName data:(id)data; 27 | - (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; 28 | - (void)reset; 29 | - (void)setWebViewDelegate:(id)webViewDelegate; 30 | - (void)disableJavscriptAlertBoxSafetyTimeout; 31 | 32 | @end 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/WebViewJavascriptBridge/WKWebViewJavascriptBridge.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKWebViewJavascriptBridge.m 3 | // 4 | // Created by @LokiMeyburg on 10/15/14. 5 | // Copyright (c) 2014 @LokiMeyburg. All rights reserved. 6 | // 7 | 8 | 9 | #import "WKWebViewJavascriptBridge.h" 10 | 11 | #if defined supportsWKWebView 12 | 13 | @implementation WKWebViewJavascriptBridge { 14 | __weak WKWebView* _webView; 15 | __weak id _webViewDelegate; 16 | long _uniqueId; 17 | WebViewJavascriptBridgeBase *_base; 18 | } 19 | 20 | /* API 21 | *****/ 22 | 23 | + (void)enableLogging { [WebViewJavascriptBridgeBase enableLogging]; } 24 | 25 | + (instancetype)bridgeForWebView:(WKWebView*)webView { 26 | WKWebViewJavascriptBridge* bridge = [[self alloc] init]; 27 | [bridge _setupInstance:webView]; 28 | [bridge reset]; 29 | return bridge; 30 | } 31 | 32 | - (void)send:(id)data { 33 | [self send:data responseCallback:nil]; 34 | } 35 | 36 | - (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback { 37 | [_base sendData:data responseCallback:responseCallback handlerName:nil]; 38 | } 39 | 40 | - (void)callHandler:(NSString *)handlerName { 41 | [self callHandler:handlerName data:nil responseCallback:nil]; 42 | } 43 | 44 | - (void)callHandler:(NSString *)handlerName data:(id)data { 45 | [self callHandler:handlerName data:data responseCallback:nil]; 46 | } 47 | 48 | - (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback { 49 | [_base sendData:data responseCallback:responseCallback handlerName:handlerName]; 50 | } 51 | 52 | - (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { 53 | _base.messageHandlers[handlerName] = [handler copy]; 54 | } 55 | 56 | - (void)removeHandler:(NSString *)handlerName { 57 | [_base.messageHandlers removeObjectForKey:handlerName]; 58 | } 59 | 60 | - (void)reset { 61 | [_base reset]; 62 | } 63 | 64 | - (void)setWebViewDelegate:(id)webViewDelegate { 65 | _webViewDelegate = webViewDelegate; 66 | } 67 | 68 | - (void)disableJavscriptAlertBoxSafetyTimeout { 69 | [_base disableJavscriptAlertBoxSafetyTimeout]; 70 | } 71 | 72 | /* Internals 73 | ***********/ 74 | 75 | - (void)dealloc { 76 | _base = nil; 77 | _webView = nil; 78 | _webViewDelegate = nil; 79 | _webView.navigationDelegate = nil; 80 | } 81 | 82 | 83 | /* WKWebView Specific Internals 84 | ******************************/ 85 | 86 | - (void) _setupInstance:(WKWebView*)webView { 87 | _webView = webView; 88 | _webView.navigationDelegate = self; 89 | _base = [[WebViewJavascriptBridgeBase alloc] init]; 90 | _base.delegate = self; 91 | } 92 | 93 | 94 | - (void)WKFlushMessageQueue { 95 | [_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) { 96 | if (error != nil) { 97 | NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error); 98 | } 99 | [_base flushMessageQueue:result]; 100 | }]; 101 | } 102 | 103 | - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { 104 | if (webView != _webView) { return; } 105 | 106 | __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; 107 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) { 108 | [strongDelegate webView:webView didFinishNavigation:navigation]; 109 | } 110 | } 111 | 112 | 113 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { 114 | if (webView != _webView) { return; } 115 | 116 | __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; 117 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationResponse:decisionHandler:)]) { 118 | [strongDelegate webView:webView decidePolicyForNavigationResponse:navigationResponse decisionHandler:decisionHandler]; 119 | } 120 | else { 121 | decisionHandler(WKNavigationResponsePolicyAllow); 122 | } 123 | } 124 | 125 | - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler { 126 | if (webView != _webView) { return; } 127 | 128 | __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; 129 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didReceiveAuthenticationChallenge:completionHandler:)]) { 130 | [strongDelegate webView:webView didReceiveAuthenticationChallenge:challenge completionHandler:completionHandler]; 131 | } else { 132 | completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); 133 | } 134 | } 135 | 136 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { 137 | if (webView != _webView) { return; } 138 | NSURL *url = navigationAction.request.URL; 139 | __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; 140 | 141 | if ([_base isWebViewJavascriptBridgeURL:url]) { 142 | if ([_base isBridgeLoadedURL:url]) { 143 | [_base injectJavascriptFile]; 144 | } else if ([_base isQueueMessageURL:url]) { 145 | [self WKFlushMessageQueue]; 146 | } else { 147 | [_base logUnkownMessage:url]; 148 | } 149 | decisionHandler(WKNavigationActionPolicyCancel); 150 | } 151 | 152 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) { 153 | [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler]; 154 | } else { 155 | decisionHandler(WKNavigationActionPolicyAllow); 156 | } 157 | } 158 | 159 | - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { 160 | if (webView != _webView) { return; } 161 | 162 | __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; 163 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) { 164 | [strongDelegate webView:webView didStartProvisionalNavigation:navigation]; 165 | } 166 | } 167 | 168 | 169 | - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { 170 | if (webView != _webView) { return; } 171 | 172 | __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; 173 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailNavigation:withError:)]) { 174 | [strongDelegate webView:webView didFailNavigation:navigation withError:error]; 175 | } 176 | } 177 | 178 | - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { 179 | if (webView != _webView) { return; } 180 | 181 | __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; 182 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailProvisionalNavigation:withError:)]) { 183 | [strongDelegate webView:webView didFailProvisionalNavigation:navigation withError:error]; 184 | } 185 | } 186 | 187 | - (NSString*) _evaluateJavascript:(NSString*)javascriptCommand { 188 | [_webView evaluateJavaScript:javascriptCommand completionHandler:nil]; 189 | return NULL; 190 | } 191 | 192 | 193 | 194 | @end 195 | 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/WebViewJavascriptBridge/WebViewJavascriptBridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewJavascriptBridge.h 3 | // ExampleApp-iOS 4 | // 5 | // Created by Marcus Westin on 6/14/13. 6 | // Copyright (c) 2013 Marcus Westin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WebViewJavascriptBridgeBase.h" 11 | 12 | #if (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_1) 13 | #define supportsWKWebView 14 | #endif 15 | 16 | #if defined supportsWKWebView 17 | #import 18 | #endif 19 | 20 | #if defined __MAC_OS_X_VERSION_MAX_ALLOWED 21 | #define WVJB_PLATFORM_OSX 22 | #define WVJB_WEBVIEW_TYPE WebView 23 | #define WVJB_WEBVIEW_DELEGATE_TYPE NSObject 24 | #define WVJB_WEBVIEW_DELEGATE_INTERFACE NSObject 25 | #elif defined __IPHONE_OS_VERSION_MAX_ALLOWED 26 | #import 27 | #define WVJB_PLATFORM_IOS 28 | #define WVJB_WEBVIEW_TYPE UIWebView 29 | #define WVJB_WEBVIEW_DELEGATE_TYPE NSObject 30 | #define WVJB_WEBVIEW_DELEGATE_INTERFACE NSObject 31 | #endif 32 | 33 | @interface WebViewJavascriptBridge : WVJB_WEBVIEW_DELEGATE_INTERFACE 34 | 35 | 36 | + (instancetype)bridgeForWebView:(id)webView; 37 | + (instancetype)bridge:(id)webView; 38 | 39 | + (void)enableLogging; 40 | + (void)setLogMaxLength:(int)length; 41 | 42 | - (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler; 43 | - (void)removeHandler:(NSString*)handlerName; 44 | - (void)callHandler:(NSString*)handlerName; 45 | - (void)callHandler:(NSString*)handlerName data:(id)data; 46 | - (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; 47 | - (void)setWebViewDelegate:(id)webViewDelegate; 48 | - (void)disableJavscriptAlertBoxSafetyTimeout; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/WebViewJavascriptBridge/WebViewJavascriptBridge.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewJavascriptBridge.m 3 | // ExampleApp-iOS 4 | // 5 | // Created by Marcus Westin on 6/14/13. 6 | // Copyright (c) 2013 Marcus Westin. All rights reserved. 7 | // 8 | 9 | #import "WebViewJavascriptBridge.h" 10 | 11 | #if defined(supportsWKWebView) 12 | #import "WKWebViewJavascriptBridge.h" 13 | #endif 14 | 15 | #if __has_feature(objc_arc_weak) 16 | #define WVJB_WEAK __weak 17 | #else 18 | #define WVJB_WEAK __unsafe_unretained 19 | #endif 20 | 21 | @implementation WebViewJavascriptBridge { 22 | WVJB_WEAK WVJB_WEBVIEW_TYPE* _webView; 23 | WVJB_WEAK id _webViewDelegate; 24 | long _uniqueId; 25 | WebViewJavascriptBridgeBase *_base; 26 | } 27 | 28 | /* API 29 | *****/ 30 | 31 | + (void)enableLogging { 32 | [WebViewJavascriptBridgeBase enableLogging]; 33 | } 34 | + (void)setLogMaxLength:(int)length { 35 | [WebViewJavascriptBridgeBase setLogMaxLength:length]; 36 | } 37 | 38 | + (instancetype)bridgeForWebView:(id)webView { 39 | return [self bridge:webView]; 40 | } 41 | + (instancetype)bridge:(id)webView { 42 | #if defined supportsWKWebView 43 | if ([webView isKindOfClass:[WKWebView class]]) { 44 | return (WebViewJavascriptBridge*) [WKWebViewJavascriptBridge bridgeForWebView:webView]; 45 | } 46 | #endif 47 | if ([webView isKindOfClass:[WVJB_WEBVIEW_TYPE class]]) { 48 | WebViewJavascriptBridge* bridge = [[self alloc] init]; 49 | [bridge _platformSpecificSetup:webView]; 50 | return bridge; 51 | } 52 | [NSException raise:@"BadWebViewType" format:@"Unknown web view type."]; 53 | return nil; 54 | } 55 | 56 | - (void)setWebViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate { 57 | _webViewDelegate = webViewDelegate; 58 | } 59 | 60 | - (void)send:(id)data { 61 | [self send:data responseCallback:nil]; 62 | } 63 | 64 | - (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback { 65 | [_base sendData:data responseCallback:responseCallback handlerName:nil]; 66 | } 67 | 68 | - (void)callHandler:(NSString *)handlerName { 69 | [self callHandler:handlerName data:nil responseCallback:nil]; 70 | } 71 | 72 | - (void)callHandler:(NSString *)handlerName data:(id)data { 73 | [self callHandler:handlerName data:data responseCallback:nil]; 74 | } 75 | 76 | - (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback { 77 | [_base sendData:data responseCallback:responseCallback handlerName:handlerName]; 78 | } 79 | 80 | - (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { 81 | _base.messageHandlers[handlerName] = [handler copy]; 82 | } 83 | 84 | - (void)removeHandler:(NSString *)handlerName { 85 | [_base.messageHandlers removeObjectForKey:handlerName]; 86 | } 87 | 88 | - (void)disableJavscriptAlertBoxSafetyTimeout { 89 | [_base disableJavscriptAlertBoxSafetyTimeout]; 90 | } 91 | 92 | 93 | /* Platform agnostic internals 94 | *****************************/ 95 | 96 | - (void)dealloc { 97 | [self _platformSpecificDealloc]; 98 | _base = nil; 99 | _webView = nil; 100 | _webViewDelegate = nil; 101 | } 102 | 103 | - (NSString*) _evaluateJavascript:(NSString*)javascriptCommand { 104 | return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand]; 105 | } 106 | 107 | #if defined WVJB_PLATFORM_OSX 108 | /* Platform specific internals: OSX 109 | **********************************/ 110 | 111 | - (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView { 112 | _webView = webView; 113 | _webView.policyDelegate = self; 114 | _base = [[WebViewJavascriptBridgeBase alloc] init]; 115 | _base.delegate = self; 116 | } 117 | 118 | - (void) _platformSpecificDealloc { 119 | _webView.policyDelegate = nil; 120 | } 121 | 122 | - (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { 123 | if (webView != _webView) { return; } 124 | 125 | NSURL *url = [request URL]; 126 | if ([_base isWebViewJavascriptBridgeURL:url]) { 127 | if ([_base isBridgeLoadedURL:url]) { 128 | [_base injectJavascriptFile]; 129 | } else if ([_base isQueueMessageURL:url]) { 130 | NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; 131 | [_base flushMessageQueue:messageQueueString]; 132 | } else { 133 | [_base logUnkownMessage:url]; 134 | } 135 | [listener ignore]; 136 | } else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) { 137 | [_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener]; 138 | } else { 139 | [listener use]; 140 | } 141 | } 142 | 143 | 144 | 145 | #elif defined WVJB_PLATFORM_IOS 146 | /* Platform specific internals: iOS 147 | **********************************/ 148 | 149 | - (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView { 150 | _webView = webView; 151 | _webView.delegate = self; 152 | _base = [[WebViewJavascriptBridgeBase alloc] init]; 153 | _base.delegate = self; 154 | } 155 | 156 | - (void) _platformSpecificDealloc { 157 | _webView.delegate = nil; 158 | } 159 | 160 | - (void)webViewDidFinishLoad:(UIWebView *)webView { 161 | if (webView != _webView) { return; } 162 | 163 | __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; 164 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { 165 | [strongDelegate webViewDidFinishLoad:webView]; 166 | } 167 | } 168 | 169 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { 170 | if (webView != _webView) { return; } 171 | 172 | __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; 173 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) { 174 | [strongDelegate webView:webView didFailLoadWithError:error]; 175 | } 176 | } 177 | 178 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { 179 | if (webView != _webView) { return YES; } 180 | 181 | NSURL *url = [request URL]; 182 | __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; 183 | if ([_base isWebViewJavascriptBridgeURL:url]) { 184 | if ([_base isBridgeLoadedURL:url]) { 185 | [_base injectJavascriptFile]; 186 | } else if ([_base isQueueMessageURL:url]) { 187 | NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; 188 | [_base flushMessageQueue:messageQueueString]; 189 | } else { 190 | [_base logUnkownMessage:url]; 191 | } 192 | return NO; 193 | } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { 194 | return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; 195 | } else { 196 | return YES; 197 | } 198 | } 199 | 200 | - (void)webViewDidStartLoad:(UIWebView *)webView { 201 | if (webView != _webView) { return; } 202 | 203 | __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; 204 | if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) { 205 | [strongDelegate webViewDidStartLoad:webView]; 206 | } 207 | } 208 | 209 | #endif 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewJavascriptBridgeBase.h 3 | // 4 | // Created by @LokiMeyburg on 10/15/14. 5 | // Copyright (c) 2014 @LokiMeyburg. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | #define kOldProtocolScheme @"wvjbscheme" 11 | #define kNewProtocolScheme @"https" 12 | #define kQueueHasMessage @"__wvjb_queue_message__" 13 | #define kBridgeLoaded @"__bridge_loaded__" 14 | 15 | typedef void (^WVJBResponseCallback)(id responseData); 16 | typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback); 17 | typedef NSDictionary WVJBMessage; 18 | 19 | @protocol WebViewJavascriptBridgeBaseDelegate 20 | - (NSString*) _evaluateJavascript:(NSString*)javascriptCommand; 21 | @end 22 | 23 | @interface WebViewJavascriptBridgeBase : NSObject 24 | 25 | 26 | @property (weak, nonatomic) id delegate; 27 | @property (strong, nonatomic) NSMutableArray* startupMessageQueue; 28 | @property (strong, nonatomic) NSMutableDictionary* responseCallbacks; 29 | @property (strong, nonatomic) NSMutableDictionary* messageHandlers; 30 | @property (strong, nonatomic) WVJBHandler messageHandler; 31 | 32 | + (void)enableLogging; 33 | + (void)setLogMaxLength:(int)length; 34 | - (void)reset; 35 | - (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName; 36 | - (void)flushMessageQueue:(NSString *)messageQueueString; 37 | - (void)injectJavascriptFile; 38 | - (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url; 39 | - (BOOL)isQueueMessageURL:(NSURL*)urll; 40 | - (BOOL)isBridgeLoadedURL:(NSURL*)urll; 41 | - (void)logUnkownMessage:(NSURL*)url; 42 | - (NSString *)webViewJavascriptCheckCommand; 43 | - (NSString *)webViewJavascriptFetchQueyCommand; 44 | - (void)disableJavscriptAlertBoxSafetyTimeout; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewJavascriptBridgeBase.m 3 | // 4 | // Created by @LokiMeyburg on 10/15/14. 5 | // Copyright (c) 2014 @LokiMeyburg. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "WebViewJavascriptBridgeBase.h" 10 | #import "WebViewJavascriptBridge_JS.h" 11 | 12 | @implementation WebViewJavascriptBridgeBase { 13 | __weak id _webViewDelegate; 14 | long _uniqueId; 15 | } 16 | 17 | static bool logging = false; 18 | static int logMaxLength = 500; 19 | 20 | + (void)enableLogging { logging = true; } 21 | + (void)setLogMaxLength:(int)length { logMaxLength = length;} 22 | 23 | - (id)init { 24 | if (self = [super init]) { 25 | self.messageHandlers = [NSMutableDictionary dictionary]; 26 | self.startupMessageQueue = [NSMutableArray array]; 27 | self.responseCallbacks = [NSMutableDictionary dictionary]; 28 | _uniqueId = 0; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)dealloc { 34 | self.startupMessageQueue = nil; 35 | self.responseCallbacks = nil; 36 | self.messageHandlers = nil; 37 | } 38 | 39 | - (void)reset { 40 | self.startupMessageQueue = [NSMutableArray array]; 41 | self.responseCallbacks = [NSMutableDictionary dictionary]; 42 | _uniqueId = 0; 43 | } 44 | 45 | - (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName { 46 | NSMutableDictionary* message = [NSMutableDictionary dictionary]; 47 | 48 | if (data) { 49 | message[@"data"] = data; 50 | } 51 | 52 | if (responseCallback) { 53 | NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId]; 54 | self.responseCallbacks[callbackId] = [responseCallback copy]; 55 | message[@"callbackId"] = callbackId; 56 | } 57 | 58 | if (handlerName) { 59 | message[@"handlerName"] = handlerName; 60 | } 61 | [self _queueMessage:message]; 62 | } 63 | 64 | - (void)flushMessageQueue:(NSString *)messageQueueString{ 65 | if (messageQueueString == nil || messageQueueString.length == 0) { 66 | NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page."); 67 | return; 68 | } 69 | 70 | id messages = [self _deserializeMessageJSON:messageQueueString]; 71 | for (WVJBMessage* message in messages) { 72 | if (![message isKindOfClass:[WVJBMessage class]]) { 73 | NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message); 74 | continue; 75 | } 76 | [self _log:@"RCVD" json:message]; 77 | 78 | NSString* responseId = message[@"responseId"]; 79 | if (responseId) { 80 | WVJBResponseCallback responseCallback = _responseCallbacks[responseId]; 81 | responseCallback(message[@"responseData"]); 82 | [self.responseCallbacks removeObjectForKey:responseId]; 83 | } else { 84 | WVJBResponseCallback responseCallback = NULL; 85 | NSString* callbackId = message[@"callbackId"]; 86 | if (callbackId) { 87 | responseCallback = ^(id responseData) { 88 | if (responseData == nil) { 89 | responseData = [NSNull null]; 90 | } 91 | 92 | WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; 93 | [self _queueMessage:msg]; 94 | }; 95 | } else { 96 | responseCallback = ^(id ignoreResponseData) { 97 | // Do nothing 98 | }; 99 | } 100 | 101 | WVJBHandler handler = self.messageHandlers[message[@"handlerName"]]; 102 | 103 | if (!handler) { 104 | NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message); 105 | continue; 106 | } 107 | 108 | handler(message[@"data"], responseCallback); 109 | } 110 | } 111 | } 112 | 113 | - (void)injectJavascriptFile { 114 | NSString *js = WebViewJavascriptBridge_js(); 115 | [self _evaluateJavascript:js]; 116 | if (self.startupMessageQueue) { 117 | NSArray* queue = self.startupMessageQueue; 118 | self.startupMessageQueue = nil; 119 | for (id queuedMessage in queue) { 120 | [self _dispatchMessage:queuedMessage]; 121 | } 122 | } 123 | } 124 | 125 | - (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url { 126 | if (![self isSchemeMatch:url]) { 127 | return NO; 128 | } 129 | return [self isBridgeLoadedURL:url] || [self isQueueMessageURL:url]; 130 | } 131 | 132 | - (BOOL)isSchemeMatch:(NSURL*)url { 133 | NSString* scheme = url.scheme.lowercaseString; 134 | return [scheme isEqualToString:kNewProtocolScheme] || [scheme isEqualToString:kOldProtocolScheme]; 135 | } 136 | 137 | - (BOOL)isQueueMessageURL:(NSURL*)url { 138 | NSString* host = url.host.lowercaseString; 139 | return [self isSchemeMatch:url] && [host isEqualToString:kQueueHasMessage]; 140 | } 141 | 142 | - (BOOL)isBridgeLoadedURL:(NSURL*)url { 143 | NSString* host = url.host.lowercaseString; 144 | return [self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded]; 145 | } 146 | 147 | - (void)logUnkownMessage:(NSURL*)url { 148 | NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@", [url absoluteString]); 149 | } 150 | 151 | - (NSString *)webViewJavascriptCheckCommand { 152 | return @"typeof WebViewJavascriptBridge == \'object\';"; 153 | } 154 | 155 | - (NSString *)webViewJavascriptFetchQueyCommand { 156 | return @"WebViewJavascriptBridge._fetchQueue();"; 157 | } 158 | 159 | - (void)disableJavscriptAlertBoxSafetyTimeout { 160 | [self sendData:nil responseCallback:nil handlerName:@"_disableJavascriptAlertBoxSafetyTimeout"]; 161 | } 162 | 163 | // Private 164 | // ------------------------------------------- 165 | 166 | - (void) _evaluateJavascript:(NSString *)javascriptCommand { 167 | [self.delegate _evaluateJavascript:javascriptCommand]; 168 | } 169 | 170 | - (void)_queueMessage:(WVJBMessage*)message { 171 | if (self.startupMessageQueue) { 172 | [self.startupMessageQueue addObject:message]; 173 | } else { 174 | [self _dispatchMessage:message]; 175 | } 176 | } 177 | 178 | - (void)_dispatchMessage:(WVJBMessage*)message { 179 | NSString *messageJSON = [self _serializeMessage:message pretty:NO]; 180 | [self _log:@"SEND" json:messageJSON]; 181 | messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; 182 | messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; 183 | messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"]; 184 | messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; 185 | messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; 186 | messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"]; 187 | messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"]; 188 | messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"]; 189 | 190 | NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON]; 191 | if ([[NSThread currentThread] isMainThread]) { 192 | [self _evaluateJavascript:javascriptCommand]; 193 | 194 | } else { 195 | dispatch_sync(dispatch_get_main_queue(), ^{ 196 | [self _evaluateJavascript:javascriptCommand]; 197 | }); 198 | } 199 | } 200 | 201 | - (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{ 202 | return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding]; 203 | } 204 | 205 | - (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON { 206 | return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; 207 | } 208 | 209 | - (void)_log:(NSString *)action json:(id)json { 210 | if (!logging) { return; } 211 | if (![json isKindOfClass:[NSString class]]) { 212 | json = [self _serializeMessage:json pretty:YES]; 213 | } 214 | if ([json length] > logMaxLength) { 215 | NSLog(@"WVJB %@: %@ [...]", action, [json substringToIndex:logMaxLength]); 216 | } else { 217 | NSLog(@"WVJB %@: %@", action, json); 218 | } 219 | } 220 | 221 | @end 222 | -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NSString * WebViewJavascriptBridge_js(); -------------------------------------------------------------------------------- /WKWebView/WKWebViewManager/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.m: -------------------------------------------------------------------------------- 1 | // This file contains the source for the Javascript side of the 2 | // WebViewJavascriptBridge. It is plaintext, but converted to an NSString 3 | // via some preprocessor tricks. 4 | // 5 | // Previous implementations of WebViewJavascriptBridge loaded the javascript source 6 | // from a resource. This worked fine for app developers, but library developers who 7 | // included the bridge into their library, awkwardly had to ask consumers of their 8 | // library to include the resource, violating their encapsulation. By including the 9 | // Javascript as a string resource, the encapsulation of the library is maintained. 10 | 11 | #import "WebViewJavascriptBridge_JS.h" 12 | 13 | NSString * WebViewJavascriptBridge_js() { 14 | #define __wvjb_js_func__(x) #x 15 | 16 | // BEGIN preprocessorJSCode 17 | static NSString * preprocessorJSCode = @__wvjb_js_func__( 18 | ;(function() { 19 | if (window.WebViewJavascriptBridge) { 20 | return; 21 | } 22 | 23 | if (!window.onerror) { 24 | window.onerror = function(msg, url, line) { 25 | console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":" + line); 26 | } 27 | } 28 | window.WebViewJavascriptBridge = { 29 | registerHandler: registerHandler, 30 | callHandler: callHandler, 31 | disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout, 32 | _fetchQueue: _fetchQueue, 33 | _handleMessageFromObjC: _handleMessageFromObjC 34 | }; 35 | 36 | var messagingIframe; 37 | var sendMessageQueue = []; 38 | var messageHandlers = {}; 39 | 40 | var CUSTOM_PROTOCOL_SCHEME = 'https'; 41 | var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__'; 42 | 43 | var responseCallbacks = {}; 44 | var uniqueId = 1; 45 | var dispatchMessagesWithTimeoutSafety = true; 46 | 47 | function registerHandler(handlerName, handler) { 48 | messageHandlers[handlerName] = handler; 49 | } 50 | 51 | function callHandler(handlerName, data, responseCallback) { 52 | if (arguments.length == 2 && typeof data == 'function') { 53 | responseCallback = data; 54 | data = null; 55 | } 56 | _doSend({ handlerName:handlerName, data:data }, responseCallback); 57 | } 58 | function disableJavscriptAlertBoxSafetyTimeout() { 59 | dispatchMessagesWithTimeoutSafety = false; 60 | } 61 | 62 | function _doSend(message, responseCallback) { 63 | if (responseCallback) { 64 | var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime(); 65 | responseCallbacks[callbackId] = responseCallback; 66 | message['callbackId'] = callbackId; 67 | } 68 | sendMessageQueue.push(message); 69 | messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; 70 | } 71 | 72 | function _fetchQueue() { 73 | var messageQueueString = JSON.stringify(sendMessageQueue); 74 | sendMessageQueue = []; 75 | return messageQueueString; 76 | } 77 | 78 | function _dispatchMessageFromObjC(messageJSON) { 79 | if (dispatchMessagesWithTimeoutSafety) { 80 | setTimeout(_doDispatchMessageFromObjC); 81 | } else { 82 | _doDispatchMessageFromObjC(); 83 | } 84 | 85 | function _doDispatchMessageFromObjC() { 86 | var message = JSON.parse(messageJSON); 87 | var messageHandler; 88 | var responseCallback; 89 | 90 | if (message.responseId) { 91 | responseCallback = responseCallbacks[message.responseId]; 92 | if (!responseCallback) { 93 | return; 94 | } 95 | responseCallback(message.responseData); 96 | delete responseCallbacks[message.responseId]; 97 | } else { 98 | if (message.callbackId) { 99 | var callbackResponseId = message.callbackId; 100 | responseCallback = function(responseData) { 101 | _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); 102 | }; 103 | } 104 | 105 | var handler = messageHandlers[message.handlerName]; 106 | if (!handler) { 107 | console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); 108 | } else { 109 | handler(message.data, responseCallback); 110 | } 111 | } 112 | } 113 | } 114 | 115 | function _handleMessageFromObjC(messageJSON) { 116 | _dispatchMessageFromObjC(messageJSON); 117 | } 118 | 119 | messagingIframe = document.createElement('iframe'); 120 | messagingIframe.style.display = 'none'; 121 | messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; 122 | document.documentElement.appendChild(messagingIframe); 123 | 124 | registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout); 125 | 126 | setTimeout(_callWVJBCallbacks, 0); 127 | function _callWVJBCallbacks() { 128 | var callbacks = window.WVJBCallbacks; 129 | delete window.WVJBCallbacks; 130 | for (var i=0; i 2 | 3 | 4 | 5 | iOS与H5交互练习 6 | 11 | 12 | 13 | 14 |

一起来学习吧!

15 | 16 |
17 | 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | 28 |
29 |

30 | 31 | 32 |
33 | 34 |
35 |
36 | 37 |
38 | 39 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /WKWebView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WKWebView 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import 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 | -------------------------------------------------------------------------------- /WKWebViewTests/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 | -------------------------------------------------------------------------------- /WKWebViewTests/WKWebViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKWebViewTests.m 3 | // WKWebViewTests 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WKWebViewTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation WKWebViewTests 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 | -------------------------------------------------------------------------------- /WKWebViewUITests/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 | -------------------------------------------------------------------------------- /WKWebViewUITests/WKWebViewUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WKWebViewUITests.m 3 | // WKWebViewUITests 4 | // 5 | // Created by Jney on 2017/7/27. 6 | // Copyright © 2017年 Jney. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WKWebViewUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation WKWebViewUITests 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 | --------------------------------------------------------------------------------