在iOS中更改JavaScript警报对话框标题

VK.*_*Dev 35 javascript uiwebview ios

是否可以UIWebview在iPhone中更改JavaScript警报标题?

twk*_*twk 57

基本上你不能改变标题,但最近我找到了一种方法来显示一个没有标题的警告对话框.

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);
Run Code Online (Sandbox Code Playgroud)

  • 在ios safari的浏览器中运行良好...虽然不太好. (2认同)

Tom*_*ift 15

我提出了三个解决这个问题的独立方案.生产代码的最佳解决方案是@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;
}
Run Code Online (Sandbox Code Playgroud)

解决方案2:首先,我要说我永远不会在生产代码中亲自使用以下解决方案. 实现此功能的最佳方法是执行@Martin H在其答案中建议的内容,或者@twk建议如果空标题实际上是所需的内容.如果您无法控制Web内容本身,也许您可​​以在加载内容后注入@Martin H的代码.

也就是说,如果我们做出一些假设,这是可以实现的.首先,Javascript alert()方法确实映射到真实UIAlertView.(事实上​​确实如此!)其次,我们可以提出一些机制来识别来自其他应用程序源的alert()-sourced .(我们可以!)UIAlertViewUIAlertViews

我的方法是调整UIAlertView.我知道 - 调情糟透了,并且有各种各样的缺点.如果我能提供帮助,我不会在生产应用程序中徘徊.但对于这个科学项目,我们将调整这种UIAlertView setDelegate:方法.

我发现1)UIWebViewwill构造一个UIAlertView显示javascript警报,2)UIAlertView标题在UIAlertView设置之前delegate设置.通过UIAlertView setDelegate:我自己的方法调配我可以完成两件事:1)我可以确定UIAlertView代表a委托UIWebView(只是检查代表...)和2)我有机会在alertview之前重新设置标题显示.

你可能会问"为什么不只是混合UIAlertview -show方法?" 那会很棒,但在我的实验中我发现UIWebView从未被调用过show.它必须调用其他一些内部方法,我没有进一步调查.

我的解决方案在UIAlertView上实现了一个类别,它添加了几个类方法.基本上,您使用这些方法注册UIWebView,并提供要使用的替换标题.或者,您可以提供一个回调块,在调用时返回标题.

我使用iOS6 NSMapTable类来保存一组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
Run Code Online (Sandbox Code Playgroud)

解决方案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
Run Code Online (Sandbox Code Playgroud)


Gru*_*kes 10

不,你不能在Javascript警报中这样做.

但是如果Javascript是你的,那么你可以调用一个调用Objective C并调用iOS本机警报的函数,而不是调用警报.

如果Javascript不是你的,那么使用UIWebView你可以注入一些Javascript来覆盖默认行为并将其更改为调用iOS本机警报,即这样的事情

window.alert = function(message) {
  window.location = "myScheme://" + message"
};
Run Code Online (Sandbox Code Playgroud)

然后在UIWebView的shouldStartLoadWithRequest中查找myScheme并解压缩消息

这是从Javascript调用Objective-C的标准方法

如何从Javascript调用Objective-C?