在iOS中更改JavaScript提醒对话框标题
是否有可能在iPhone中的UIWebview
更改JavaScript警报标题?
基本上你不能改变标题,但最近我find了一个方法来显示一个没有标题的警告对话框。
var iframe = document.createElement("IFRAME"); iframe.setAttribute("src", 'data:text/plain,'); document.documentElement.appendChild(iframe); window.frames[0].window.alert('hello'); iframe.parentNode.removeChild(iframe);
我提出了三个单独的解决scheme, 生产代码的最佳解决scheme是由@Martin H.描述的。我唯一的补充是一个具体的例子,展示了如何实现它。
我的其他解决scheme取决于UIWebView的未logging行为,但不需要对内容/ JS进行任何更改。
解决scheme1 :下面是一个具体的例子,演示了@Martin H提出的技术。这可能是最好的解决scheme,因为它不依赖于UIWebView
/ UIAlertView
未logging的行为。
@interface TSViewController () <UIWebViewDelegate> @end @implementation TSViewController - (UIWebView*) webView { return (UIWebView*) self.view; } - (void) loadView { UIWebView* wv = [[UIWebView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)]; wv.delegate = self; wv.scalesPageToFit = YES; self.view = wv; } - (void) viewDidLoad { [super viewDidLoad]; [self.webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.craigslist.org"]]]; } - (void) webViewDidFinishLoad: (UIWebView *) webView { // inject some js to re-map window.alert() // ideally just do this in the html/js itself if you have control of that. NSString* js = @"window.alert = function(message) { window.location = \"http://action/alert?message=\" + message; }"; [webView stringByEvaluatingJavaScriptFromString: js]; // trigger an alert. for demonstration only: [webView stringByEvaluatingJavaScriptFromString: @"alert('hello, world');" ]; } - (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSURL* url = request.URL; // look for our custom action to come through: if ( [url.host isEqualToString: @"action"] && [url.path isEqualToString: @"/alert"] ) { // parse out the message NSString* message = [[[[url query] componentsSeparatedByString: @"="] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; // show our alert UIAlertView* av = [[UIAlertView alloc] initWithTitle: @"My Custom Title" message: message delegate: nil cancelButtonTitle: @"OK" otherButtonTitles: nil]; [av show]; return NO; } return YES; }
解决scheme2:首先,让我说我永远不会在生产代码中亲自使用以下解决scheme。 实现这个function的最好方法就是去做@Martin H在他的回答中提出的build议,或者如果一个空的标题实际上是我们所期望的,那么@twk可能会提出什么build议。 如果您无法控制Web内容本身,也许可以在加载内容后注入@Martin H的代码。
也就是说,如果我们做一些假设,这是可以实现的。 首先,JavaScript alert()
方法确实映射到一个真正的UIAlertView
。 (实际上它确实!)其次,我们可以想出一些机制来从其他应用程序源的UIAlertViews
识别一个alert()
源的UIAlertViews
。 (我们可以!)
我的方法是调整UIAlertView
。 我知道 – 滚滚su and,有种种缺点 。 如果我可以帮忙的话,我不会在生产应用程序中混合使用。 但是对于这个科学项目,我们要调整UIAlertView setDelegate:
方法。
我发现的是1) UIWebView
将构造一个UIAlertView
来显示javascript警报,并且2) UIAlertView
标题在UIAlertView delegate
被设置之前被设置。 通过UIAlertView setDelegate:
用我自己的方法,我可以完成两件事情:1)我可以确定代表一个UIWebView
委托UIAlertView
(只是检查代表…)和2)我有机会重新设置显示alertview之前的标题。
你可能会问: “为什么不只是UIAlertview -show
方法? 这将是伟大的,但在我的实验中,我发现UIWebView从未调用show
。 这一定是在调用其他的内部方法,而我没有进一步调查。
我的解决scheme在UIAlertView上实现了一个类,它添加了一些类方法。 基本上你用这些方法注册一个UIWebView,并提供一个replace标题来使用。 或者,您可以提供一个callback块,在调用时返回一个标题。
我使用iOS6的NSMapTable
类来保存一组UIWebView到Title的映射,其中UIWebView是一个弱键。 这样,我永远不必注销我的UIWebView
,一切都很好地清理。 因此,这个当前的实现是仅iOS6。
下面是在基本视图控制器中使用的代码:
#import <objc/runtime.h> typedef NSString*(^TSAlertTitleBlock)(UIWebView* webView, NSString* defaultTitle ); @interface UIAlertView (TS) @end @implementation UIAlertView (TS) NSMapTable* g_webViewMapTable; + (void) registerWebView: (UIWebView*) webView alertTitleStringOrBlock: (id) stringOrBlock { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ g_webViewMapTable = [NSMapTable weakToStrongObjectsMapTable]; // $wizzle Method originalMethod = class_getInstanceMethod(self, @selector(setDelegate:)); Method overrideMethod = class_getInstanceMethod(self, @selector(setDelegate_ts:)); method_exchangeImplementations(originalMethod, overrideMethod); }); [g_webViewMapTable setObject: [stringOrBlock copy] forKey: webView]; } + (void) registerWebView: (UIWebView*) webView alertTitle: (NSString*) title { [self registerWebView: webView alertTitleStringOrBlock: title]; } + (void) registerWebView: (UIWebView*) webView alertTitleBlock: (TSAlertTitleBlock) alertTitleBlock { [self registerWebView: webView alertTitleStringOrBlock: alertTitleBlock]; } - (void) setDelegate_ts: (id) delegate { // call the original implementation [self setDelegate_ts: delegate]; // oooh - is a UIWebView asking for this UIAlertView???? if ( [delegate isKindOfClass: [UIWebView class]] ) { // see if we've registered a title/title-block for ( UIWebView* wv in g_webViewMapTable.keyEnumerator ) { if ( wv != delegate) continue; id stringOrBlock = [g_webViewMapTable objectForKey: wv]; if ( [stringOrBlock isKindOfClass: NSClassFromString( @"NSBlock" )] ) { stringOrBlock = ((TSAlertTitleBlock)stringOrBlock)( wv, self.title ); } NSParameterAssert( [stringOrBlock isKindOfClass: [NSString class]] ); [self setTitle: stringOrBlock]; break; } } } @end @interface TSViewController () <UIWebViewDelegate> @end @implementation TSViewController - (UIWebView*) webView { return (UIWebView*) self.view; } - (void) loadView { UIWebView* wv = [[UIWebView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)]; wv.delegate = self; wv.scalesPageToFit = YES; self.view = wv; } - (void) viewDidLoad { [super viewDidLoad]; // method 1: bind a title to a webview: [UIAlertView registerWebView: self.webView alertTitle: nil]; /* // method 2: return a title each time it's needed: [UIAlertView registerWebView: self.webView alertTitleBlock: ^NSString *(UIWebView *webView, NSString* defaultTitle) { return @"Custom Title"; }]; */ [self.webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.craigslist.org"]]]; } - (void) webViewDidFinishLoad: (UIWebView *) webView { // trigger an alert [webView stringByEvaluatingJavaScriptFromString: @"alert('hello, world');" ]; } @end
解决scheme3 :经过反思,这里有一个比我最初在解决scheme2中描述的更简单的技术。它仍然依赖于未经证实的事实,即使用UIAlertView实现了javascript警报,并将其委托设置为采购UIWebView。 对于这个解决scheme,简单的子类UIWebView和UIAlertView willPresentAlertView:实现自己的委托方法,当它被调用时,将标题重置为任何你想要的。
@interface TSWebView : UIWebView @end @implementation TSWebView - (void) willPresentAlertView:(UIAlertView *)alertView { if ( [self.superclass instancesRespondToSelector: @selector( willPresentAlertView:) ]) { [super performSelector: @selector( willPresentAlertView:) withObject: alertView]; } alertView.title = @"My Custom Title"; } @end @interface TSViewController () <UIWebViewDelegate> @end @implementation TSViewController - (UIWebView*) webView { return (UIWebView*) self.view; } - (void) loadView { UIWebView* wv = [[TSWebView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)]; wv.delegate = self; wv.scalesPageToFit = YES; self.view = wv; } - (void) viewDidLoad { [super viewDidLoad]; [self.webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.craigslist.org"]]]; } - (void) webViewDidFinishLoad: (UIWebView *) webView { // trigger an alert [webView stringByEvaluatingJavaScriptFromString: @"alert('hello, world');" ]; } @end
不,你不能在Javascript的警报。
但是,如果Javascript是你的,那么不用调用警报,而是调用一个调用Objective C并调用iOS本机警报的函数。
如果Javascript不是你的,那么使用UIWebView,你可以注入一些Javascript来覆盖默认行为,并将其更改为调用iOS本机警报,例如
window.alert = function(message) { window.location = "myScheme://" + message" };
然后查找myScheme并在UIWebView的shouldStartLoadWithRequest中提取消息
这里是从Javascript调用Objective-C的标准方法
如何从Javascript调用Objective-C?
Phonegap / Cordova提供了一个navigator.notification.alert函数。 它允许指定一个自定义标题。
只要为你的UIWebView做一个类,并使其中的willPresentAlertView(alertView:UIAlertView)函数。 之后设置alertView.title = "your fav title"
,它完成了。
import UIKit class webviewClass: UIWebView { func willPresentAlertView(alertView: UIAlertView) { alertView.title = "my custum title" } }
之后,当有提醒或确认…显示,您的自定义标题将显示。
写了什么,我简单地覆盖了警报function。 在ios7上很好用。
function alert(words){ var iframe = document.createElement("IFRAME"); iframe.setAttribute("src", 'data:text/plain,'); document.documentElement.appendChild(iframe); window.frames[0].window.alert(words); iframe.parentNode.removeChild(iframe); }
使用cordova-plugin-dialogs。 它允许创build本机对话框。
下面的代码将创build本机警报:
navigator.notification.alert(message, alertCallback, [title], [buttonName])