UIWebview
是否可以在 iPhone中更改 JavaScript 警报标题?
7 回答
基本上你不能更改标题,但最近我找到了一种显示没有标题的警报对话框的方法。
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);
我针对这个问题提出了三种不同的解决方案。生产代码的最佳解决方案是@Martin H 描述的解决方案。我对此的唯一补充是一个具体示例,展示了如何实现它。
我的其他解决方案依赖于 UIWebView 的未记录行为,但不需要对内容/JS 进行任何更改。
解决方案 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;
}
解决方案2:首先,让我说我永远不会在生产代码中亲自使用以下解决方案。 实现此功能的最佳方法是执行@Martin H 在他的回答中建议的操作,或者如果实际上需要一个空标题,则可能执行@twk 的建议。如果您无法控制 Web 内容本身,也许您可以在内容加载后注入 @Martin H 的代码。
也就是说,如果我们做出一些假设,这是可以实现的。首先,Javascriptalert()
方法确实映射到一个真实的UIAlertView
. (事实上确实如此!)其次,我们可以提出一些机制来区分alert()
-sourcedUIAlertView
和其他 application-sourced UIAlertViews
。(我们可以!)
我的方法是调配UIAlertView
. 我知道——调酒糟透了,有各种各样的缺点。如果我能提供帮助,我不会在生产应用程序中调皮捣蛋。但是对于这个科学项目,我们将混合UIAlertView setDelegate:
方法。
我发现 1)UIWebView
将构造一个UIAlertView
来显示 javascript 警报,以及 2) 在UIAlertView
设置 UIAlertView 之前设置标题delegate
。通过UIAlertView setDelegate:
使用我自己的方法,我可以完成两件事:1)我可以确定UIAlertView
正在代表 a 委托UIWebView
(只需检查委托......)和 2)我有机会在 alertview 之前重新设置标题显示。
您可能会问“为什么不只是调酒UIAlertview -show
法?” 那会很棒,但在我的实验中,我发现 UIWebView 从未调用过show
. 它一定是在调用其他一些内部方法,我没有进一步调查。
我的解决方案在 UIAlertView 上实现了一个类别,它添加了几个类方法。基本上,您使用这些方法注册 UIWebView,并提供要使用的替换标题。或者,您可以提供一个回调块,在调用时返回一个标题。
我使用 iOS6NSMapTable
类来保留一组 UIWebView 到标题的映射,其中 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
解决方案 3:经过反思,这里有一个比我最初在解决方案 2 中描述的更简单的技术。它仍然取决于未记录的事实,即 javascript 警报是使用 UIAlertView 实现的,该 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
不,您不能在 Javascript 警报中执行此操作。
但是,如果 Javascript 是您的,那么您可以调用一个调用 Objective C 的函数并调用 iOS 本机警报,而不是调用警报。
如果 Javascript 不是您的,那么使用 UIWebView 您可以注入一些 Javascript 来覆盖默认行为并将其更改为调用 iOS 本机警报,即类似这样
window.alert = function(message) {
window.location = "myScheme://" + message"
};
然后在 UIWebView 的 shouldStartLoadWithRequest 中查找 myScheme 并提取消息
这是从 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"
}
}
之后,当出现警报或确认并...显示时,您的自定义标题将显示。
离开@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);
}
使用科尔多瓦插件对话框。它允许创建本机对话框。
下面的代码将创建本机警报:
navigator.notification.alert(message, alertCallback, [title], [buttonName])