在 iOS 中更改 JavaScript 警报对话框标题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10681381/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Change JavaScript alert dialog title in iOS
提问by VK.Dev
Is it possible to change the JavaScript alert title in a UIWebview
in iPhone?
是否可以UIWebview
在 iPhone 中更改 JavaScript 警报标题?
回答by twk
Basically you cannot change the title, but recently I have found a way to show an alert dialog with no title.
基本上你不能改变标题,但最近我找到了一种显示没有标题的警报对话框的方法。
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);
回答by TomSwift
I present three separate solutions to this problem. The best solution for production code is the one described by @Martin H. My only addition to this is a concrete example showing how to implement it.
我针对这个问题提出了三种不同的解决方案。生产代码的最佳解决方案是@Martin H 描述的解决方案。我对此的唯一补充是展示如何实现它的具体示例。
My other solutions depend on undocumented behaviors of UIWebView, but don't require any changes to the content/JS.
我的其他解决方案取决于 UIWebView 的未记录行为,但不需要对内容/JS 进行任何更改。
Solution 1: Here is a concrete example demonstrating the technique put forth by @Martin H. This is likely the best solution as it does not rely on UIWebView
/ UIAlertView
undocumented behaviors.
解决方案 1:这是一个具体示例,展示了 @Martin H 提出的技术。这可能是最好的解决方案,因为它不依赖于UIWebView
/UIAlertView
未记录的行为。
@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;
}
Solution 2: First, let me say that I would never personally use the following solution in production code.The best way to achieve this functionality is to do what @Martin H suggests in his answer, or possibly what @twk suggests if an empty title is actually what is desired. If you don't have control over the web content itself, perhaps you could inject @Martin H's code after the content has been loaded.
解决方案2:首先,让我说我永远不会在生产代码中亲自使用以下解决方案。实现此功能的最佳方法是按照@Martin H 在他的回答中建议的操作,或者如果空标题实际上是所需的,则可能按照@twk 的建议进行操作。如果您无法控制 Web 内容本身,也许您可以在加载内容后注入 @Martin H 的代码。
That said, this is otherwise achievable if we make some assumptions. First, that the Javascript alert()
method indeed maps to a real UIAlertView
. (In fact it does!) Second, that we can come up with some mechanism to discern an alert()
-sourced UIAlertView
from other application-sourced UIAlertViews
. (We can!)
也就是说,如果我们做出一些假设,这是可以实现的。首先,Javascriptalert()
方法确实映射到一个真实的UIAlertView
. (事实上确实如此!)其次,我们可以想出一些机制来区分alert()
-sourcedUIAlertView
和其他 application-sourced UIAlertViews
。(我们可以!)
My approach is to swizzle the UIAlertView
. I know - swizzling sucks and has all sorts of drawbacks. I don't swizzle in production apps if I can help it. But for this science project, we're going to swizzle the UIAlertView setDelegate:
method.
我的方法是将UIAlertView
. 我知道 - swizzling 很烂并且有各种各样的缺点。如果我能帮上忙,我不会在生产应用程序中混为一谈。但是对于这个科学项目,我们将调整UIAlertView setDelegate:
方法。
What I found is that 1) the UIWebView
will construct an UIAlertView
to display the javascript alert, and 2) the UIAlertView
title is set before the UIAlertView delegate
is set. By swizzling UIAlertView setDelegate:
with my own method I can accomplish two things: 1) I can determine that the UIAlertView
is being commissioned on behalf of a UIWebView
(just inspect the delegate...) and 2) I have opportunity to re-set the title before the alertview is shown.
我发现 1)UIWebView
将构造一个UIAlertView
来显示 javascript 警报,以及 2) 在UIAlertView
设置 UIAlertView 之前设置标题delegate
。通过UIAlertView setDelegate:
使用我自己的方法swizzling ,我可以完成两件事:1)我可以确定UIAlertView
正在代表 a 委托UIWebView
(只需检查委托......)和 2)我有机会在警报视图之前重新设置标题显示。
You might ask "Why not just swizzle UIAlertview -show
method?"That would be great, but in my experimenting I found that the UIWebView never invoked show
. It must be calling some other internal method, and I didn't investigate further.
您可能会问“为什么不只是 swizzleUIAlertview -show
方法?” 那会很棒,但在我的实验中,我发现 UIWebView 从未调用show
. 它一定是在调用其他一些内部方法,我没有进一步调查。
My solution implements a category on UIAlertView, which adds a couple of class methods. Basically you register a UIWebView with these methods, and provide a replacement title to be used. Alternatively you can provide a callback block that returns a title when invoked.
我的解决方案在 UIAlertView 上实现了一个类别,它添加了几个类方法。基本上,您使用这些方法注册 UIWebView,并提供要使用的替换标题。或者,您可以提供一个回调块,在调用时返回标题。
I used the iOS6 NSMapTable
class to keep a set of UIWebView-to-Title mappings, where the UIWebView is a weak key. This way I don't ever have to unregister my UIWebView
and everything gets cleaned up nicely. Thus, this current implementation is iOS6-only.
我使用 iOS6NSMapTable
类来保留一组 UIWebView 到标题的映射,其中 UIWebView 是一个弱键。这样我就不必注销我的UIWebView
一切,一切都得到了很好的清理。 因此,此当前实现仅适用于 iOS6。
Here's the code, shown in-use in a basic view controller:
这是代码,显示在基本视图控制器中的使用中:
#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
Solution 3: Upon reflection, here's a simpler technique than I initially described in Solution 2. It still depends on the undocumented fact that the javascript alert is implemented using a UIAlertView that has its delegate set to the sourcing UIWebView. For this solution, simply subclass UIWebView and implement your own delegate method for UIAlertView willPresentAlertView:, and when it is called, reset the title to whatever you wish.
解决方案 3:经过反思,这里有一个比我最初在解决方案 2 中描述的更简单的技术。它仍然依赖于未记录的事实,即使用 UIAlertView 实现 javascript 警报,该 UIAlertView 将其委托设置为源 UIWebView。对于这个解决方案,只需继承 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
回答by Gruntcakes
No you can't do this in a Javascript alert.
不,您不能在 Javascript 警报中执行此操作。
But if the Javascript is yours then instead of calling the alert you could instead call a function that calls Objective C and invoke an iOS native alert.
但是,如果 Javascript 是您的,那么您可以调用一个函数来调用 Objective C 并调用 iOS 本机警报,而不是调用警报。
If the Javascript isn't yours then using UIWebView you can inject some Javascript to override the default behaviour and change it to call an iOS native alert i.e. something like this
如果 Javascript 不是你的,那么使用 UIWebView 你可以注入一些 Javascript 来覆盖默认行为并将其更改为调用 iOS 本机警报,即像这样
window.alert = function(message) {
window.location = "myScheme://" + message"
};
Then look for myScheme and extract message in UIWebView's shouldStartLoadWithRequest
然后在 UIWebView 的 shouldStartLoadWithRequest 中查找 myScheme 并提取消息
Here's the standard method for invoking Objective-C from Javascript
这是从 Javascript 调用 Objective-C 的标准方法
回答by Myrne Stol
For what it's worth, Phonegap/Cordova offers a navigator.notification.alertfunction. It allows specifying a custom title.
就其价值而言,Phonegap/Cordova 提供了navigator.notification.alert功能。它允许指定自定义标题。
回答by ShervinJam
Just make a class for your UIWebView and make willPresentAlertView (alertView: UIAlertView) function in it. After that set alertView.title = "your fav title"
and it is done.
只需为您的 UIWebView 创建一个类并在其中创建 willPresentAlertView (alertView: UIAlertView) 函数。在那之后alertView.title = "your fav title"
就完成了。
import UIKit
class webviewClass: UIWebView {
func willPresentAlertView(alertView: UIAlertView) {
alertView.title = "my custum title"
}
}
After that when ever an alert or confirm and... shown, your custom title will shown.
之后,当出现警报或确认和...时,您的自定义标题将显示。
回答by socca1157
Going off of what @twk wrote, I simply overwrite the alert function. Works great on ios7.
从@twk 写的内容开始,我只是覆盖了警报功能。在 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);
}
回答by Aladin
Use cordova-plugin-dialogs. It allows to create native dialogs.
使用cordova-plugin-dialogs。它允许创建本机对话框。
Below code will create native alert:
下面的代码将创建本机警报:
navigator.notification.alert(message, alertCallback, [title], [buttonName])