288 lines
9.7 KiB
Objective-C
288 lines
9.7 KiB
Objective-C
//
|
||
// KBWebViewViewController.m
|
||
// keyBoard
|
||
//
|
||
// Created by 张伟 on 2025/11/10.
|
||
//
|
||
|
||
#import "KBWebViewViewController.h"
|
||
#import <WebKit/WebKit.h>
|
||
#import "KBConfig.h"
|
||
#import "Masonry.h"
|
||
|
||
@interface KBWebViewViewController () <WKNavigationDelegate, WKScriptMessageHandler>
|
||
|
||
@property(nonatomic, strong) WKWebView * webView;
|
||
// 🟢 顶部加载进度条
|
||
@property(nonatomic, strong) UIProgressView *progressView;
|
||
@property(nonatomic, assign) BOOL observingProgress;
|
||
|
||
@end
|
||
|
||
@implementation KBWebViewViewController
|
||
|
||
+ (instancetype)legalViewControllerWithType:(KBLegalDocumentType)type {
|
||
KBWebViewViewController *vc = [[KBWebViewViewController alloc] init];
|
||
vc.pageTitle = [self kb_titleForLegalDocumentType:type];
|
||
NSString *remoteURL = [self kb_remoteURLForLegalDocumentType:type];
|
||
if (remoteURL.length > 0) {
|
||
vc.url = remoteURL;
|
||
} else {
|
||
// vc.htmlString = [self kb_htmlForLegalDocumentType:type];
|
||
}
|
||
return vc;
|
||
}
|
||
|
||
+ (void)presentLegalDocumentType:(KBLegalDocumentType)type fromViewController:(UIViewController *)viewController {
|
||
if (![viewController isKindOfClass:UIViewController.class]) { return; }
|
||
KBWebViewViewController *vc = [self legalViewControllerWithType:type];
|
||
UINavigationController *nav = viewController.navigationController;
|
||
if (nav) {
|
||
[nav pushViewController:vc animated:YES];
|
||
return;
|
||
}
|
||
[viewController presentViewController:vc animated:YES completion:nil];
|
||
}
|
||
|
||
+ (nullable NSNumber *)legalDocumentTypeNumberFromQueryValue:(NSString *)queryValue {
|
||
NSString *value = queryValue.lowercaseString ?: @"";
|
||
if ([value isEqualToString:@"privacy"]) {
|
||
return @(KBLegalDocumentTypePrivacyPolicy);
|
||
}
|
||
if ([value isEqualToString:@"membership"]) {
|
||
return @(KBLegalDocumentTypeMembershipAgreement);
|
||
}
|
||
if ([value isEqualToString:@"terms"]) {
|
||
return @(KBLegalDocumentTypeTermsOfService);
|
||
}
|
||
return nil;
|
||
}
|
||
|
||
+ (NSString *)queryValueForLegalDocumentType:(KBLegalDocumentType)type {
|
||
switch (type) {
|
||
case KBLegalDocumentTypePrivacyPolicy:
|
||
return @"privacy";
|
||
case KBLegalDocumentTypeMembershipAgreement:
|
||
return @"membership";
|
||
case KBLegalDocumentTypeTermsOfService:
|
||
default:
|
||
return @"terms";
|
||
}
|
||
}
|
||
|
||
- (void)viewWillAppear:(BOOL)animated {
|
||
[super viewWillAppear:animated];
|
||
if (self.pageTitle.length > 0) {
|
||
self.title = self.pageTitle;
|
||
}
|
||
}
|
||
|
||
- (void)viewDidLoad {
|
||
[super viewDidLoad];
|
||
self.view.backgroundColor = UIColor.whiteColor;
|
||
[self configUI];
|
||
}
|
||
|
||
- (void)configUI {
|
||
// 1. 配置 JS 交互
|
||
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
|
||
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
|
||
[userContentController addScriptMessageHandler:self name:@"keyBoard"];
|
||
config.userContentController = userContentController;
|
||
|
||
// 2. 创建 webView
|
||
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
|
||
self.webView.navigationDelegate = self;
|
||
self.webView.backgroundColor = UIColor.clearColor;
|
||
[self.view addSubview:self.webView];
|
||
[self.webView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.top.equalTo(self.kb_navView.mas_bottom);
|
||
make.left.right.bottom.equalTo(self.view);
|
||
}];
|
||
|
||
// 🟢 3. 顶部 2 像素进度条
|
||
self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
|
||
|
||
self.progressView.trackTintColor = [UIColor clearColor]; // 背景透明
|
||
self.progressView.progressTintColor = [UIColor greenColor];
|
||
|
||
self.progressView.progress = 0.0f;
|
||
self.progressView.hidden = YES; // 初始隐藏
|
||
|
||
[self.view addSubview:self.progressView];
|
||
|
||
// 约束:贴在最上面,高度 2 像素,左右撑满
|
||
[self.progressView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.top.equalTo(self.kb_navView.mas_bottom);
|
||
make.left.right.equalTo(self.view);
|
||
make.height.mas_equalTo(2.0);
|
||
}];
|
||
|
||
// 🟢 4. 监听 WKWebView 加载进度(estimatedProgress)
|
||
[self.webView addObserver:self
|
||
forKeyPath:@"estimatedProgress"
|
||
options:NSKeyValueObservingOptionNew
|
||
context:nil];
|
||
self.observingProgress = YES;
|
||
|
||
// 5. 加载内容
|
||
if (self.htmlString.length > 0) {
|
||
[self.webView loadHTMLString:self.htmlString baseURL:nil];
|
||
if (self.pageTitle.length > 0) {
|
||
self.title = self.pageTitle;
|
||
}
|
||
return;
|
||
}
|
||
|
||
NSURL *URL = [NSURL URLWithString:self.url ?: @""];
|
||
if (!URL) {
|
||
[self.webView loadHTMLString:[self.class kb_fallbackErrorHTML]
|
||
baseURL:nil];
|
||
return;
|
||
}
|
||
|
||
NSURLRequest * req = [NSURLRequest requestWithURL:URL];
|
||
[self.webView loadRequest:req];
|
||
[self.view bringSubviewToFront:self.kb_navView];
|
||
[self.view bringSubviewToFront:self.progressView];
|
||
}
|
||
|
||
#pragma mark - KVO: 监听加载进度(estimatedProgress) 🟢
|
||
|
||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||
ofObject:(id)object
|
||
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
|
||
context:(void *)context {
|
||
if (object == self.webView && [keyPath isEqualToString:@"estimatedProgress"]) {
|
||
CGFloat progress = self.webView.estimatedProgress;
|
||
|
||
// 0 ~ 1之间的进度
|
||
self.progressView.hidden = NO;
|
||
[self.progressView setProgress:progress animated:YES];
|
||
|
||
if (progress >= 1.0f) {
|
||
// 加载完成后延迟一点点再隐藏,顺滑一点
|
||
[UIView animateWithDuration:0.25
|
||
delay:0.25
|
||
options:UIViewAnimationOptionCurveEaseOut
|
||
animations:^{
|
||
self.progressView.alpha = 0.0;
|
||
} completion:^(BOOL finished) {
|
||
self.progressView.progress = 0.0;
|
||
self.progressView.hidden = YES;
|
||
self.progressView.alpha = 1.0;
|
||
}];
|
||
}
|
||
} else {
|
||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||
}
|
||
}
|
||
|
||
#pragma mark - WKNavigationDelegate
|
||
|
||
// 开始加载
|
||
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
|
||
NSLog(@"开始加载url");
|
||
// 开始加载时,确保进度条出现
|
||
self.progressView.hidden = NO;
|
||
self.progressView.progress = 0.0;
|
||
}
|
||
|
||
// 加载完成
|
||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
||
NSLog(@"页面加载成功");
|
||
if (self.pageTitle.length > 0) {
|
||
self.title = self.pageTitle;
|
||
} else if (webView.title.length > 0) {
|
||
self.title = webView.title;
|
||
}
|
||
}
|
||
|
||
// 加载失败
|
||
- (void)webView:(WKWebView *)webView
|
||
didFailProvisionalNavigation:(WKNavigation *)navigation
|
||
withError:(NSError *)error {
|
||
NSLog(@"webView load error: %@", error);
|
||
// 失败时也把进度条收一下
|
||
self.progressView.hidden = YES;
|
||
self.progressView.progress = 0.0;
|
||
}
|
||
|
||
#pragma mark - WKScriptMessageHandler (JS 调原生)
|
||
|
||
- (void)userContentController:(WKUserContentController *)userContentController
|
||
didReceiveScriptMessage:(WKScriptMessage *)message {
|
||
|
||
if (![message.body isKindOfClass:NSDictionary.class]) {
|
||
NSLog(@"Body参数不合法");
|
||
return;
|
||
}
|
||
|
||
[self handleJSMethodWithParams:message.body];
|
||
}
|
||
|
||
- (void)handleJSMethodWithParams:(NSDictionary *)params {
|
||
NSDictionary * body = [params copy];
|
||
NSLog(@"收到 JS 消息: %@", body);
|
||
NSString * type = body[@"type"];
|
||
|
||
// 测试打印
|
||
if ([type isEqual:@"ONE_METHOD"]) {
|
||
NSLog(@"come on baby js 调用你的方法了");
|
||
}
|
||
|
||
// 修改title
|
||
if ([type isEqual:@"changeTiele"]) {
|
||
NSLog(@"%@", body);
|
||
NSString * newTiele = body[@"payload"][@"data"];
|
||
self.title = newTiele;
|
||
}
|
||
}
|
||
|
||
#pragma mark - Legal Content
|
||
|
||
+ (NSString *)kb_titleForLegalDocumentType:(KBLegalDocumentType)type {
|
||
switch (type) {
|
||
case KBLegalDocumentTypePrivacyPolicy:
|
||
return KBLocalized(@"Privacy Policy");
|
||
case KBLegalDocumentTypeMembershipAgreement:
|
||
return KBLocalized(@"Membership Agreement");
|
||
case KBLegalDocumentTypeTermsOfService:
|
||
default:
|
||
return KBLocalized(@"Agreement");
|
||
}
|
||
}
|
||
|
||
+ (NSString *)kb_remoteURLForLegalDocumentType:(KBLegalDocumentType)type {
|
||
switch (type) {
|
||
case KBLegalDocumentTypePrivacyPolicy:
|
||
return KB_PRIVACY_POLICY_URL;
|
||
case KBLegalDocumentTypeMembershipAgreement:
|
||
return KB_MEMBERSHIP_AGREEMENT_URL;
|
||
case KBLegalDocumentTypeTermsOfService:
|
||
default:
|
||
return KB_TERMS_OF_SERVICE_URL;
|
||
}
|
||
}
|
||
|
||
+ (NSString *)kb_fallbackErrorHTML {
|
||
return @"<!doctype html><html><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><style>body{font-family:-apple-system,BlinkMacSystemFont,'Helvetica Neue',sans-serif;padding:32px;color:#1f2937;}h1{font-size:22px;margin:0 0 12px;}p{font-size:15px;line-height:1.6;color:#4b5563;}</style></head><body><h1>Page unavailable</h1><p>The requested document could not be loaded.</p></body></html>";
|
||
}
|
||
|
||
#pragma mark - Clean up 🟢
|
||
|
||
- (void)dealloc {
|
||
if (self.observingProgress) {
|
||
@try {
|
||
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
|
||
} @catch (NSException *exception) {
|
||
NSLog(@"removeObserver estimatedProgress exception: %@", exception);
|
||
}
|
||
}
|
||
|
||
// 顺便把 JS 通道也移除,避免潜在的循环引用
|
||
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"keyBoard"];
|
||
}
|
||
|
||
@end
|