Files
keyboard/keyBoard/Class/WebView/KBWebViewViewController.m
2026-03-09 17:34:08 +08:00

288 lines
9.7 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// 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