iOS与H5交互遇到的坑
之前的博客写过使用<JavaScriptCore/JavaScriptCore.h>库来实现与H5的交互,但是在项目中还是遇到了一些不得不踩的坑。在这里将我遇到的问题以及参考网上几位大神的解决方案列举出来,如果有更好的办法,欢迎讨论指正。在阅读本博客前,请参阅我之前的《iOS与H5交互》。
关于下面问题一,将JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]放到webViewDidFinishLoad方法中可避免出现类似的bug。这里感谢 @有棱角的圆 同学的热情指正,关于这部分的具体代码请移步http://www.cnblogs.com/sunjianfei/p/6559396.html。
一、问题一:在webView中加载H5界面,webView中的H5一级界面可以轻松实现oc与js方法互调,但如果在H5界面上进入二级界面,二级界面中再使用之前方法来交互就会失效。如图:左图为H5一级界面,右图为二级界面。
解决办法:
第一步:在控制器中声明两个变量,isNotFirstLoad用来记录webView是否是第一次加载网页;loadCount计数器,用来记录网页转跳次数,做返回处理。
第二步:实现- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType方法,代码如下:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// isNotFirstLoad,记录webView是否第一次加载H5页面
if (isNotFirstLoad) {
CGRect frame = self.webView.frame;
[self.webView removeFromSuperview];
[self.animationView removeFromSuperview];
UIWebView *webView = [[UIWebView alloc] initWithFrame:frame];
webView.delegate = self;
[self.view addSubview:webView];
[webView loadRequest:request];
self.webView = webView;
//首先创建JSContext 对象(此处通过当前webView的键获取到jscontext)
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//创建JSTestObjext对象,赋值给js的对象
JSTestObjext *test = [JSTestObjext new];
test.delegate = self;
context[@"iOS"] = test;
isNotFirstLoad = NO;
return NO;
}
isNotFirstLoad = YES;
// 计数器,用来记录网页转跳次数,做返回处理
loadCount ++;
if (loadCount == 3) {
loadCount = 1;
}
return YES;
}
在网页转跳二级界面的时候重新创建UIWebView和JSContext对象,将其当成一个新的网页,再使用JSContext对象来实现交互的时候就不会出现失效的情况。
第三步:此时在H5二级界面互调方法就不会有问题了,但新的问题又出现了,当点击返回按钮的时候如何返回上级界面。这时就要用到申明的loadCount成员变量了。具体代码写在自定义返回按钮的点击事件中,我在项目中导航栏是自定义的,重写返回按钮只需重写导航栏的leftBarButtonItem。代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = self.webTitle;
// 设置导航栏返回按钮
self.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithIcon:@"nav_menu_back_02" highlighted:@"nav_menu_back_03" target:self action:@selector(backClick)];
[self createUI];
}
返回按钮点击事件代码如下:
/**
* 返回按钮点击事件
*/
- (void)backClick
{
if (loadCount == 1) { // pop到上级VC
[self.navigationController popViewControllerAnimated:YES];
}else{ // 如果计数器为2,重新加载一级界面的url
NSURL *url = [NSURL URLWithString:self.url];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}
}
到此这个问题算是解决了。
二、问题二:当H5界面中嵌套视频,在用手机横屏播放视频,点击右上角完成按钮退出播放界面的时候,会出现导航栏上移,与状态栏重合的bug。如图:
左图为正常进入H5界面的样子,点击视频播放按钮,进入视频播放界面,打开手机的竖排方向锁定,在手机横屏时候播放器会自动横屏播放,这时点击播放起左上角完成按钮活着右下角全屏按钮退出播放界面就会出现右图的bug,导航栏会向上移动,与状态栏重合。
解决方法:
第一步:在AppDelegate.h中增加一个属性值,用来设置是否允许横屏。代码如下:
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
/*** 是否允许横屏的标记 */
@property (nonatomic,assign)BOOL allowRotation;
@end
在AppDelegate.m中实现- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window方法,具体代码如下:
/**
* 是否允许横屏
*/
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window{
if (self.allowRotation) {
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationMaskPortrait;
}
第二步:在加载webView的控制器中注册两个通知,通过监听UIWindow是否可见来判断视频播放器是否出现。在viewDidLoad中注册通知,见代码:
// 播放视频,监听视频播放器
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(begainFullScreen) name:UIWindowDidBecomeVisibleNotification object:nil];//进入全屏
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endFullScreen) name:UIWindowDidBecomeHiddenNotification object:nil];//退出全屏
实现通知方法:
- (void)begainFullScreen
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.allowRotation = YES;
}
/**
* 退出全屏
*/
- (void)endFullScreen
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.allowRotation = NO;
// 设置设备方向为竖排
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = UIInterfaceOrientationPortrait;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
获取appDelegate需要引入头文件#import "AppDelegate.h"。这样就可以避免导航栏上移出现的bug。