Modifier le titre de la boîte de dialogue D'alerte JavaScript dans iOS

Est-il possible de changer le titre de L'alerte JavaScript dans un UIWebview dans iPhone?

35
demandé sur Robert Karl 2012-05-21 11:55:10

7 réponses

Fondamentalement, vous ne pouvez pas changer le titre, mais récemment, j'ai trouvé un moyen d'afficher une boîte de dialogue d'alerte Sans Titre.

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);
57
répondu twk 2013-01-28 15:13:46

Je présente trois solutions distinctes à ce problème. La meilleure solution pour le code de production est celle décrite par @Martin H. mon seul ajout à cela est un exemple concret montrant comment l'implémenter.

Mes autres solutions dépendent des comportements non documentés de UIWebView, mais ne nécessitent aucune modification du contenu/JS.

Solution 1 : voici un exemple concret démontrant la technique avancée par @ Martin H. C'est probablement la meilleure solution car elle ne repose pas sur UIWebView / UIAlertView comportements non documentés.

@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: tout d'abord, permettez-moi de dire que je n'utiliserais jamais personnellement la solution suivante dans le code de production. la meilleure façon d'atteindre cette fonctionnalité est de faire ce que @ Martin H suggère dans sa réponse, ou peut-être ce que @twk suggère si un titre vide est réellement ce qui est désiré. Si vous n'avez pas le contrôle sur le contenu Web lui-même, vous pourriez peut-être injecter le code de @Martin H après que le contenu a été charger.

Cela dit, c'est autrement réalisable si nous faisons quelques hypothèses. Tout d'abord, que la méthode Javascript alert() correspond en effet à un vrai UIAlertView. (En fait il ne!) Deuxièmement, que nous pouvons trouver un mécanisme pour discerner un alert()-sourced UIAlertView à partir d'autres UIAlertViews sources d'application. (On peut!)

Mon approche est de balancer le UIAlertView. Je sais - swizzling suce et a toutes sortes d'inconvénients. Je ne swizzle pas dans les applications de production si je peux l'aider. Mais pour ce projet scientifique, nous allons balayer la méthode UIAlertView setDelegate:.

Ce que j'ai trouvé, c'est que 1) le UIWebView construire une UIAlertView pour afficher l'alerte javascript, et 2) UIAlertView titre est fixée avant la UIAlertView delegate est définie. En swizzling UIAlertView setDelegate: avec ma propre méthode, je peux accomplir deux choses: 1) je peux déterminer que le UIAlertView est commandé au nom d'un UIWebView (Il suffit d'inspecter le délégué...) et 2) j'ai l'occasion de re-définir le titre avant que l'alertview ne soit illustré.

, Vous pourriez demander "Pourquoi ne pas simplement swizzle UIAlertview -show méthode?" Ce serait génial, mais dans mon expérimentation, j'ai trouvé que L'UIWebView n'a jamais invoqué show. Il doit appeler une autre méthode interne, et je n'ai pas enquêté plus loin.

Ma solution implémente une catégorie sur UIAlertView, qui ajoute quelques méthodes de classe. Fondamentalement, vous enregistrez un UIWebView avec ces méthodes, et fournissez un titre de remplacement à utiliser. Vous pouvez également fournir un bloc de rappel qui renvoie un titre lorsqu'il est invoqué.

J'ai utilisé la classe iOS6 NSMapTable pour conserver un ensemble de mappages UIWebView-to-Title, où UIWebView est une clé faible. De cette façon, je n'ai jamais besoin de désinscrire mon UIWebView et tout est bien nettoyé. ainsi, cette implémentation actuelle est uniquement iOS6.

Voici le code, affiché en cours d'utilisation dans un contrôleur de vue de base:

#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 : Après réflexion, voici une technique plus simple que moi décrit initialement dans la Solution 2. Cela dépend toujours du fait non documenté que l'alerte javascript est implémentée à l'aide D'un UIAlertView dont le délégué est défini sur le sourcing UIWebView. Pour cette solution, il suffit de sous-classer UIWebView et d'implémenter votre propre méthode de délégué pour UIAlertView willPresentAlertView:, et quand il est appelé, réinitialisez le titre à ce que vous voulez.

@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
15
répondu TomSwift 2017-05-23 11:46:16

Non, vous ne pouvez pas le faire dans une alerte Javascript.

Mais si le Javascript est le vôtre, au lieu d'appeler l'alerte, vous pouvez appeler une fonction qui appelle Objective C et invoquer une alerte native iOS.

Si le Javascript N'est pas le vôtre, en utilisant UIWebView, vous pouvez injecter du Javascript pour remplacer le comportement par défaut et le changer pour appeler une alerte native iOS, c'est-à-dire quelque chose comme ceci

window.alert = function(message) {
  window.location = "myScheme://" + message"
};

Ensuite, recherchez myScheme et extrayez le message dans UIWebView shouldStartLoadWithRequest

Voici la méthode standard pour invoquer Objective-C à partir de Javascript

Comment appeler Objective - C à partir de Javascript?

10
répondu Gruntcakes 2017-05-23 11:53:56

Pour ce que ça vaut, Phonegap / Cordova offre un navigateur .notification.alerte Fonction. Il permet de spécifier un titre personnalisé.

4
répondu Myrne Stol 2013-05-25 17:32:34

Créez simplement une classe pour votre UIWebView et créez la fonction willPresentAlertView (alertView: UIAlertView). Après cela set alertView.title = "your fav title" et c'est fait.

    import UIKit

class webviewClass: UIWebView {

func willPresentAlertView(alertView: UIAlertView) {


    alertView.title = "my custum title"
}

}

Après cela, lorsque jamais une alerte ou confirmer et... montré, votre titre personnalisé sera affiché.

1
répondu ShervinJam 2016-03-01 20:00:35

En sortant de ce que @ twk a écrit, j'écrase simplement la fonction d'alerte. Fonctionne très bien sur 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);
}
0
répondu socca1157 2014-02-25 06:05:05

Utiliser cordova-plugin-les dialogues. Il permet de créer des dialogues natifs.

Le code ci-dessous créera une alerte native:

navigator.notification.alert(message, alertCallback, [title], [buttonName])
0
répondu Aladin 2015-10-05 17:24:54