之前的博客寫過使用<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。
