Можно ли изменить заголовок предупреждения JavaScript в UIWebview на iPhone?
Изменить заголовок диалогового окна JavaScript в iOS
Ответ 1
В принципе вы не можете изменить заголовок, но недавно я нашел способ показать диалоговое окно предупреждения без заголовка.
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);
Ответ 2
Я представляю три отдельных решения этой проблемы. Лучшим решением для производственного кода является тот, который описывается @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 предлагает, если пустой заголовок на самом деле является желательным. Если у вас нет контроля над самим веб-контентом, возможно, вы можете ввести код @Martin H после загрузки содержимого.
Тем не менее, это может быть достигнуто, если мы сделаем некоторые предположения. Во-первых, метод Javascript alert() действительно отображает реальный UIAlertView. (На самом деле это так!) Во-вторых, мы можем придумать какой-то механизм, чтобы различать alert() -сертифицированный UIAlertView из другого приложения UIAlertViews. (Мы можем!)
Мой подход заключается в том, чтобы опробовать UIAlertView. Я знаю - swizzling сосет и имеет всевозможные недостатки. Если я смогу это сделать, я не буду заниматься производственными приложениями. Но для этого научного проекта мы будем подбирать метод UIAlertView setDelegate:.
Я обнаружил, что 1) UIWebView построит UIAlertView, чтобы отобразить предупреждение javascript, и 2) заголовок UIAlertView установлен до установки UIAlertView delegate. Путем swizzling UIAlertView setDelegate: с помощью собственного метода я могу выполнить две вещи: 1) Я могу определить, что UIAlertView вводится в эксплуатацию от имени UIWebView (просто осмотрите делегата...) и 2) у меня есть возможность для повторного набора заголовка до отображения предупреждения.
Вы можете спросить: "Почему бы не просто swizzle UIAlertview -show метод?" Это было бы здорово, но в моих экспериментах я обнаружил, что UIWebView никогда не вызывал show. Он должен вызывать другой внутренний метод, и я еще не исследовал его.
Мое решение реализует категорию в UIAlertView, которая добавляет несколько методов класса. В основном вы регистрируете UIWebView с помощью этих методов и предоставляете заменяющий заголовок для использования. В качестве альтернативы вы можете предоставить блок обратного вызова, который возвращает заголовок при вызове.
Я использовал класс iOS6 NSMapTable, чтобы поддерживать набор отображений UIWebView-to-Title, где 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 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, у которого его делегат установлен на поиск UIWebView. Для этого решения просто подкласс UIWebView и реализовать собственный метод делегирования для UIAlertView willPresentAlertView:, и когда он вызывается, reset заголовок, который вы хотите.
@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
Ответ 3
Нет, вы не можете сделать это в предупреждении Javascript.
Но если Javascript является вашим, тогда вместо вызова предупреждения вы можете вместо этого вызвать функцию, вызывающую Objective C, и вызвать собственное оповещение iOS.
Если Javascript не принадлежит вам, то с помощью UIWebView вы можете вставить некоторый Javascript, чтобы переопределить поведение по умолчанию и изменить его, чтобы вызвать собственное оповещение iOS, например, что-то вроде этого
window.alert = function(message) {
  window.location = "myScheme://" + message"
};
Затем найдите myScheme и извлеките сообщение в UIWebView shouldStartLoadWithRequest
Здесь стандартный метод вызова Objective-C из Javascript
Ответ 4
Для чего это стоит, Phonegap/Cordova предлагает navigator.notification.alert функцию. Он позволяет указать пользовательский заголовок.
Ответ 5
Просто создайте класс для своего UIWebView и создайте в нем функцию willPresentAlertView (alertView: UIAlertView). После этого установите alertView.title = "your fav title" и это будет сделано.
    import UIKit
class webviewClass: UIWebView {
func willPresentAlertView(alertView: UIAlertView) {
    alertView.title = "my custum title"
}
}
После этого, когда появится предупреждение или подтвердится и... показано, будет отображаться ваш пользовательский заголовок.
Ответ 6
Отключившись от того, что написал @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);
}
Ответ 7
Используйте диалоговые окна cordova-plugin. Это позволяет создавать собственные диалоги.
Ниже код создаст собственное предупреждение:
navigator.notification.alert(message, alertCallback, [title], [buttonName])
