DsWebController.m 13.2 KB
//
//  DsWebController.m
//  DreamSleep
//
//  Created by peter on 2022/4/24.
//

#import "DsWebController.h"
#import <WebKit/webKit.h>
#import "DsMaskView.h"
#import "SomeProxy.h"
#import "InviteController.h"

@interface DsWebController () <WKNavigationDelegate, WKScriptMessageHandler>
@property (nonatomic, copy)   NSString *naviTitle;
@property (nonatomic, strong) NSURL *linkUrl;
@property (nonatomic, assign) BOOL isShowNavi;
@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) NSMutableURLRequest *request;
@property (nonatomic, strong) UIProgressView *progressView;
@property (nonatomic, strong) ExceptionDefaultView *exceptionView;
/// 白天/夜间模式导航栏返回按钮
@property (nonatomic, strong) UIButton *dkBackBtn;
/// 遮罩
@property (nonatomic, strong) DsMaskView *dsMaskView;
/// 邀请好友悬浮框
@property (nonatomic, strong) UIView *inviteView;
/// 是否已获取当前webview的title
@property (assign, nonatomic) BOOL webViewLoadTitle;
@end

@implementation DsWebController

- (instancetype)initWithTitle:(NSString *)title link:(NSString *)link {
    if (self = [super init]) {
        self.naviTitle = title;
        self.isShowNavi = YES;
        self.linkUrl = [NSURL URLWithString:link];
    }
    return self;
}

- (instancetype)initWithLink:(NSString *)link isShowNavi:(BOOL)isShowNavi {
    if (self = [super init]) {
        self.linkUrl = [NSURL URLWithString:link];
        self.isShowNavi = isShowNavi;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.navigationItem.title = self.naviTitle;
    self.view.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
    
    [self.view addSubview:self.webView];
    [self.view addSubview:self.progressView];
    if (self.isShowNavi == NO) {
        [self.view addSubview:self.dkBackBtn];
    }
    
    [self.webView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(0, 0, 0, 0));
    }];
    [self.progressView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.right.equalTo(self.view);
    }];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    /*
     webview已获取到title,但返回时无法获取title:
     可能是内存不足造成的,此时需要重新加载同时避免第一次进入重新加载;
     */
    if (self.webViewLoadTitle == YES && self.webView.title == nil) {
        [self.webView reload];
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    if (self.webView.title) {
        self.webViewLoadTitle = YES;
    }
}

- (void)dealloc {
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"AppModel"];
    [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
}

- (void)closeCurrentPage {
    if (self.navigationController.viewControllers.count) {
        [self.navigationController popViewControllerAnimated:YES];
    }
}

#pragma mark - WKWebView的监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"estimatedProgress"]) {
        [self.progressView setProgress:self.webView.estimatedProgress animated:YES];
        self.progressView.hidden = self.progressView.progress == 1;
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler {
    DSLog(@"通用H5页面入口->跳转链接:%@", navigationAction.request.URL.absoluteString);
    
    // 为了解决跨域问题,每次跳转url时把cookies拼接上
    NSMutableURLRequest *request = (NSMutableURLRequest *)navigationAction.request;
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    request.allHTTPHeaderFields = dict;
    
    decisionHandler(WKNavigationActionPolicyAllow);
}

- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    self.exceptionView.hidden = YES;
    if (self.isShowNavi == NO) { self.dkBackBtn.hidden = YES; }
    [self.dsMaskView display];
    self.webView.hidden = !self.exceptionView.hidden;
    
    [self serverErrorDeal];
}

// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    DSLog(@"加载失败:%@", error.userInfo);
    [self wkErrorDeal];
}

// 提交发生错误时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    DSLog(@"提交失败:%@", error.userInfo);
    [self wkErrorDeal];
}

// 内存不足出现白屏问题
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    [self.webView reload];
    
    // 友盟自定义异常
    NSString *url = self.webView.URL.absoluteString.lastPathComponent ? self.webView.URL.absoluteString.lastPathComponent : @"空链接";
    [DataStatisticsUtil reportExceptionWithName:H5Monitor reason:@"webViewWebContentProcessDidTerminate" stackTrace:@[url]];
}

#pragma mark - 错误处理
- (void)wkErrorDeal {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    self.exceptionView.hidden = NO;
    self.webView.hidden = !self.exceptionView.hidden;
    if (self.isShowNavi == NO) { self.dkBackBtn.hidden = NO; }
}

- (void)serverErrorDeal {
    // 网页加载成功了,处理服务器常见错误
    NSURLSessionTask * datatask = [[NSURLSession sharedSession] dataTaskWithRequest:self.request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        DSLog(@"statusCode:%ld, error:%@, info:%@", httpResponse.statusCode, error, [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]);
        
        if (httpResponse.statusCode == 404 || httpResponse.statusCode == 502) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.exceptionView.hidden = NO;
                if (self.isShowNavi == NO) { self.dkBackBtn.hidden = NO; }
                self.webView.hidden = !self.exceptionView.hidden;
                [self.exceptionView showServerErrInfo:[NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]];
            });
        }
    }];
    [datatask resume];
}

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"AppModel"]) {
        NSDictionary *bodyDic = message.body;
        int type = [bodyDic[@"type"] intValue];
        DSLog(@"bodyDic:%@", bodyDic);
        
        switch (type) {
            case 66: // 显示邀请好友测试悬浮按钮
            {
                self.inviteView.hidden = NO;
            }
                break;
            case 77: // 隐藏邀请好友测试悬浮按钮
            {
                self.inviteView.hidden = YES;
            }
                break;
            case 88: // 关闭webview
            {
                if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(reloadAIPage)]) {
                    [self.refreshDelegate reloadAIPage];
                }
                [self.navigationController popViewControllerAnimated:YES];
            }
                break;
            default:
                break;
        }
    }
}

#pragma mark - lazy
- (WKWebView *)webView {
    if (!_webView) {
        // 进行配置控制器
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
        // 实例化对象
        configuration.userContentController = [WKUserContentController new];
        // 调用JS方法
        [configuration.userContentController addScriptMessageHandler:[[SomeProxy alloc] initObject:self] name:@"AppModel"];
        
        // 进行偏好设置
        WKPreferences *preferences = [WKPreferences new];
        preferences.javaScriptCanOpenWindowsAutomatically = NO;
        preferences.javaScriptEnabled = YES;
        configuration.preferences = preferences;
        
        _webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
        _webView.navigationDelegate = self;
        _webView.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
        _webView.scrollView.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
        _webView.scrollView.showsVerticalScrollIndicator = NO;
        _webView.hidden = YES;
        _webView.scrollView.bounces = NO;
        _webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        [_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:self.linkUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
        self.request = request;
        [_webView loadRequest:self.request];
    }
    return _webView;
}

- (UIProgressView *)progressView {
    if (!_progressView) {
        _progressView = [[UIProgressView alloc] init];
        _progressView.progressTintColor = HighlightColor;
        _progressView.dk_trackTintColorPicker = DKColorPickerWithKey(VCViewBG);
    }
    return _progressView;
}

- (ExceptionDefaultView *)exceptionView {
    if (!_exceptionView) {
        WS(weakSelf);
        _exceptionView = [[ExceptionDefaultView alloc] initWithType:ExceptionTypeNet block:^{
            weakSelf.progressView.hidden = NO;
            weakSelf.progressView.progress = 0;
            weakSelf.exceptionView.hidden = YES;
            if (weakSelf.isShowNavi == NO) { weakSelf.dkBackBtn.hidden = YES; }
            [weakSelf.webView loadRequest:weakSelf.request];
        } superView:self.view];
    }
    return _exceptionView;
}

- (UIButton *)dkBackBtn {
    if (!_dkBackBtn) {
        _dkBackBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, kStatusBarHeight, 40, 40)];
        [_dkBackBtn dk_setImage:DKImagePickerWithNames(@"cus_back_icon", @"dk_cus_back_icon", @"dk_cus_back_icon") forState:UIControlStateNormal];
        [_dkBackBtn addTarget:self action:@selector(closeCurrentPage) forControlEvents:UIControlEventTouchUpInside];
        _dkBackBtn.hidden = YES;
    }
    return _dkBackBtn;
}

- (DsMaskView *)dsMaskView {
    if (!_dsMaskView) {
        _dsMaskView = [[DsMaskView alloc] initWithSuperView:self.view];
    }
    return _dsMaskView;
}

- (UIView *)inviteView {
    if (!_inviteView) {
        _inviteView = [UIView new];
        _inviteView.hidden = YES;
        [self.view addSubview:_inviteView];
        
        WS(weakSelf);
        UIButton *closeBtn = [UIButton new];
        [closeBtn setImage:[UIImage imageNamed:@"close_invite_icon"] forState:UIControlStateNormal];
        [closeBtn addTouchUpInsideHandler:^(NSInteger tag) {
            weakSelf.inviteView.hidden = YES;
        }];
        [_inviteView addSubview:closeBtn];
        
        UIButton *inviteBtn = [UIButton new];
        [inviteBtn setImage:[UIImage imageNamed:@"invite_friend_test"] forState:UIControlStateNormal];
        [inviteBtn addTouchUpInsideHandler:^(NSInteger tag) {
            InviteController *inviteVC = [InviteController new];
            inviteVC.isFromH5 = YES;
            [weakSelf.navigationController pushViewController:inviteVC animated:YES];
        }];
        [_inviteView addSubview:inviteBtn];
        
        [_inviteView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.size.mas_equalTo(CGSizeMake(91, 80));
            make.right.equalTo(self.view).offset(-10);
            make.bottom.equalTo(self.view).offset(-150-Bottom_SafeArea_Height);
        }];
        [closeBtn mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.left.equalTo(_inviteView);
            make.width.height.equalTo(@22);
        }];
        [inviteBtn mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.right.equalTo(_inviteView);
            make.width.height.equalTo(@80);
        }];
    }
    return _inviteView;
}

#pragma mark - 关闭侧滑
- (BOOL)enableInteractivePopGestureRecognizer {
    return NO;
}

#pragma mark - 隐藏导航栏
- (BOOL)isShowNavigationBar {
    return !self.isShowNavi;
}

#pragma mark - H5页面导航栏为默认模式
- (NaviStyle)navigationBarStyle {
    return NaviStyleDefault;
}

#pragma mark - 设置状态栏文字颜色
- (UIStatusBarStyle)preferredStatusBarStyle {
    return [self.dk_manager.themeVersion isEqualToString:DKThemeVersionNormal] ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent;
}

@end