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);
この問題に対する3つの別々の解決策を提示します。実動コードの最良の解決策は、@ Martin Hによって記述されたものです。これに対する私の唯一の追加は、それを実装する方法を示す具体的な例です。
私の他のソリューションは、UIWebViewの文書化されていない動作に依存していますが、content/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コンテンツ自体を制御できない場合は、コンテンツがロードされた後に@MartinHのコードを挿入できます。
とは言うものの、いくつかの仮定を立てれば、これは他の方法で達成可能です。まず、Javascriptalert()
メソッドが実際に実際にマップされることUIAlertView
。(実際にはそうです!)次に、他のアプリケーションソースからソースを識別するためのメカニズムをalert()
考え出すことができます。(私たちはできる!)UIAlertView
UIAlertViews
私のアプローチは、をスウィズルすることUIAlertView
です。私は知っています-スウィズリングは吸うし、あらゆる種類の欠点があります。私はそれを助けることができれば、本番アプリでスウィズルすることはありません。しかし、この科学プロジェクトでは、メソッドをスウィズルしますUIAlertView setDelegate:
。
私が見つけたのは、1)javascriptアラートを表示するためのUIWebView
を構築し、2) UIAlertViewが設定される前にタイトルが設定されることです。独自の方法でスウィズリングすることで、2つのことを達成できます。1)が(代理人を検査するだけで...)に代わって委託されていることを確認できます。2)アラートビューの前にタイトルを再設定する機会があります。示されています。UIAlertView
UIAlertView
delegate
UIAlertView setDelegate:
UIAlertView
UIWebView
「スウィズルUIAlertview -show
法だけじゃないの?」と聞くかもしれません。 それは素晴らしいことですが、私の実験では、UIWebViewが呼び出されなかったことがわかりましたshow
。それは他の内部メソッドを呼び出しているに違いありません、そして私はそれ以上調査しませんでした。
私のソリューションは、UIAlertViewにカテゴリを実装し、いくつかのクラスメソッドを追加します。基本的に、UIWebViewをこれらのメソッドに登録し、使用する置換タイトルを提供します。または、呼び出されたときにタイトルを返すコールバックブロックを提供することもできます。
iOS6クラスを使用してNSMapTable
、UIWebViewからタイトルへのマッピングのセットを保持しました。UIWebViewは弱鍵です。このようにして、登録を解除する必要がなくなり、UIWebView
すべてが正常にクリーンアップされます。 したがって、この現在の実装はiOS6のみです。
基本的なViewControllerで使用中のコードは次のとおりです。
#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アラートがソーシングUIWebViewにデリゲートが設定されているUIAlertViewを使用して実装されているという文書化されていない事実に依存します。このソリューションでは、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が自分のものである場合は、アラートを呼び出す代わりに、ObjectiveCを呼び出す関数を呼び出してiOSネイティブアラートを呼び出すことができます。
Javascriptが自分のものでない場合は、UIWebViewを使用して、Javascriptを挿入してデフォルトの動作をオーバーライドし、iOSネイティブアラートを呼び出すように変更できます。
window.alert = function(message) {
window.location = "myScheme://" + message"
};
次に、mySchemeを探し、UIWebViewのshouldStartLoadWithRequestでメッセージを抽出します
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);
}
cordova-plugin-dialogsを使用します。ネイティブダイアログを作成できます。
以下のコードはネイティブアラートを作成します:
navigator.notification.alert(message, alertCallback, [title], [buttonName])