├── _config.yml
├── .DS_Store
├── image
├── image1.png
├── image2.png
└── 微信回调Scheme.png
├── WKWebViewDemo
├── .DS_Store
├── WKWebViewDemo
│ ├── .DS_Store
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── XYWKWebView
│ │ ├── .DS_Store
│ │ ├── wk_backIcon@2x.png
│ │ ├── XYScriptMessage.m
│ │ ├── XYScriptMessage.h
│ │ ├── XYWKTool.h
│ │ ├── XYWKWebViewController.h
│ │ ├── XYWKWebView.h
│ │ ├── XYWKTool.m
│ │ ├── XYWKWebView.m
│ │ └── XYWKWebViewController.m
│ ├── UnifiedAccessViewController.swift
│ ├── LaunchScreen.storyboard
│ ├── Info.plist
│ ├── AppDelegate.swift
│ ├── ViewController.swift
│ ├── WebViewController.swift
│ ├── Main.storyboard
│ └── main.html
├── WKWebViewDemo.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
└── WKWebViewDemo-Bridging-Header.h
├── LICENSE
├── .gitignore
├── iOS App 接入H5 支付.md
└── README.md
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-hacker
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoyouPrince/XYWKWebView/HEAD/.DS_Store
--------------------------------------------------------------------------------
/image/image1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoyouPrince/XYWKWebView/HEAD/image/image1.png
--------------------------------------------------------------------------------
/image/image2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoyouPrince/XYWKWebView/HEAD/image/image2.png
--------------------------------------------------------------------------------
/image/微信回调Scheme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoyouPrince/XYWKWebView/HEAD/image/微信回调Scheme.png
--------------------------------------------------------------------------------
/WKWebViewDemo/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoyouPrince/XYWKWebView/HEAD/WKWebViewDemo/.DS_Store
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoyouPrince/XYWKWebView/HEAD/WKWebViewDemo/WKWebViewDemo/.DS_Store
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoyouPrince/XYWKWebView/HEAD/WKWebViewDemo/WKWebViewDemo/XYWKWebView/.DS_Store
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/wk_backIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoyouPrince/XYWKWebView/HEAD/WKWebViewDemo/WKWebViewDemo/XYWKWebView/wk_backIcon@2x.png
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/XYScriptMessage.m:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // XYScriptMessage.m
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/6/28.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 |
11 | #import "XYScriptMessage.h"
12 |
13 | @implementation XYScriptMessage
14 |
15 | - (NSString *)description {
16 | return [NSString stringWithFormat:@"<%@:{method:%@,params:%@,callback:%@}>", NSStringFromClass([self class]),self.method, self.params, self.callback];
17 | }
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | // 主要借鉴了 XXX,学习一下对方的封装思想。对 WKWebView 学习之后,也顺便完善了一下对应的功能和不足
6 |
7 | // 实例提供主要类
8 | // XYWebViewController -- > 控制XYWebView的UI 和 对应的 WebView 代理(WKUIDelegate,WKNavigationDelegate)
9 | // XYWebView -- > 自定义WebView.加载本地/远程URL方法
10 | // XYScriptMessage -- > JS 回调 OC 的方法
11 |
12 |
13 | #import "XYWKWebViewController.h"
14 | #import "XYWKWebView.h"
15 | #import "XYScriptMessage.h"
16 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/XYScriptMessage.h:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // XYScriptMessage.h
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/6/28.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 |
11 | #import
12 |
13 | /**
14 | * WKWebView与JS调用时参数规范实体
15 | */
16 | @interface XYScriptMessage : NSObject
17 |
18 | /**
19 | * 方法名
20 | * 用来确定Native App的执行逻辑
21 | */
22 | @property (nonatomic, copy) NSString *method;
23 |
24 | /**
25 | * 方法参数
26 | * json字符串
27 | */
28 | @property (nonatomic, copy) NSDictionary *params;
29 |
30 | /**
31 | * 回调函数名
32 | * Native App执行完后回调的JS方法名
33 | */
34 | @property (nonatomic, copy) NSString *callback;
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/UnifiedAccessViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnifiedAccessViewController.swift
3 | // WKWebViewDemo
4 | //
5 | // Created by 渠晓友 on 2020/3/16.
6 | // Copyright © 2020 xiaoyou. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class UnifiedAccessViewController: XYWKWebViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 |
20 | /*
21 | // MARK: - Navigation
22 |
23 | // In a storyboard-based application, you will often want to do a little preparation before navigation
24 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
25 | // Get the new view controller using segue.destination.
26 | // Pass the selected object to the new view controller.
27 | }
28 | */
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/XYWKTool.h:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // XYWKTool.h
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/7/3.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 | // 可扩展工具类
11 |
12 | #import
13 | #import
14 |
15 | @interface NSDictionary (JSON)
16 | - (NSString *)jsonString;
17 | @end
18 | @interface XYWKTool : NSObject
19 |
20 | /// 调转到App Store
21 | + (void)jumpToAppStoreFromVc:(UIViewController *)fromVc withUrl:(NSURL *)url;
22 | + (void)jumpToAppStoreFromVc:(UIViewController *)fromVc withAppID:(NSString *)appID;
23 |
24 | /// 打开对应App的URL
25 | + (void)openURLFromVc:(UIViewController *)fromVc withUrl:(NSURL *)url;
26 |
27 | /// 选择图片相关
28 | + (void)chooseImageFromVC:(UIViewController *)fromVc sourceType:(UIImagePickerControllerSourceType)type callBackMethod:(NSString *)callback;
29 | + (void)removeTempImages;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 渠晓友
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | # CocoaPods
32 | #
33 | # We recommend against adding the Pods directory to your .gitignore. However
34 | # you should judge for yourself, the pros and cons are mentioned at:
35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
36 | #
37 | # Pods/
38 |
39 | # Carthage
40 | #
41 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
42 | # Carthage/Checkouts
43 |
44 | Carthage/Build
45 |
46 | # fastlane
47 | #
48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
49 | # screenshots whenever they are needed.
50 | # For more information about the recommended setup visit:
51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
52 |
53 | fastlane/report.xml
54 | fastlane/Preview.html
55 | fastlane/screenshots/**/*.png
56 | fastlane/test_output
57 |
58 | # Code Injection
59 | #
60 | # After new code Injection tools there's a generated folder /iOSInjectionProject
61 | # https://github.com/johnno1962/injectionforxcode
62 |
63 | iOSInjectionProject/
64 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/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 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/XYWKWebViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // XYWKWebViewController.h
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/6/28.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 |
11 | #import
12 | #import "XYWKWebView.h"
13 | #import "XYScriptMessage.h"
14 |
15 | @interface XYWKWebViewController : UIViewController
16 |
17 | @property (nonatomic, strong) XYWKWebView *webView;
18 | @property (nonatomic, copy) NSString *url;
19 | /**
20 | * JS & App 协议的交互名称
21 | * 用于子类化自由设置,默认 @“webViewApp”
22 | */
23 | @property (nonatomic, copy) NSString * webViewAppName;
24 |
25 | #pragma mark -- navigation
26 |
27 | /**
28 | * 加载时是否显示HUD提示层(默认YES)
29 | */
30 | @property (nonatomic, assign) BOOL showHudWhenLoading;
31 |
32 | /**
33 | * 是否显示加载进度 (默认YES)
34 | */
35 | @property (nonatomic, assign) BOOL shouldShowProgress;
36 |
37 | /**
38 | * 是否使用WebPage的title作为导航栏title(默认YES)
39 | */
40 | @property (nonatomic, assign) BOOL isUseWebPageTitle;
41 |
42 | #pragma mark --
43 |
44 | /**
45 | * 是否允许WebView内部的侧滑返回(默认YES)
46 | */
47 | @property (nonatomic, assign) BOOL alwaysAllowSideBackGesture;
48 |
49 | /**
50 | * 是否支持滚动(默认YES)
51 | */
52 | @property (nonatomic, assign) BOOL scrollEnabled;
53 |
54 | /**
55 | * 是否使用web页面导航栏(默认NO)
56 | */
57 | @property (nonatomic, assign) BOOL useWebNavigationBar;
58 |
59 | #pragma mark - 微信 & 支付宝 H5支付
60 |
61 | /**
62 | * 微信H5支付的 Referer -- 即完成回跳 App 的 Scheme
63 | * @note 这个参数必须为申请微信支付的”授权安全域名“
64 | * @note 在 Info.plist 中 @b 必须 设置相同的 App 回调 URL Scheme
65 | */
66 | @property (nonatomic, copy) NSString * wx_Referer;
67 |
68 | /**
69 | * 支付宝H5支付的 AppUrlScheme -- 即完成回跳 App 的 Scheme
70 | * @note 在 Info.plist 中 @b 必须 设置相同的 App 回调URL Scheme
71 | */
72 | @property (nonatomic, copy) NSString * zfb_AppUrlScheme;
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | NSAppTransportSecurity
24 |
25 | NSAllowsArbitraryLoads
26 |
27 |
28 | NSLocationWhenInUseUsageDescription
29 | 申请使用时候,使用您位置
30 | NSPhotoLibraryUsageDescription
31 | 访问一下相册
32 | UILaunchStoryboardName
33 | LaunchScreen
34 | UIMainStoryboardFile
35 | Main
36 | UIRequiredDeviceCapabilities
37 |
38 | armv7
39 |
40 | UISupportedInterfaceOrientations
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | UISupportedInterfaceOrientations~ipad
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationPortraitUpsideDown
50 | UIInterfaceOrientationLandscapeLeft
51 | UIInterfaceOrientationLandscapeRight
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // AppDelegate.swift
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/6/26.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 |
11 | import UIKit
12 |
13 | @UIApplicationMain
14 | class AppDelegate: UIResponder, UIApplicationDelegate {
15 |
16 | var window: UIWindow?
17 |
18 |
19 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
20 | // Override point for customization after application launch.
21 | return true
22 | }
23 |
24 | func applicationWillResignActive(_ application: UIApplication) {
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 | func applicationDidEnterBackground(_ application: UIApplication) {
30 | // 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.
31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
32 | }
33 |
34 | func applicationWillEnterForeground(_ application: UIApplication) {
35 | // 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.
36 | }
37 |
38 | func applicationDidBecomeActive(_ application: UIApplication) {
39 | // 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.
40 | }
41 |
42 | func applicationWillTerminate(_ application: UIApplication) {
43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
44 | }
45 |
46 |
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/XYWKWebView.h:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // XYWKWebView.h
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/6/28.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 |
11 | #define XYWKScreenW [UIScreen mainScreen].bounds.size.width
12 | #define XYWKScreenH [UIScreen mainScreen].bounds.size.height
13 | #define XYWKiPhoneX (XYWKScreenH >= 812) // iPhone X height
14 | #define XYWKNavHeight (XYWKiPhoneX ? (88.f) : (64.f)) // statusBarH + TopBarH
15 |
16 | #ifdef DEBUG
17 | #define XYWKLog(...) NSLog( @"< %s:(第%d行) > %@",__func__ , __LINE__, [NSString stringWithFormat:__VA_ARGS__] )
18 | #define XYWKFunc DLog(@"");
19 | #else
20 | #define XYWKLog( s, ... )
21 | #define XYWKFunc;
22 | #endif
23 |
24 | #import
25 |
26 | @class XYWKWebView;
27 | @class XYScriptMessage;
28 |
29 | @protocol XYWKWebViewMessageHandleDelegate
30 |
31 | @optional
32 | - (void)xy_webView:(nonnull XYWKWebView *)webView didReceiveScriptMessage:(nonnull XYScriptMessage *)message;
33 |
34 | @end
35 |
36 | @interface XYWKWebView : WKWebView
37 |
38 | //webview加载的url地址
39 | @property (nullable, nonatomic, copy) NSString *webViewRequestUrl;
40 | //webview加载的参数
41 | @property (nullable, nonatomic, copy) NSDictionary *webViewRequestParams;
42 |
43 | @property (nullable, nonatomic, weak) id xy_messageHandlerDelegate;
44 |
45 | #pragma mark - Load Url
46 |
47 | - (void)loadRequestWithRelativeUrl:(nonnull NSString *)relativeUrl;
48 |
49 | - (void)loadRequestWithRelativeUrl:(nonnull NSString *)relativeUrl params:(nullable NSDictionary *)params;
50 |
51 | /**
52 | * 加载本地HTML页面
53 | *
54 | * @param htmlName html页面文件名称
55 | */
56 | - (void)loadLocalHTMLWithFileName:(nonnull NSString *)htmlName;
57 |
58 |
59 | /**
60 | 定制化内容,底部添加分享和赞的功能
61 |
62 | @param footerJS 底部的功能部分JS代码
63 | */
64 | - (void)loadLocalHTML:(nonnull NSString *)htmlName withAddingStyleJS:(nullable NSString *)styleJS funcJS:(nullable NSString *)funcJS FooterJS:(nullable NSString *)footerJS;
65 |
66 | #pragma mark - View Method
67 |
68 | /**
69 | * 重新加载webview
70 | */
71 | - (void)reloadWebView;
72 |
73 | #pragma mark - JS Method Invoke
74 |
75 | /**
76 | * 调用JS方法(无返回值)
77 | *
78 | * @param jsMethod JS方法名称
79 | */
80 | - (void)callJS:(nonnull NSString *)jsMethod;
81 |
82 | /**
83 | * 调用JS方法(可处理返回值)
84 | *
85 | * @param jsMethod JS方法名称
86 | * @param handler 回调block
87 | */
88 | - (void)callJS:(nonnull NSString *)jsMethod handler:(nullable void(^)(__nullable id response))handler;
89 |
90 | @end
91 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // ViewController.swift
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/6/26.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 |
11 | import UIKit
12 | import WebKit
13 |
14 | class ViewController: UIViewController , WKUIDelegate{
15 |
16 | // var webView: WKWebView!
17 | // lazy var link : CADisplayLink! = { [weak self] in
18 | // let link = CADisplayLink(target: self ?? UIView(), selector: #selector(loadProgerss))
19 | // return link
20 | // }()
21 | // lazy var progressView : UIProgressView! = { [weak self] in
22 | // let progressView = UIProgressView(progressViewStyle: .default)
23 | // progressView.frame = CGRect(x: 0, y: 100, width: (self?.view.frame.size.width)!, height: 5)
24 | // return progressView
25 | // }()
26 | //
27 | //
28 | // override func loadView() {
29 | // let webConfiguration = WKWebViewConfiguration()
30 | // webView = WKWebView(frame: .zero, configuration: webConfiguration)
31 | // webView.uiDelegate = self
32 | // view = webView
33 | // }
34 | //
35 | // override func viewDidLoad() {
36 | // super.viewDidLoad()
37 | //
38 | // let myURL = URL(string:"https://www.apple.com")
39 | // let myRequest = URLRequest(url: myURL!)
40 | // webView.load(myRequest)
41 | // link.add(to: RunLoop.current, forMode: .commonModes)
42 | //
43 | // webView.allowsBackForwardNavigationGestures = true
44 | //
45 | // // 监听
46 | // webView.addObserver(self, forKeyPath: "isLoading", options: .old, context: nil)
47 | // }
48 |
49 |
50 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
51 |
52 |
53 | // let config = WKWebViewConfiguration()
54 | // let webView = WKWebView(frame: CGRect(x: 0, y: 84, width: UIScreen.main.bounds.size.width, height: 300), configuration:config)
55 | // self.view.addSubview(webView)
56 | //
57 | // let path = Bundle.main.path(forResource: "main", ofType: "html")
58 | // let url = URL(string: path!)
59 | //
60 | // do {
61 | // let str = try String(contentsOfFile: path!, encoding: String.Encoding.utf8)
62 | // print("content is: \(str)")
63 | // webView.loadHTMLString(str, baseURL: nil)
64 | // }
65 | // catch {
66 | // print("file read failed!")
67 | // }
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | }
76 | @IBAction func localHTMLtest(_ sender: Any) {
77 | let webVC = WebViewController()
78 | navigationController?.pushViewController(webVC, animated: true)
79 | }
80 | @IBAction func unifiyTest(_ sender: Any) {
81 | let webVC = UnifiedAccessViewController()
82 | webVC.url = "http://39.107.94.38:8005/h5/#/?code=9940A63EC6DC1F9685FD54955DF51C0DA39F7C0FFCD21C0C47046FCF92ADBEAF193A7B7E12EF6FE0FB8214BAE565D90B67623E9FD8C68FE73E8FE0BB1CEF02F765710F5911633F10CAC5BB3929B5598974C66C54ADE386A30957E6515E1582A6";
83 | navigationController?.pushViewController(webVC, animated: true)
84 | }
85 | }
86 |
87 | extension ViewController{
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | // @objc func loadProgerss(){
97 | // print(webView.estimatedProgress)
98 | // self.view.addSubview(progressView)
99 | // self.progressView.progress = Float(webView.estimatedProgress)
100 | //
101 | // if webView.estimatedProgress == 1.0 {
102 | // link.remove(from: RunLoop.current, forMode: .commonModes)
103 | // progressView.removeFromSuperview()
104 | // }
105 | // }
106 | //
107 | // override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
108 | // print("keypath = \(String(describing: keyPath))")
109 | // print("object = \(String(describing: object))")
110 | // print("change = \(String(describing: change))")
111 | // print("context = \(String(describing: context))")
112 | //
113 | // print("webView.isLoading = \(webView.isLoading)")
114 | //
115 | //
116 | // }
117 | }
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/WebViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // WebViewController.swift
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/6/28.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 |
11 | import UIKit
12 |
13 | class WebViewController: XYWKWebViewController {
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 |
18 | /// #用法0: 直接加载对应的地址 <没有参数>
19 | // self.webView.loadRequest(withRelativeUrl: "https://www.httpbin.org/")
20 |
21 | /// #用法1: 直接加载对应的地址 <有参数>
22 | // let params = ["name":"xiaoyou",
23 | // "password" : "123456#/HTTP_Methods/get_get"]
24 | // self.webView.loadRequest(withRelativeUrl: "https://www.httpbin.org/", params: params)
25 |
26 | /// #用法2: 直接加载本地HTML文件 <没有参数>
27 | self.webView.loadLocalHTML(withFileName: "main")
28 |
29 | /// #用法3: JS 注入,添加一些方法 <这里的原生坐标和JS之间无法直接相对应>
30 | let margin : CGFloat = 6.0
31 | let padding : CGFloat = 10.0
32 | let width = UIScreen.main.bounds.size.width - (margin * 2.0) - (margin * 7.0 + padding)
33 | let btnWidth = (width - padding - 5) / 2.0
34 |
35 | let styleJS = """
36 |
61 | """
62 |
63 | let funcJS = """
64 | \t\t\tfunction testFunc(text){\n
65 | \t\t\t\tvar message = \"点我干什么\";\n
66 | \t\t\t\twindow.webkit.messageHandlers.webViewApp.postMessage(message);\n
67 | \t\t\t\talert(text);\n
68 | \t\t\t}\n
69 | """
70 |
71 | let footerJS = """
72 | \t
\n
73 | \t
77 | """
78 | self.webView.loadLocalHTML("main", withAddingStyleJS: styleJS, funcJS: funcJS, footerJS: footerJS)
79 | // self.webView.backgroundColor = UIColor.red
80 |
81 | /// 设置导航
82 | self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "返回", style: .plain, target: self, action: #selector(backAction));
83 | self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "调用JS", style: .plain, target: self, action: #selector(callJS));
84 | }
85 |
86 |
87 |
88 |
89 |
90 | }
91 |
92 |
93 | /// #用法4: OC 调用JS方法。这里可以调用JS,把H5需要的参数传给他们
94 | /// 这里是JS 回调方法
95 | extension WebViewController{
96 |
97 | @objc func backAction() {
98 | navigationController?.popViewController(animated: true)
99 | }
100 |
101 | @objc func callJS() {
102 | self.webView.callJS("call('Hello World!')") { (response) in
103 | print("\(String(describing: response))")
104 | }
105 | }
106 |
107 | /// 这里是重写了WebView接受到JS消息的回调,需要调用super方法才能执行内部方法,否则这里只是打印
108 | override func xy_webView(_ webView: XYWKWebView, didReceive message: XYScriptMessage) {
109 |
110 | // 如果完全自定义的js方法处理,无需重写父类,自行实现即可
111 | super.xy_webView(webView, didReceive: message)
112 | print(message)
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
29 |
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 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/iOS App 接入H5 支付.md:
--------------------------------------------------------------------------------
1 | # iOS App 接入 H5 支付
2 |
3 | **「H5 支付」**是在手机浏览器中购买商品,发起支付的一种应用场景。
4 |
5 | 微信官方不建议在 App 内接入 H5 支付,但实际 App 开发中会有接入 Web 商城的实际需求。
6 |
7 | 本文档即是对 iOS App 中的 H5 支付的统一封装,整体基于 WKWebView,关于WKWebView的封装和使用可以看这篇文章[WKWebView 的封装和使用](README.md)
8 |
9 | 下面先说使用方法,然后再说实现思路
10 |
11 | ### 使用方法
12 |
13 | **1.设置微信/支付宝支付完成回调 App 的 URL Scheme**
14 |
15 | - 支付宝客户端H5支付回跳 App 的 Scheme 可以自定义,
16 | - 微信客户端H5支付回跳 App 的 Scheme **必须为微信商户注册的微信支付安全域名**,此处问产品经理
17 |
18 | **2.生成实例对象,设置对应的 wx_Referer 和 zfb_AppUrlScheme**
19 |
20 | - wx_Referer 即微信支付的回调 Scheme
21 | - zfb_AppUrlScheme 即支付宝支付回调的 Scheme
22 |
23 | 工具基类名:XYWKWebViewController ,建议使用子类继承,这样不同的业务模块可以互不影响,代码示例如下
24 |
25 | ```
26 | // 直接赋值对应的回调Scheme即可。
27 | XYWKWebViewController * vc = [XYWKWebViewController new];
28 | vc.wx_Referer = @"wxser.fesco.com.cn";
29 | vc.zfb_AppUrlScheme = @"testmobilepay";
30 | [self.navigationController pushViewController:vc];
31 | ```
32 |
33 |
34 | **H5 支付核心思路**
35 |
36 | 1. 网页内 H5 调起三方支付,发起统一下单接口。【此步骤为H5开发】
37 | 2. 统一支付返回值中会返回调起支付的中间页面,商户后台会发到支付平台。【此中间页面地址需要我们客户端自己处理,这样才可以在支付完成/取消之后回调到App页面】
38 | 3. 中间页面进行 H5 校验,成功后发起支付链接。【此处监听到支付链接需要我们客户端调起对应的支付App】
39 |
40 | **注意:H5 页面调起三方支付,必须设置回到 App 的 URL scheme,否则回不到自己App**
41 |
42 | 下面是调试时候我这边检测到的支付调用
43 |
44 | ```
45 | 输入密码后确定支付: 其中每个地址都经过了urlencode处理
46 |
47 | // 1.统一下单接口调用,其中域名为商户申请微信支付的安全域名。
48 | http://wxser.xxxxx.com.cn/pay/index?app=3&sign=BD226DB5ADDC7DE913FAEB83D7A45271&orderno=TF202001170000798372&money=16900&description=%E5%BE%AE%E5%95%86%E5%9F%8E&tencentPayType=3&backurl=https%3A%2F%2Fwmall.fesco.com.cn%2Fpageview%2Fhtml%2FPersonal%2F%E6%94%AF%E4%BB%98%E6%88%90%E5%8A%9F.html
49 |
50 | // 2.统一下单接口返回的微信支付中间页地址,由商户后台调用,发起微信支付
51 | https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx17101746455190817ba211ed1068551700&package=2480363457&redirect_url=https%3a%2f%2fwxser.fesco.com.cn%2fpay%2fH5PayBack
52 |
53 | // 3.中间页通过验证,调起微信支付
54 | weixin://wap/pay?prepayid%3Dwx17101746455190817ba211ed1068551700&package=2480363457&noncestr=1579227520&sign=31577677cb607b31def4781fa2d8be0d
55 |
56 | // 支付宝处理
57 | // 1. 统一支付接口链接
58 | https://www.alipay.com/cooperate/gateway.do?service=alipay.wap.create.direct.pay.by.user&partner=2088011635010164&_input_charset=UTF-8&seller_email=DZSW%40fesco.com.cn&out_trade_no=TF202001190000380271&subject=%e7%a6%8f%e5%88%a9%e5%95%86%e5%93%81%e5%85%91%e6%8d%a2&body=%e7%a6%8f%e5%88%a9%e5%95%86%e5%93%81%e5%85%91%e6%8d%a2&total_fee=0.01&payment_type=1&app_pay=Y&return_url=http%3a%2f%2ffesco3.datayan.cn%2fOrder%2fwxAlipayHsh_New_Return¬ify_url=http%3a%2f%2ffesco3.datayan.cn%2fOrder%2fAlipayHsh_New_Notify&sign=4b55fa10da04831fca8936b272645b57&sign_type=MD5
59 |
60 | // 2. 支付宝反馈的支付中间页面
61 | https://mclient.alipay.com/home/exterfaceAssign.htm?seller_email=DZSW%40fesco.com.cn&_input_charset=UTF-8&subject=%E7%A6%8F%E5%88%A9%E5%95%86%E5%93%81%E5%85%91%E6%8D%A2&sign=4b55fa10da04831fca8936b272645b57&body=%E7%A6%8F%E5%88%A9%E5%95%86%E5%93%81%E5%85%91%E6%8D%A2¬ify_url=http%3A%2F%2Ffesco3.datayan.cn%2FOrder%2FAlipayHsh_New_Notify&alipay_exterface_invoke_assign_model=cashier&alipay_exterface_invoke_assign_target=mapi_direct_trade.htm&payment_type=1&out_trade_no=TF202001190000380271&partner=2088011635010164&alipay_exterface_invoke_assign_sign=_oe_srjnatso%2B_y8%2B_i6_sum_me_b_e_o_c_jgej_kd_w_gn_ivd_qds%2Bya36_p_g_z4_z_c_s_qg%3D%3D&service=alipay.wap.create.direct.pay.by.user&total_fee=0.01&app_pay=Y&return_url=http%3A%2F%2Ffesco3.datayan.cn%2FOrder%2FwxAlipayHsh_New_Return&sign_type=MD5&alipay_exterface_invoke_assign_client_ip=219.239.42.66
62 |
63 | // 3. 调起支付宝支付的最终接口
64 | alipay://alipayclient/?%7B%22requestType%22%3A%22SafePay%22%2C%22fromAppUrlScheme%22%3A%22alipays%22%2C%22dataString%22%3A%22h5_route_token%3D%5C%22RZ110bgnfPGrMUV7a2yVXp8lR31YgImobilecashierRZ11%5C%22%26is_h5_route%3D%5C%22true%5C%22%22%7D
65 | ```
66 |
67 |
68 | ### 微信H5支付流程和注意点
69 |
70 | 流程直接看文档[官方文档](https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1)
71 |
72 | iOS 端注意点主要:
73 |
74 | #### 1. Referer 和 redirect_url 说明
75 |
76 | `HTTP Referer` 是 header 的一部分,当浏览器向web服务器发起请求的时,一般会带上 Referer,告诉服务器我是从哪个页面链接过来。微信中间页会对 Referer 进行校验,非安全域名将不能正常加载。
77 |
78 | `redirect_url` 是微信中间页唤起微信支付之后,页面重定向的地址。中间页唤起微信支付后会跳转到指定的 redirect_url。并且微信APP在支付完成时,也是通过 redirect_url 回调结果,redirect_url一般是一个页面地址,所以微信支付完成会打开 Safari 浏览器。本文通过修改 redirect_url,实现微信支付完毕跳回当前APP。
79 |
80 | > **注意:
81 | > 微信会校验 Referer(来源) 和 redirect_url(目标) 是否是安全域名。如果不传redirect_url,微信会将 Referer 当成 redirect_url,唤起支付之后会重定向到 Referer 对应的页面,建议带上 redirect_url
82 | 1.需对redirect_url进行urlencode处理
83 | 2.由于设置redirect_url后,回跳指定页面的操作可能发生在:1,微信支付中间页调起微信收银台后超过5秒 2,用户点击“取消支付“或支付完成后点“完成”按钮。因此无法保证页面回跳时,支付流程已结束,所以商户设置的redirect_url地址不能自动执行查单操作,应让用户去点击按钮触发查单操作**
84 |
85 |
86 |
87 | #### 2. 必须设置微信支付完成回跳 App 的 URL Scheme
88 |
89 | 
90 |
91 | ### 微信H5支付流程封装
92 |
93 | 弄清楚了微信支付流程,那封装思路就清晰了,这里只讲思路和接口,具体实现请参考[项目地址](https://www.github.com/xiaoyouPrince/WKWebViewDemo)
94 |
95 | #### 1. wx_Referer 入参设置
96 |
97 | ```
98 | /**
99 | * 微信H5支付的 Referer -- 即完成回跳 App 的 Scheme
100 | * @note 这个参数必须为申请微信支付的”授权安全域名“
101 | * @note 在 Info.plist 中 @b 必须 设置相同的 App 回调 URL Scheme
102 | */
103 | @property (nonatomic, copy) NSString * wx_Referer;
104 | ```
105 | #### 2. wx_redirect_url 源文件内部变量,存放拉起微信H5支付的回调地址
106 |
107 | ```
108 | /**
109 | * 微信H5支付的重定向地址
110 | */
111 | @property (nonatomic, copy) NSString * wx_redirect_url;
112 | ```
113 |
114 | #### 3. 源文件实现逻辑(详见项目源码)
115 |
116 | ```
117 | 1. 重写 - webView: decidePolicyForNavigationAction: decisionHandler: 方法,处理每次请求
118 | 2. 处理中间页面地址【Scheme+域名为”https://wx.tenpay.com“】,查看是否包含重定向地址 `redirect_url`参数,
119 | 3. 如果后台没有配置,则手动配置为 self.wx_Referer 如 abc.com:// 停止当前请求并发起新地址的请求
120 | 4. 如果已经配置且不等于 self.wx_Referer 则设置为 self.wx_Referer 如 abc.com:// 并用`wx_redirect_url` 保存原来的重定向地址,停止当前请求并发起新地址的请求
121 | 5. 如果已经配置且重定向地址为 self.wx_Referer 则直接通过本次请求不做处理
122 | ```
123 |
124 | 详细代码请看项目源码,里面混合了支付宝与微信的H5支付逻辑,有兴趣可以自行查阅。
125 |
126 | ### 支付宝H5支付流程和注意点
127 |
128 | 支付宝的支付逻辑就相对简单了,支付宝调起中间页校验成功之后会拉起支付宝,在拉起支付宝客户端我们对该地址进行处理,将我们的 App Scheme 替换给该地址内部的 `fromAppUrlScheme` 参数即可,支付宝客户端即可在支付完成/取消之后回调到我们的 App
129 |
130 | #### 1. zfb_AppUrlScheme 入参设置
131 |
132 | ```
133 | /**
134 | * 支付宝H5支付的 AppUrlScheme -- 即完成回跳 App 的 Scheme
135 | * @note 在 Info.plist 中 @b 必须 设置相同的 App 回调URL Scheme
136 | */
137 | @property (nonatomic, copy) NSString * zfb_AppUrlScheme;
138 | ```
139 |
140 | #### 2. 实现文件处理调起支付宝客户端
141 |
142 | ```
143 | 1. 重写 - webView: decidePolicyForNavigationAction: decisionHandler: 方法,处理每次请求
144 | 2. 处理拉起支付宝客户端的地址,标志为 URL Scheme 为 alipay
145 | 3. 将请求URL内的 fromAppUrlScheme 参数替换为我们自己 App Scheme。取消当前请求并发起新地址的请求
146 | ```
147 |
148 | ## 最后
149 |
150 | 我封装的好的项目地址,可以直接使用[项目地址](https://github.com/xiaoyouPrince/WKWebViewDemo)
151 |
152 | 如果此项目帮助到了你,欢迎点赞!
153 |
154 | 最后祝大家玩的愉快~
155 |
156 | 参考文章:
157 | [微信H5支付](https://www.jianshu.com/p/65979e8bf251)
158 | [支付宝H5支付](https://www.jianshu.com/p/72e867a7e40e)
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/XYWKTool.m:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // XYWKTool.m
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/7/3.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 | // 抽取一个可扩展工具类
11 |
12 | #import "XYWKTool.h"
13 | #import
14 | #import "XYWKWebViewController.h"
15 |
16 | @implementation NSDictionary (JSON)
17 | - (NSString *)jsonString
18 | {
19 | NSMutableString *strM = [[NSMutableString alloc] initWithString:@"{\n"];
20 | for (NSString *key in self.keyEnumerator) {
21 | [strM appendFormat:@"%@: \"%@\",\n",key,self[key]];
22 | }
23 | [strM appendString:@"}"];
24 | return strM;
25 | }
26 |
27 | @end
28 |
29 | @interface XYWKTool()
30 | /** 本地临时图片地址数组 */
31 | @property (nonatomic, strong) NSMutableArray * tempImagePathArray;
32 |
33 | @end
34 | @implementation XYWKTool
35 | static __weak XYWKWebViewController * _webVC;
36 | static NSString * _webViewCallBackMethod;
37 | static XYWKTool *_tool;
38 |
39 | - (instancetype)init
40 | {
41 | self = [super init];
42 | if (self) {
43 | self.tempImagePathArray = @[].mutableCopy;
44 | }
45 | return self;
46 | }
47 |
48 | + (void)jumpToAppStoreFromVc:(UIViewController *)fromVc withUrl:(NSURL *)url
49 | {
50 | // 通常App Store的scheme形式为 itms-appss://itunes.apple.com/cn/app/id382201985?mt=8
51 | // 取出appID
52 | NSString *urlLastPathComponent = url.lastPathComponent;
53 | NSString *idStr = [[urlLastPathComponent componentsSeparatedByString:@"?"] firstObject];
54 | NSString *appID = [idStr substringFromIndex:2];
55 |
56 | [self jumpToAppStoreFromVc:fromVc withAppID:appID];
57 | }
58 |
59 | /**
60 | 应用内跳转到App Store页
61 | */
62 | + (void)jumpToAppStoreFromVc:(UIViewController *)fromVc withAppID:(NSString *)appID
63 | {
64 |
65 | // 直接禁用之前页面,并不可重复点击
66 | if (!fromVc.view.isUserInteractionEnabled) return;
67 | [fromVc.view setUserInteractionEnabled:NO];
68 |
69 |
70 | // 创建对象
71 | SKStoreProductViewController *storeVC = [[SKStoreProductViewController alloc] init];
72 | // 设置代理
73 | _tool = _tool ?: [self new];
74 | storeVC.delegate = _tool;
75 | // 初始化参数
76 | NSDictionary *dict = [NSDictionary dictionaryWithObject:appID forKey:SKStoreProductParameterITunesItemIdentifier];
77 |
78 | // 跳转App Store页
79 | [storeVC loadProductWithParameters:dict completionBlock:^(BOOL result, NSError * _Nullable error) {
80 | if (error) {
81 | NSLog(@"错误信息:%@",error.userInfo);
82 | }
83 | else
84 | {
85 | // 弹出模态视图
86 | [fromVc presentViewController:storeVC animated:YES completion:^{
87 | [fromVc.view setUserInteractionEnabled:YES];
88 | }];
89 | }
90 | }];
91 | }
92 |
93 |
94 | #pragma mark -- SKStoreProductViewControllerDelegate
95 | /**
96 | SKStoreProductViewControllerDelegate 方法,选择完成之后的处理
97 | @param viewController SKStoreProductViewController
98 | */
99 | - (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController
100 | {
101 | NSLog(@"将要退出 App Store 页面了");
102 | [viewController dismissViewControllerAnimated:YES completion:^{
103 | NSLog(@"已经退出 App Store 页面完成了");
104 | }];
105 | }
106 |
107 |
108 | + (void)openURLFromVc:(UIViewController *)fromVc withUrl:(NSURL *)url
109 | {
110 | // 处理是否是去AppStore
111 | // 这里进行重定向了,例如 网页内下载APP 链接,起初是https://地址。重定向之后itms-appss:// 这里需要重新让WebView加载一下
112 | NSString *redirectionUrlScheme = url.scheme;
113 | if ([redirectionUrlScheme isEqualToString:@"itms-appss"]) {
114 | [XYWKTool jumpToAppStoreFromVc:fromVc withUrl:url];
115 | return;
116 | }
117 |
118 | // 处理手机上内部App
119 | BOOL success = [[UIApplication sharedApplication] canOpenURL:url];
120 | if (success) {
121 | // 打开App
122 | if (__builtin_available(iOS 10.0, *)) {
123 | [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
124 | } else {
125 | [[UIApplication sharedApplication] openURL:url];
126 | }
127 | }else
128 | {
129 | // 设置弹窗
130 | NSString *string = [NSString stringWithFormat:@"无法打开%@,因为 iOS 无法识别以\"%@\"开头的互联网地址",url,url.scheme];
131 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"温馨提示" message:string preferredStyle:UIAlertControllerStyleAlert];
132 | // 确定按键不带点击事件
133 | [alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
134 | [fromVc presentViewController:alertController animated:YES completion:nil];
135 | }
136 | }
137 |
138 | #pragma mark - 相册相关
139 |
140 |
141 | + (void)chooseImageFromVC:(UIViewController *)fromVc sourceType:(UIImagePickerControllerSourceType)type callBackMethod:(NSString *)callback
142 | {
143 | if ([fromVc isKindOfClass:XYWKWebViewController.class]) {
144 | _webVC = (XYWKWebViewController *)fromVc;
145 | _webViewCallBackMethod = callback;
146 | }
147 |
148 | // 进行弹出相册
149 | UIImagePickerController *vc = [[UIImagePickerController alloc] init];
150 | _tool = _tool ?: [self new];
151 | vc.delegate = _tool;
152 | vc.sourceType = type;
153 |
154 | [_webVC presentViewController:vc animated:YES completion:nil];
155 |
156 | }
157 |
158 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
159 |
160 | UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
161 |
162 | NSInteger randNUM = arc4random()%100;
163 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
164 | NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"%ld.png",(randNUM)]]; // 保存
165 | [self.tempImagePathArray addObject:filePath];
166 | [UIImagePNGRepresentation(image) writeToFile: filePath atomically:YES];
167 |
168 | NSDictionary *dict = @{
169 | @"localOriginalUri": filePath,
170 | @"localCompressedUri": filePath,
171 | };
172 |
173 | [picker dismissViewControllerAnimated:YES completion:^{
174 | [_webVC.webView callJS:[NSString stringWithFormat:@"%@(%@)", _webViewCallBackMethod,dict.jsonString]];
175 | }];
176 | }
177 |
178 |
179 |
180 | + (void)removeTempImages
181 | {
182 | if (_tool.tempImagePathArray.count) {
183 | for (NSString *path in _tool.tempImagePathArray) {
184 | if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
185 | [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
186 | }
187 | }
188 | [_tool.tempImagePathArray removeAllObjects];
189 | }
190 | }
191 |
192 |
193 | #pragma mark - 地理位置相关
194 |
195 | @end
196 |
197 |
198 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/main.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Webview页面标题
16 |
21 |
22 |
150 |
151 |
152 | Hello,World!
153 |
154 |
155 |
156 |
157 |
158 |
159 | 测试新页面打开 W3School
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/WKWebViewDemo/WKWebViewDemo/XYWKWebView/XYWKWebView.m:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // XYWKWebView.m
4 | // WKWebViewDemo
5 | //
6 | // Created by 渠晓友 on 2018/6/28.
7 | //
8 | // Copyright © 2018年 xiaoyouPrince. All rights reserved.
9 | //
10 |
11 | #import "XYWKWebView.h"
12 | #import "XYScriptMessage.h"
13 |
14 | //这里可以统一设置WebView的访问域名,方便切换
15 | #ifdef DEBUG
16 | # define BASE_URL_API @"http://****/" //测试环境
17 | #else
18 | # define BASE_URL_API @"http://****/" //正式环境
19 | #endif
20 |
21 | @interface XYWKWebView ()
22 |
23 | @property (nonatomic, strong) NSURL *baseUrl;
24 |
25 | @end
26 |
27 | @implementation XYWKWebView
28 |
29 | - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration {
30 | self = [super initWithFrame:frame configuration:configuration];
31 | if (self) {
32 |
33 | //默认允许系统自带的侧滑后退
34 | self.allowsBackForwardNavigationGestures = YES;
35 | self.baseUrl = [NSURL URLWithString:BASE_URL_API];
36 |
37 | //设置允许JS自动打开新window
38 | WKPreferences *preference = [WKPreferences new];
39 | preference.javaScriptCanOpenWindowsAutomatically = YES;
40 | }
41 |
42 | return self;
43 | }
44 |
45 | #pragma mark - Load Url
46 |
47 | - (void)loadRequestWithRelativeUrl:(NSString *)relativeUrl; {
48 |
49 |
50 | // 这里目前只支持HTTP(s)协议,如果需要跳转到App Store就需要验证对应的Scheme : itms-appss
51 | [self loadRequestWithRelativeUrl:relativeUrl params:nil];
52 | }
53 |
54 | - (void)loadRequestWithRelativeUrl:(NSString *)relativeUrl params:(NSDictionary *)params {
55 |
56 | NSURL *url = [self generateURL:relativeUrl params:params];
57 |
58 | [self loadRequest:[NSURLRequest requestWithURL:url]];
59 | }
60 |
61 | /**
62 | * 加载本地HTML页面
63 | *
64 | * @param htmlName html页面文件名称
65 | */
66 | - (void)loadLocalHTMLWithFileName:(nonnull NSString *)htmlName {
67 |
68 | NSString *path = [[NSBundle mainBundle] bundlePath];
69 | NSURL *baseURL = [NSURL fileURLWithPath:path];
70 | NSString * htmlPath = [[NSBundle mainBundle] pathForResource:htmlName
71 | ofType:@"html"];
72 | NSString * htmlCont = [NSString stringWithContentsOfFile:htmlPath
73 | encoding:NSUTF8StringEncoding
74 | error:nil];
75 |
76 | [self loadHTMLString:htmlCont baseURL:baseURL];
77 | }
78 |
79 | - (void)loadLocalHTML:(NSString *)htmlName withAddingStyleJS:(NSString *)styleJS funcJS:(NSString *)funcJS FooterJS:(NSString *)footerJS
80 | {
81 |
82 | if (footerJS == nil) {
83 | return; // 如果html为空直接返回。
84 | }
85 |
86 |
87 | // 找到对应HTML内容
88 | NSString *path = [[NSBundle mainBundle] bundlePath];
89 | NSURL *baseURL = [NSURL fileURLWithPath:path];
90 | NSString * htmlPath = [[NSBundle mainBundle] pathForResource:htmlName
91 | ofType:@"html"];
92 | NSMutableString * htmlCont = [NSMutableString stringWithContentsOfFile:htmlPath
93 | encoding:NSUTF8StringEncoding
94 | error:nil];
95 |
96 | // 插入Style代码
97 | NSRange rangeOfTitle = [htmlCont rangeOfString:@""];
98 | [htmlCont insertString:styleJS atIndex:rangeOfTitle.location + rangeOfTitle.length + 1];
99 |
100 | // 插入JS代码
101 | NSRange rangeOfScript = [htmlCont rangeOfString:@"