Commit d26ad44c authored by pyfer's avatar pyfer
Browse files

LIVE-48895-80%: 【iOS】Webview重构——重构小窗,改造通信协议与联调

parent a4f5302a
......@@ -12,6 +12,7 @@
125AA2C725B7C25800AB6C0F /* PLVUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 125AA2C625B7C25800AB6C0F /* PLVUtil.m */; };
125F3AFA25B134D9005DF71B /* PLVFloatingWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 125F3AF925B134D9005DF71B /* PLVFloatingWindow.m */; };
125F3B0425B135C2005DF71B /* PLVFloatingWKWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 125F3B0325B135C2005DF71B /* PLVFloatingWKWebViewController.m */; };
365A8DC827E07B2A00F8EFC8 /* PLVWebViewBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 365A8DC727E07B2A00F8EFC8 /* PLVWebViewBridge.m */; };
36EC652927D19C65003E3878 /* PLVFWebViewJavascriptBridge_JS.m in Sources */ = {isa = PBXBuildFile; fileRef = 36EC652427D19C64003E3878 /* PLVFWebViewJavascriptBridge_JS.m */; };
36EC652A27D19C65003E3878 /* PLVFWebViewJavascriptBridgeBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 36EC652527D19C64003E3878 /* PLVFWebViewJavascriptBridgeBase.m */; };
36EC652B27D19C65003E3878 /* PLVFWKWebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 36EC652727D19C64003E3878 /* PLVFWKWebViewJavascriptBridge.m */; };
......@@ -35,6 +36,8 @@
125F3AF925B134D9005DF71B /* PLVFloatingWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PLVFloatingWindow.m; sourceTree = "<group>"; };
125F3B0225B135C2005DF71B /* PLVFloatingWKWebViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PLVFloatingWKWebViewController.h; sourceTree = "<group>"; };
125F3B0325B135C2005DF71B /* PLVFloatingWKWebViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PLVFloatingWKWebViewController.m; sourceTree = "<group>"; };
365A8DC627E07B2A00F8EFC8 /* PLVWebViewBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PLVWebViewBridge.h; sourceTree = "<group>"; };
365A8DC727E07B2A00F8EFC8 /* PLVWebViewBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PLVWebViewBridge.m; sourceTree = "<group>"; };
36EC652327D19C64003E3878 /* PLVFWKWebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PLVFWKWebViewJavascriptBridge.h; sourceTree = "<group>"; };
36EC652427D19C64003E3878 /* PLVFWebViewJavascriptBridge_JS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLVFWebViewJavascriptBridge_JS.m; sourceTree = "<group>"; };
36EC652527D19C64003E3878 /* PLVFWebViewJavascriptBridgeBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLVFWebViewJavascriptBridgeBase.m; sourceTree = "<group>"; };
......@@ -91,6 +94,7 @@
125F3AF725B134B2005DF71B /* Floating */ = {
isa = PBXGroup;
children = (
365A8DC527E07B1800F8EFC8 /* PLVJSBridge */,
125F3B0125B13564005DF71B /* FloatingWindow */,
125F3B0225B135C2005DF71B /* PLVFloatingWKWebViewController.h */,
125F3B0325B135C2005DF71B /* PLVFloatingWKWebViewController.m */,
......@@ -108,6 +112,15 @@
path = FloatingWindow;
sourceTree = "<group>";
};
365A8DC527E07B1800F8EFC8 /* PLVJSBridge */ = {
isa = PBXGroup;
children = (
365A8DC627E07B2A00F8EFC8 /* PLVWebViewBridge.h */,
365A8DC727E07B2A00F8EFC8 /* PLVWebViewBridge.m */,
);
path = PLVJSBridge;
sourceTree = "<group>";
};
36E4980927CE28F50017BB97 /* Supporting Files */ = {
isa = PBXGroup;
children = (
......@@ -133,11 +146,11 @@
isa = PBXGroup;
children = (
36EC652327D19C64003E3878 /* PLVFWKWebViewJavascriptBridge.h */,
36EC652427D19C64003E3878 /* PLVFWebViewJavascriptBridge_JS.m */,
36EC652527D19C64003E3878 /* PLVFWebViewJavascriptBridgeBase.m */,
36EC652627D19C64003E3878 /* PLVFWebViewJavascriptBridge_JS.h */,
36EC652727D19C64003E3878 /* PLVFWKWebViewJavascriptBridge.m */,
36EC652627D19C64003E3878 /* PLVFWebViewJavascriptBridge_JS.h */,
36EC652427D19C64003E3878 /* PLVFWebViewJavascriptBridge_JS.m */,
36EC652827D19C64003E3878 /* PLVFWebViewJavascriptBridgeBase.h */,
36EC652527D19C64003E3878 /* PLVFWebViewJavascriptBridgeBase.m */,
);
path = WebViewJavascriptBridge;
sourceTree = "<group>";
......@@ -289,6 +302,7 @@
files = (
125AA2C125B7BA4300AB6C0F /* PLVBaseNavigationController.m in Sources */,
36EC652927D19C65003E3878 /* PLVFWebViewJavascriptBridge_JS.m in Sources */,
365A8DC827E07B2A00F8EFC8 /* PLVWebViewBridge.m in Sources */,
36EC652B27D19C65003E3878 /* PLVFWKWebViewJavascriptBridge.m in Sources */,
125F3AFA25B134D9005DF71B /* PLVFloatingWindow.m in Sources */,
36EC652A27D19C65003E3878 /* PLVFWebViewJavascriptBridgeBase.m in Sources */,
......@@ -370,7 +384,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
......@@ -419,7 +433,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
......
......@@ -35,14 +35,4 @@
}
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
......@@ -23,14 +23,12 @@
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// self.navigationController.navigationBar.translucent = NO;
// @"https://playertest.polyv.net/player2/lijunyu/test-player/index-ios.html";
self.urlTextView.text = @"https://playertest.polyv.net/player2/lijunyu/test-player/index-ios.html";
// self.urlTextView.text = @"https://live.polyv.cn/watch/2680353";
// self.urlTextView.text = @"https://api-zts.videocc.net/#/channel/2720174";
// 设置加载白名单
[[PLVFloatingWindow sharedInstance] setHostWhitelist:@[@"live.polyv.cn"]];
self.urlTextView.text = @"";
}
#pragma mark - Action
-(IBAction)clickFloatingAction:(id)sender {
......@@ -66,5 +64,4 @@
// Dispose of any resources that can be recreated.
}
@end
......@@ -19,19 +19,29 @@ NS_ASSUME_NONNULL_BEGIN
/// 还原时,如果控制器还在导航栈中,则通过此回调处理。
@property (nonatomic, copy) void (^recoverWebViewBlock)(WKWebView *webview);
/// 手动关闭小窗的回调,关闭小窗后需要主控制器销毁掉WebView
@property (nonatomic, copy) void (^closeFloatingWindowBlock)(void);
+ (instancetype)sharedInstance;
/// 设置加载的 URL 域名 白名单 只有设置了白名单 才会进行JS的交互
/// 只有在 webview 加载前设置才会生效
/// @param whitelist 白名单
- (void)setHostWhitelist:(NSArray <NSString *>*)whitelist;
/// 设置浏览器的UA信息 必须在加载URL 之前设置
/// @param webview 需要设置UA的 webview
/// @param url 需要设置UA的url链接
- (void)setWebViewUserAgent:(WKWebView *)webview loadURL:(NSURL *)url;
/// 把webview从控制器移动到window上
/// @param webview 要移动的webview
/// @param height webview的内容高度(为0则使用默认高度)
/// @param size webview的内容 size
/// @param containerVC webview所在的控制器
-(void)moveWebviewToWindow:(WKWebView *)webview contentHeight:(CGFloat)height fromController:(PLVFloatingWKWebViewController *)containerVC;
- (void)moveWebviewToWindow:(WKWebView *)webview webViewSize:(CGSize)size fromController:(PLVFloatingWKWebViewController *)containerVC;
/// 还原小窗 在收到 JS "changeToNormal" 事件后调用
- (void)changeWindowToNormal;
/// 关闭小窗 不会触发[closeFloatingWindowBlock]
-(void)clickCloseWindowAction;
/// 关闭小窗
- (void)clickCloseWindowAction;
@end
......
......@@ -13,17 +13,17 @@
@interface PLVFloatingWindow ()<UIGestureRecognizerDelegate>
/// 数据
/// 悬浮窗宽度,默认值为设备屏幕宽度0.3
@property (nonatomic, assign) CGFloat windowWidth;
/// 悬浮窗宽高比,默认 3 : 4
@property (nonatomic, assign) CGFloat sizeScale;
/// 悬浮窗初始位置,默认为离屏幕底部 100pt,离屏幕右侧 10pt
@property (nonatomic, assign) CGPoint originPoint;
/// 域名 白名单
@property (nonatomic, strong) NSMutableArray <NSString *>*whitelist;
@property (nonatomic, strong) WKWebView *mainWebview;
@property (nonatomic, strong) UIButton *closeButton;
@property (nonatomic, strong) UITapGestureRecognizer *tapGestureRecognizer;
@property (nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer;
@property (nonatomic, assign) CGRect originalFrame; //!< webView在原控制器的位置大小
......@@ -32,13 +32,11 @@
@end
@implementation PLVFloatingWindow
#pragma mark - Init
#pragma mark - [ Life Cycle ]
-(instancetype)init
{
- (instancetype)init {
if (self = [super init]) {
self.hidden = YES;
self.windowLevel = UIWindowLevelNormal + 1;
......@@ -48,16 +46,12 @@
self.windowWidth = MIN(PLV_ScreenWidth, PLV_ScreenHeight) * 0.3;
[self resetFrame];
[self addGestureRecognizer:self.tapGestureRecognizer];
[self addGestureRecognizer:self.panGestureRecognizer];
[self addSubview:self.closeButton];
}
return self;
}
+(instancetype)sharedInstance
{
+ (instancetype)sharedInstance {
static PLVFloatingWindow *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
......@@ -66,25 +60,43 @@
return _sharedInstance;
}
#pragma mark - Action
#pragma mark - Public Methods
#pragma mark - [ Public Method ]
- (void)setHostWhitelist:(NSArray <NSString *>*)whitelist {
[whitelist enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![self.whitelist containsObject:obj]) {
[self.whitelist addObject:obj];
}
}];
}
- (void)setWebViewUserAgent:(WKWebView *)webview loadURL:(NSURL *)url {
NSString *oldUserAgent = [self getWebViewUserAgent];
// plvwebviewapp/<webview-version>
NSString *customUserAgent = @"plvwebviewapp/1.0";
if ([self.whitelist containsObject:url.host] &&
![oldUserAgent containsString:customUserAgent]) {
NSString *newUserAgent = [NSString stringWithFormat:@"%@ %@",oldUserAgent, customUserAgent];
webview.customUserAgent = newUserAgent;
}
}
/// 把webview从控制器移动到window上
/// @param webview 要移动的webview
/// @param height webview的内容高度(为0则使用默认高度)
/// @param size webview的内容size
/// @param containerVC webview所在的控制器
-(void)moveWebviewToWindow:(WKWebView *)webview contentHeight:(CGFloat)height fromController:(PLVFloatingWKWebViewController *)containerVC
{
// 若没有获取到webview内容高度,则给定一个默认高度
CGFloat contentHeight = height > 0 ? height : 603;
self.sizeScale = PLV_ScreenWidth / contentHeight;
[self resetFrame];
- (void)moveWebviewToWindow:(WKWebView *)webview webViewSize:(CGSize)size fromController:(PLVFloatingWKWebViewController *)containerVC {
// 设置 webview 的size
if (size.height > 0 && size.width > 0) {
self.windowWidth = size.width;
self.sizeScale = size.width / size.height;
[self resetFrame];
}
self.mainWebview = webview;
self.originalFrame = self.mainWebview.frame;
self.mainWebview.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
self.mainWebview.scrollView.userInteractionEnabled = NO;
self.mainWebview.scrollView.bounces = NO;
self.originalViewController = containerVC;
self.originalNavigation = containerVC.navigationController;
......@@ -93,9 +105,33 @@
self.hidden = NO;
}
/// 还原 小窗
- (void)changeWindowToNormal {
if (self.originalNavigation) {
//通过导航方式加载的控制器
NSArray *vcArray = self.originalNavigation.viewControllers;
NSInteger index = [vcArray indexOfObject:self.originalViewController];
if (index == NSNotFound) {
//原控制器已经不存在,则新建控制器
[self dealRebuildWebView];
} else {
if (index == vcArray.count - 1) {
// 原控制器在导航栈顶
[self dealRecoverWebView];
} else {
// 原控制器不在导航栈顶
[self.originalNavigation popToViewController:self.originalViewController animated:NO];
[self dealRecoverWebView];
}
}
} else {
// 不是通过导航方式加载的控制器
[self dealRecoverWebView];
}
}
/// 处理关闭小窗口
-(void)clickCloseWindowAction
{
- (void)clickCloseWindowAction {
self.originalViewController = nil;
self.originalNavigation = nil;
[self.mainWebview stopLoading];
......@@ -104,46 +140,41 @@
self.hidden = YES;
}
#pragma mark - Private Methods
#pragma mark - [ Private Method ]
/// 重设window的位置大小
-(void)resetFrame
{
- (void)resetFrame {
CGFloat windowHeight = self.windowWidth / self.sizeScale;
self.originPoint = CGPointMake(PLV_ScreenWidth - self.windowWidth - 10, PLV_ScreenHeight - windowHeight - 100);
self.frame = CGRectMake(self.originPoint.x, self.originPoint.y, self.windowWidth, windowHeight);
self.closeButton.frame = CGRectMake(self.frame.size.width - 35, 0, 35, 35);
}
/// 源控制器存在于导航栈的情况下,处理把webview还原到原控制器
-(void)dealRecoverWebView
{
if (self.recoverWebViewBlock) {
self.originalViewController = nil;
self.originalNavigation = nil;
//还原frame
self.mainWebview.frame = self.originalFrame;
self.mainWebview.scrollView.userInteractionEnabled = YES;
self.hidden = YES;
self.recoverWebViewBlock(self.mainWebview);
self.mainWebview = nil;
}
- (void)dealRecoverWebView {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.recoverWebViewBlock) {
self.originalViewController = nil;
self.originalNavigation = nil;
//还原frame
self.mainWebview.frame = self.originalFrame;
self.hidden = YES;
self.recoverWebViewBlock(self.mainWebview);
self.mainWebview = nil;
}
});
}
/// 原控制器不在,处理恢复webview到重建控制器
-(void)dealRebuildWebView
{
- (void)dealRebuildWebView {
self.mainWebview.frame = self.originalFrame;
self.mainWebview.scrollView.userInteractionEnabled = YES;
self.hidden = YES;
PLVFloatingWKWebViewController *resetContainerViewController = [[PLVFloatingWKWebViewController alloc]initWithWebView:self.mainWebview];
PLVFloatingWKWebViewController *resetContainerViewController = [[PLVFloatingWKWebViewController alloc] initWithWebView:self.mainWebview];
[self pushResetContainerVC:resetContainerViewController];
}
/// push到新建的控制器
/// @param resetContainerViewController 新建的控制器
-(void)pushResetContainerVC:(PLVFloatingWKWebViewController *)resetContainerViewController
{
- (void)pushResetContainerVC:(PLVFloatingWKWebViewController *)resetContainerViewController {
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *rootNavigation = (UINavigationController *)rootViewController;
......@@ -162,47 +193,49 @@
}
}
#pragma mark - Action
#pragma mark Getter & Setter
- (void)clickCloseWindowButtonAction {
// 主动关闭小窗口 需要回调给主控制器
self.closeFloatingWindowBlock ? self.closeFloatingWindowBlock() : nil;
[self clickCloseWindowAction];
- (NSMutableArray *)whitelist {
if (!_whitelist) {
_whitelist = [NSMutableArray array];
}
return _whitelist;
}
#pragma mark - Gesture
/// 处理单击手势,还原小窗
/// @param gesture gesture
-(void)tapWindow:(UITapGestureRecognizer *)gesture
{
if (self.originalNavigation) {
//通过导航方式加载的控制器
NSArray *vcArray = self.originalNavigation.viewControllers;
NSInteger index = [vcArray indexOfObject:self.originalViewController];
if (index == NSNotFound) {
//原控制器已经不存在,则新建控制器
[self dealRebuildWebView];
}else {
if (index == vcArray.count - 1) {
// 原控制器在导航栈顶
[self dealRecoverWebView];
}else {
// 原控制器不在导航栈顶
[self.originalNavigation popToViewController:self.originalViewController animated:NO];
[self dealRecoverWebView];
}
}
- (UIPanGestureRecognizer *)panGestureRecognizer {
if (!_panGestureRecognizer) {
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragWindow:)];
_panGestureRecognizer.delegate = self;
}
else {
// 不是通过导航方式加载的控制器
[self dealRecoverWebView];
return _panGestureRecognizer;
}
- (NSString *)getWebViewUserAgent {
WKWebView *uaWebView = [[WKWebView alloc] initWithFrame:CGRectZero];
[uaWebView loadHTMLString:@"<html></html>" baseURL:nil];
__block BOOL end = NO;
__block NSString *userAgentString = nil;
[uaWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
userAgentString = obj;
end = YES;
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
end = YES;
});
while (!end) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
return userAgentString;
}
#pragma mark - [ Event ]
#pragma mark - Gesture
/// 拖拽窗口
-(void)dragWindow:(UIPanGestureRecognizer *)gesture
{
- (void)dragWindow:(UIPanGestureRecognizer *)gesture {
CGPoint translatedPoint = [gesture translationInView:[UIApplication sharedApplication].delegate.window];
CGFloat x = gesture.view.center.x + translatedPoint.x;
CGFloat y = gesture.view.center.y + translatedPoint.y;
......@@ -236,40 +269,8 @@
#pragma mark - UIGestureRecognizerDelegate
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
#pragma mark - Loadlazy
-(UIButton *)closeButton
{
if (!_closeButton) {
_closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_closeButton setImage:[UIImage imageNamed:@"plv_floating_btn_close"] forState:0];
_closeButton.backgroundColor = [UIColor lightGrayColor];
[_closeButton addTarget:self action:@selector(clickCloseWindowButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _closeButton;
}
- (UITapGestureRecognizer *)tapGestureRecognizer
{
if (!_tapGestureRecognizer) {
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapWindow:)];
_tapGestureRecognizer.delegate = self;
}
return _tapGestureRecognizer;
}
- (UIPanGestureRecognizer *)panGestureRecognizer {
if (!_panGestureRecognizer) {
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragWindow:)];
_panGestureRecognizer.delegate = self;
}
return _panGestureRecognizer;
}
@end
......@@ -10,43 +10,32 @@
#import "PLVFloatingWindow.h"
#import "PLVUtil.h"
#import "PLVFWKWebViewJavascriptBridge.h"
#import "PLVWebViewBridge.h"
@interface PLVFloatingWKWebViewController ()<WKNavigationDelegate, WKUIDelegate>
@interface PLVFloatingWKWebViewController ()<WKNavigationDelegate, PLVWebViewBridgeDelegate>
/// UI
@property (nonatomic, strong) WKWebView *mainWebview;
@property (nonatomic, strong) WKWebView *otherWebview;
@property (nonatomic, strong) UIButton *floatFullBtn;
/// 数据
@property (nonatomic, strong) NSString *urlString; // 加载的网页链接
@property (nonatomic, assign) CGFloat webViewContentHeight;
@property (nonatomic, strong) PLVFWKWebViewJavascriptBridge *mainBridge;
@property (nonatomic, strong) PLVWebViewBridge *bridge;
@end
@implementation PLVFloatingWKWebViewController
#pragma mark - Lift cycle & dealloc
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
#pragma mark - [ Life Cycle ]
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[PLVUtil rotateOrientation:UIInterfaceOrientationPortrait];
[self setUpUI];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
-(void)dealloc {
- (void)dealloc {
NSLog(@"dealloc");
}
......@@ -56,7 +45,7 @@
/// 初始化控制器
/// @param urlString 网页url路径
-(instancetype)initWithUrlString:(NSString *)urlString {
- (instancetype)initWithUrlString:(NSString *)urlString {
if (self = [super init]) {
_urlString = urlString;
}
......@@ -65,51 +54,42 @@
/// 当存在悬浮窗,但原来webview的容器控制器不存在,通过此方法重建容器
/// @param webview webview
-(instancetype)initWithWebView:(WKWebView *)webview {
- (instancetype)initWithWebView:(WKWebView *)webview {
if (self = [super init]) {
_mainWebview = webview;
}
return self;
}
-(void)setUpUI {
- (void)setUpUI {
self.title = @"可小窗 WKWebView";
self.view.backgroundColor = [UIColor lightTextColor];
self.edgesForExtendedLayout = UIRectEdgeNone;
if (_mainWebview) {
//通过webview重建控制器
// 1.关闭小窗
if (_mainWebview) { // 通过webview重建控制器
// 关闭小窗
[[PLVFloatingWindow sharedInstance] clickCloseWindowAction];
// 2.告诉h5还原小窗布局
[self callRevertBigWindow];
self.mainWebview.scrollView.userInteractionEnabled = YES;
// 重置 webview
self.mainWebview.scrollView.bounces = YES;
self.mainWebview.frame = self.view.bounds;
[self.view addSubview:self.mainWebview];
}else {
[self.view addSubview:self.mainWebview];
[self setWebViewJavascriptBridge];
// 恢复小窗
[self.bridge changeToNormal];
} else {
[self webView:self.mainWebview loadUrlString:self.urlString];
[self registerSmallWindowEventFunction];
[self setWebViewJavascriptBridge];
}
[self.view addSubview:self.mainWebview];
[self.view addSubview:self.otherWebview];
__weak typeof(self) weakSelf = self;
// 设置回调,处理还原小窗
[[PLVFloatingWindow sharedInstance] setRecoverWebViewBlock:^(WKWebView * _Nonnull webview) {
[weakSelf handleRecoverWebView:webview];
}];
// 设置手动关闭浮动小窗的回调,需要关闭mainWebview
[[PLVFloatingWindow sharedInstance] setCloseFloatingWindowBlock:^{
[weakSelf closeMainWebView];
}];
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// [self.frontWindow addSubview:self.floatFullBtn];
// });
// [self.frontWindow addSubview:self.floatFullBtn];
}
#pragma mark - [ Private Method ]
......@@ -132,9 +112,8 @@
}
_mainWebview= [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
_mainWebview.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
_mainWebview.navigationDelegate = self;
_mainBridge = [PLVFWKWebViewJavascriptBridge bridgeForWebView:_mainWebview];
[_mainBridge setWebViewDelegate:self];
}
return _mainWebview;
}
......@@ -145,68 +124,48 @@
config.allowsInlineMediaPlayback = YES;
config.requiresUserActionForMediaPlayback = NO;
CGRect otherFrame = CGRectMake(self.view.bounds.size.width, self.mainWebview.frame.origin.y, self.view.bounds.size.width, self.view.bounds.size.height);
CGRect otherFrame = CGRectMake(self.view.bounds.size.width, 0, self.view.bounds.size.width, self.view.bounds.size.height);
_otherWebview = [[WKWebView alloc] initWithFrame:otherFrame configuration:config];
_otherWebview.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
_otherWebview.backgroundColor = [UIColor whiteColor];
_otherWebview.navigationDelegate = self;
_otherWebview.UIDelegate = self;
}
return _otherWebview;
}
- (UIButton *)floatFullBtn {
if (!_floatFullBtn) {
_floatFullBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_floatFullBtn.frame = CGRectMake(10, 100, 100, 50);
_floatFullBtn.backgroundColor = [UIColor redColor];
[_floatFullBtn addTarget:self action:@selector(floatFullBtnAction) forControlEvents:UIControlEventTouchUpInside];
}
return _floatFullBtn;
}
- (void)closeMainWebView {
[self.mainWebview stopLoading];
self.mainWebview = nil;
}
- (void)webView:(WKWebView *)webView loadUrlString:(NSString *)urlString {
if ([PLVUtil checkStringUseable:urlString]) {
NSURL *h5Url = [NSURL URLWithString:urlString];
// 设置 webview 的 UA
[[PLVFloatingWindow sharedInstance] setWebViewUserAgent:webView loadURL:h5Url];
NSURLRequest *request = [NSURLRequest requestWithURL:h5Url];
[webView loadRequest:request];
}
}
#pragma mark - Call JS Method
- (void)callRevertBigWindow {
[self.mainBridge callHandler:@"revertToBig"];
}
#pragma mark - Register JS Method
- (void)registerSmallWindowEventFunction {
__weak typeof(self) weakSelf = self;
[self.mainBridge registerHandler:@"changeToSmall" handler:^(id data, WVJBResponseCallback responseCallback) {
if ([PLVUtil checkDictionaryUseable:data]) {
[weakSelf openFloatingWindowAction:data];
}
}];
- (void)setWebViewJavascriptBridge {
self.bridge = [[PLVWebViewBridge alloc] initBridgeWithWebview:self.mainWebview webviewDelegate:self];
self.bridge.delegate = self;
}
#pragma mark - Action
/// 处理h5的changeToSmall事件,打开小窗
/// @param bodyDict h5传递的参数,包含要打开的h5的链接url
-(void)openFloatingWindowAction:(NSDictionary *)bodyDict {
/// @param link 打开的h5的链接
- (void)openFloatingWindowSize:(CGSize)size link:(NSString *)link {
// 1.使用otherWebview打开新的h5页面
[self webView:self.otherWebview loadUrlString:bodyDict[@"url"]];
[self webView:self.otherWebview loadUrlString:link];
// 2.把otherWebview移动到屏幕上
[self.otherWebview.layer addAnimation:[self moveOtherWebviewAnimation] forKey:@"move_webview"];
self.otherWebview.frame = self.view.bounds;
// 3.把mainWebview移动到window上面作为小窗存在
[[PLVFloatingWindow sharedInstance] moveWebviewToWindow:self.mainWebview contentHeight:self.webViewContentHeight fromController:self];
[[PLVFloatingWindow sharedInstance] moveWebviewToWindow:self.mainWebview webViewSize:size fromController:self];
// 4.通知JS进入小窗
[self.bridge changeToSmall];
}
/// 移动OtherWebview的动画
......@@ -226,38 +185,18 @@
/// 处理webview还原
/// @param webview mainWebview
-(void)handleRecoverWebView:(WKWebView *)webview {
// 1.告诉h5还原小窗布局
[self callRevertBigWindow];
// 2.把otherWebview移动到屏幕之外的原位置
- (void)handleRecoverWebView:(WKWebView *)webview {
// 1.把otherWebview移动到屏幕之外的原位置
[self.otherWebview.layer removeAnimationForKey:@"move_webview"];
// 3.把window上面的mainWebview移动回屏幕上
self.otherWebview.frame = CGRectMake(self.view.bounds.size.width, 0, self.view.bounds.size.width, self.view.bounds.size.height);
// 2.把window上面的mainWebview移动回屏幕上
[self.view addSubview:webview];
}
- (void)floatFullBtnAction {
// [self.mainJSBridge call:@"fullScreen" params:@[]];
[self openFloatingWindowAction:@{}];
}
- (UIWindow *)frontWindow {
NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
for (UIWindow *window in frontToBackWindows) {
BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
BOOL windowIsVisible = !window.hidden && window.alpha > 0;
BOOL windowLevelSupported = (window.windowLevel >= UIWindowLevelNormal);
BOOL windowKeyWindow = window.isKeyWindow;
if(windowOnMainScreen && windowIsVisible && windowLevelSupported && windowKeyWindow) {
return window;
}
}
return nil;
}
#pragma mark - WKNavigationDelegate
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSLog(@"%@",navigationAction.request.URL.absoluteString);
//允许跳转
decisionHandler(WKNavigationActionPolicyAllow);
......@@ -270,6 +209,22 @@
}
}
#pragma mark - PLVWebViewBridgeDelegate
- (void)webviewBridge:(PLVWebViewBridge *)bridge clickProductLink:(NSString *)link width:(CGFloat)width height:(CGFloat)height newPage:(BOOL)newPage data:(NSDictionary *)data {
if (newPage) {
[self openFloatingWindowSize:CGSizeMake(width, height) link:link];
}
}
- (void)webviewBridgeDidChangeToNormal:(PLVWebViewBridge *)bridge {
[[PLVFloatingWindow sharedInstance] changeWindowToNormal];
}
- (void)webviewBridgeDidCloseWindow:(PLVWebViewBridge *)bridge {
[[PLVFloatingWindow sharedInstance] clickCloseWindowAction];
}
#pragma mark - UIViewController+UIViewControllerRotation
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
......
//
// PLVWebViewBridge.h
// WebView-Test
//
// Created by Sakya on 2022/3/15.
// Copyright © 2022 easefun. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
NS_ASSUME_NONNULL_BEGIN
@class PLVWebViewBridge;
@protocol PLVWebViewBridgeDelegate <NSObject>
/// JS点击商品的回调
/// @param link 需要新跳转的链接
/// @param width 小窗宽度
/// @param height 小窗高度
/// @param newPage 是否需要新建小窗
/// @param data 第三方应用信息(客户自定义使用)
- (void)webviewBridge:(PLVWebViewBridge *)bridge clickProductLink:(NSString *)link width:(CGFloat)width height:(CGFloat)height newPage:(BOOL)newPage data:(NSDictionary *)data;
/// JS小窗恢复全屏的回调
- (void)webviewBridgeDidChangeToNormal:(PLVWebViewBridge *)bridge;
/// JS小窗关闭的回调
- (void)webviewBridgeDidCloseWindow:(PLVWebViewBridge *)bridge;
@end
@interface PLVWebViewBridge : NSObject
@property (nonatomic, weak) id<PLVWebViewBridgeDelegate> delegate;
- (instancetype)initBridgeWithWebview:(WKWebView *)webView webviewDelegate:(id<WKNavigationDelegate>)webViewDelegate;
/// 通知JS 需要恢复全屏
- (void)changeToNormal;
/// 通知JS 需要变小窗
- (void)changeToSmall;
@end
NS_ASSUME_NONNULL_END
//
// PLVWebViewBridge.m
// WebView-Test
//
// Created by Sakya on 2022/3/15.
// Copyright © 2022 easefun. All rights reserved.
//
#import "PLVWebViewBridge.h"
#import "PLVFWKWebViewJavascriptBridge.h"
#import "PLVUtil.h"
@interface PLVWebViewBridge()
@property (nonatomic, strong) PLVFWKWebViewJavascriptBridge *bridge;
@end
@implementation PLVWebViewBridge
#pragma mark - Initialize
- (instancetype)initBridgeWithWebview:(WKWebView *)webView webviewDelegate:(id<WKNavigationDelegate>)webViewDelegate {
self = [super init];
if (self) {
[PLVFWKWebViewJavascriptBridge enableLogging];
_bridge = [PLVFWKWebViewJavascriptBridge bridgeForWebView:webView];
[_bridge setWebViewDelegate:webViewDelegate];
if (!webView.isLoading && webView.URL) { // 如果URL已经加载过
[_bridge injectJavascriptFile];
}
[self registerClickProductEventFunction];
[self registerNormalWindowEventFunction];
[self registerCloseWindowEventFunction];
}
return self;
}
#pragma mark - [ Public Method ]
- (void)changeToSmall {
[self callHandlerChangeToSmall];
}
- (void)changeToNormal {
[self callHandlerChangeToNormal];
}
#pragma mark - [ Private Method ]
#pragma mark - Register JS Method
- (void)registerClickProductEventFunction {
__weak typeof(self) weakSelf = self;
[self.bridge registerHandler:@"clickProduct" handler:^(id data, WVJBResponseCallback responseCallback) {
if ([PLVUtil checkDictionaryUseable:data]) {
BOOL newPage = [[data objectForKey:@"newPage"] boolValue];
NSString *link = data[@"link"];
CGFloat width = [[data objectForKey:@"width"] floatValue];
CGFloat height = [[data objectForKey:@"height"] floatValue];
NSDictionary *productData = [data objectForKey:@"data"];
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(webviewBridge:clickProductLink:width:height:newPage:data:)]) {
[weakSelf.delegate webviewBridge:weakSelf clickProductLink:link width:width height:height newPage:newPage data:productData];
}
}
}];
}
- (void)registerNormalWindowEventFunction {
__weak typeof(self) weakSelf = self;
[self.bridge registerHandler:@"changeToNormal" handler:^(id data, WVJBResponseCallback responseCallback) {
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(webviewBridgeDidChangeToNormal:)]) {
[weakSelf.delegate webviewBridgeDidChangeToNormal:weakSelf];
}
}];
}
- (void)registerCloseWindowEventFunction {
__weak typeof(self) weakSelf = self;
[self.bridge registerHandler:@"closeWindow" handler:^(id data, WVJBResponseCallback responseCallback) {
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(webviewBridgeDidCloseWindow:)]) {
[weakSelf.delegate webviewBridgeDidCloseWindow:weakSelf];
}
}];
}
#pragma mark - Call JS Method
- (void)callHandlerChangeToSmall {
[self.bridge callHandler:@"changeToSmall"];
}
- (void)callHandlerChangeToNormal {
[self.bridge callHandler:@"changeToNormal"];
}
@end
......@@ -29,6 +29,13 @@
- (void)setWebViewDelegate:(id)webViewDelegate;
- (void)disableJavscriptAlertBoxSafetyTimeout;
#pragma mark Custom
/// 执行缓存中的所有 Message
/// 为了适配用已经加载过url的 webview 重新创建 Bridge 时导致 callHandler 方法不执行的问题
/// 需自行判断 webview 是否已经加载完成
- (void)injectJavascriptFile;
@end
#endif
......@@ -57,6 +57,10 @@
[_base.messageHandlers removeObjectForKey:handlerName];
}
- (void)injectJavascriptFile {
[_base injectJavascriptFile];
}
- (void)reset {
[_base reset];
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment