|
|
@@ -0,0 +1,224 @@
|
|
|
+//
|
|
|
+// WebViewController.m
|
|
|
+// AIIM
|
|
|
+//
|
|
|
+// Created by on 2025/10/23.
|
|
|
+//
|
|
|
+
|
|
|
+#import "WebViewController.h"
|
|
|
+#import <WebKit/WebKit.h>
|
|
|
+
|
|
|
+@interface WebViewController () <WKNavigationDelegate, WKUIDelegate>
|
|
|
+
|
|
|
+@property (nonatomic, strong) WKWebView *webView;
|
|
|
+@property (nonatomic, strong) UIProgressView *progressView;
|
|
|
+@property (nonatomic, strong) UIButton *backButton;
|
|
|
+@property (nonatomic, strong) UILabel *titleLabel;
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+@implementation WebViewController
|
|
|
+
|
|
|
+#pragma mark - Lifecycle
|
|
|
+
|
|
|
+- (instancetype)initWithURL:(NSURL *)url {
|
|
|
+ return [self initWithURL:url title:nil];
|
|
|
+}
|
|
|
+
|
|
|
+- (instancetype)initWithURL:(NSURL *)url title:(NSString *)title {
|
|
|
+ self = [super init];
|
|
|
+ if (self) {
|
|
|
+ _url = url;
|
|
|
+ self.title = title;
|
|
|
+ self.hidesBottomBarWhenPushed = YES;
|
|
|
+ }
|
|
|
+ return self;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)viewDidLoad {
|
|
|
+ [super viewDidLoad];
|
|
|
+
|
|
|
+ [self setupUI];
|
|
|
+ [self setupNavigationBar];
|
|
|
+ [self setupWebView];
|
|
|
+
|
|
|
+ if (self.url) {
|
|
|
+ [self loadURL:self.url];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)dealloc {
|
|
|
+ [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
|
|
|
+ [self.webView removeObserver:self forKeyPath:@"title"];
|
|
|
+ [self.webView removeObserver:self forKeyPath:@"canGoBack"];
|
|
|
+ [self.webView removeObserver:self forKeyPath:@"canGoForward"];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark - UI Setup
|
|
|
+
|
|
|
+- (void)setupUI {
|
|
|
+ self.view.backgroundColor = [UIColor blackColor];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)setupWebView {
|
|
|
+ // 配置 WKWebView
|
|
|
+ WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
|
|
|
+ configuration.allowsInlineMediaPlayback = YES;
|
|
|
+ configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
|
|
|
+
|
|
|
+ // 创建 WKWebView
|
|
|
+ self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
|
|
|
+ self.webView.navigationDelegate = self;
|
|
|
+ self.webView.UIDelegate = self;
|
|
|
+ self.webView.allowsBackForwardNavigationGestures = YES;
|
|
|
+
|
|
|
+ [self.view addSubview:self.webView];
|
|
|
+ [self.webView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.bottom.equalTo(self.view);
|
|
|
+ make.top.equalTo(self.titleLabel.mas_bottom).mas_offset(0);
|
|
|
+ }];
|
|
|
+
|
|
|
+ // 添加 KVO 监听
|
|
|
+ [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
|
|
|
+ [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
|
|
|
+ [self.webView addObserver:self forKeyPath:@"canGoBack" options:NSKeyValueObservingOptionNew context:nil];
|
|
|
+ [self.webView addObserver:self forKeyPath:@"canGoForward" options:NSKeyValueObservingOptionNew context:nil];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)setupNavigationBar {
|
|
|
+
|
|
|
+ self.titleLabel = [UILabel new];
|
|
|
+ self.titleLabel.text = self.title;
|
|
|
+ self.titleLabel.textColor = [UIColor whiteColor];
|
|
|
+ self.titleLabel.font = SYSBFONT(18);
|
|
|
+ [self.view addSubview:self.titleLabel];
|
|
|
+ [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.centerX.equalTo(self.view);
|
|
|
+ make.width.mas_lessThanOrEqualTo(200);
|
|
|
+ make.top.equalTo(self.view).mas_offset(STATUS_Height);
|
|
|
+ make.height.mas_equalTo(44);
|
|
|
+ }];
|
|
|
+
|
|
|
+ self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
+ [self.backButton setImage:kImageMake(@"fanhui") forState:UIControlStateNormal];
|
|
|
+ [self.backButton addTarget:self action:@selector(goBack) forControlEvents:UIControlEventTouchUpInside];
|
|
|
+ [self.view addSubview:self.backButton];
|
|
|
+ [self.backButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.equalTo(self.view).mas_offset(10);
|
|
|
+ make.centerY.equalTo(self.titleLabel);
|
|
|
+ make.size.mas_equalTo(CGSizeMake(32, 32));
|
|
|
+ }];
|
|
|
+
|
|
|
+
|
|
|
+ // 进度条
|
|
|
+ self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
|
|
|
+ self.progressView.progressTintColor = globalColor(GCTypeGreen);
|
|
|
+ self.progressView.trackTintColor = [UIColor clearColor];
|
|
|
+ self.progressView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
|
+ [self.view addSubview:self.progressView];
|
|
|
+
|
|
|
+ [self.progressView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.equalTo(self.view);
|
|
|
+ make.top.equalTo(self.titleLabel.mas_bottom).mas_offset(0);
|
|
|
+ make.height.mas_equalTo(2);
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark - Actions
|
|
|
+
|
|
|
+- (void)closeButtonTapped {
|
|
|
+ [self dismissViewControllerAnimated:YES completion:nil];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)goBack {
|
|
|
+ if ([self.webView canGoBack]) {
|
|
|
+ [self.webView goBack];
|
|
|
+ } else {
|
|
|
+ [self.navigationController popViewControllerAnimated:YES];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark - Private Methods
|
|
|
+
|
|
|
+- (void)loadURL:(NSURL *)url {
|
|
|
+ NSURLRequest *request = [NSURLRequest requestWithURL:url];
|
|
|
+ [self.webView loadRequest:request];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark - KVO
|
|
|
+
|
|
|
+- (void)observeValueForKeyPath:(NSString *)keyPath
|
|
|
+ ofObject:(id)object
|
|
|
+ change:(NSDictionary<NSKeyValueChangeKey,id> *)change
|
|
|
+ context:(void *)context {
|
|
|
+
|
|
|
+ if ([keyPath isEqualToString:@"estimatedProgress"]) {
|
|
|
+ // 更新进度条
|
|
|
+ float progress = [[change objectForKey:NSKeyValueChangeNewKey] floatValue];
|
|
|
+ [self.progressView setProgress:progress animated:YES];
|
|
|
+
|
|
|
+ // 进度完成时隐藏进度条
|
|
|
+ if (progress >= 1.0) {
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ self.progressView.hidden = YES;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ self.progressView.hidden = NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ([keyPath isEqualToString:@"title"]) {
|
|
|
+ NSString *title = [change objectForKey:NSKeyValueChangeNewKey];
|
|
|
+ self.title = title.length > 0 ? title : @"网页";
|
|
|
+ self.titleLabel.text = self.title;
|
|
|
+ }
|
|
|
+ else if ([keyPath isEqualToString:@"canGoBack"] || [keyPath isEqualToString:@"canGoForward"]) {
|
|
|
+ // 更新导航按钮状态
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark - WKNavigationDelegate
|
|
|
+
|
|
|
+- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
|
|
|
+ self.progressView.hidden = NO;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
|
|
|
+ if (error.code != NSURLErrorCancelled) {
|
|
|
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"加载失败"
|
|
|
+ message:error.localizedDescription
|
|
|
+ preferredStyle:UIAlertControllerStyleAlert];
|
|
|
+
|
|
|
+ UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定"
|
|
|
+ style:UIAlertActionStyleDefault
|
|
|
+ handler:nil];
|
|
|
+ [alert addAction:okAction];
|
|
|
+
|
|
|
+ [self presentViewController:alert animated:YES completion:nil];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
|
|
|
+
|
|
|
+ NSURL *url = navigationAction.request.URL;
|
|
|
+
|
|
|
+ // 处理特殊 URL scheme
|
|
|
+ if (![url.scheme isEqualToString:@"http"] && ![url.scheme isEqualToString:@"https"]) {
|
|
|
+ // 尝试打开外部应用
|
|
|
+ if ([[UIApplication sharedApplication] canOpenURL:url]) {
|
|
|
+ [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
|
|
|
+ }
|
|
|
+ decisionHandler(WKNavigationActionPolicyCancel);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ decisionHandler(WKNavigationActionPolicyAllow);
|
|
|
+}
|
|
|
+
|
|
|
+@end
|