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?
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);
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
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
Pour ce que ça vaut, Phonegap / Cordova offre un navigateur .notification.alerte Fonction. Il permet de spécifier un titre personnalisé.
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é.
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);
}
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])