WKWebView 不会打开社交媒体登录弹出窗口
WKWebView won't open social media login popups
我的应用程序中有一个 WKWebView。一切都 运行 顺利,除了我显示的网站有社交媒体登录按钮。问题是,他们显示(或尝试显示)一个弹出窗口,您允许它访问您的社交媒体帐户。我对此进行了研究并尝试了一些方法,但 none 似乎合适。这是我尝试过的:
在 viewDidLoad 中,我尝试在 init:
上启用 Javascript
WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
WKPreferences *thePreferences = [[WKPreferences alloc] init];
thePreferences.javaScriptCanOpenWindowsAutomatically = YES;
thePreferences.javaScriptEnabled = YES;
theConfiguration.preferences = thePreferences;
self.wkWeb = [[WKWebView alloc] initWithFrame:screenRect configuration:theConfiguration];
但是,这对我没有帮助。然后我试着和代表一起玩,然后走那条路。我尝试使用 createWebViewWithConfiguration 方法,但这似乎有点矫枉过正,因为我必须过滤掉它们是否在登录 URL 然后配置一个弹出窗口并显示它。然后这仍然没有用。我也来这里是为了 if not main frame logic,取消请求并在主视图中重新加载它,这是一个简单的单行修复,因为它膨胀到 20 多行。
这似乎是一个常见问题,但我似乎找不到很多信息。有什么想法吗?
编辑 - 添加
在玩弄了 Armands 的答案之后,我离得更近了。现在这是我的 createWebViewWithConfig 方法,它只显示一个白色的屏幕覆盖。这就像我需要一些东西来告诉新的弹出窗口加载内容。另外 - 我假设我可以将这个模式 window 放在我当前的 .m 文件中,它不需要一个全新的文件?
NSURL *url = navigationAction.request.URL;
if(!navigationAction.targetFrame.isMainFrame && [url.absoluteString rangeOfString:@"initiate_"].location != NSNotFound) {
//open new modal webview
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.processPool = [appDelegate getSharedPool];
self.popupLogin = [[WKWebView alloc] initWithFrame:self.wkWeb.bounds configuration:config];
self.popupLogin.frame = CGRectMake(self.wkWeb.frame.origin.x,
self.wkWeb.frame.origin.y,
self.wkWeb.frame.size.width,
self.wkWeb.frame.size.height);
self.popupLogin.navigationDelegate = self;
self.popupLogin.UIDelegate = self;
[webView addSubview:self.popupLogin];
[self.popupLogin loadRequest:navigationAction.request];
return nil;
}
那些登录按钮试图打开一个新的选项卡,WKWebView 不支持。据我所知,只有一种方法可以做到这一点。
首先添加WKUIDelegate
方法:
-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
NSURL *url = navigationAction.request.URL;
if (navigationAction.targetFrame == nil && [url.absoluteString rangeOfString:@"facebook.com/dialog"].location != NSNotFound) {
//Open new modal WKWebView
}
return nil;
}
在模式 WKWebView 中添加:
-(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
NSURL *url = navigationResponse.response.URL;
if ([url.absoluteString rangeOfString:@"close_popup.php"].location != NSNotFound) {
//Close viewController
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
close_popup.php
适用于 Facebook。如果您需要支持多个社交网络,请在它们的 URL 中找到一些独特的东西。
为了使其正常工作,您必须在 WKWebView 之间共享 Cookie。为此,您必须使用共享 WKProcessPool
初始化它们。
我更喜欢将它存储在 AppDelegate 中。
WKWebView可以这样创建:
- (void)createWebView
{
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.processPool = delegate.webViewProcessPool;
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
}
在 AppDelegate 中只是延迟加载它:
-(id)webViewProcessPool
{
if (!_webViewProcessPool) {
_webViewProcessPool = [[WKProcessPool alloc] init];
}
return _webViewProcessPool;
}
Armands 的回答是正确的,但可以显着改进。
- 如果您希望 webview 能够相互协作(例如,通过 window.opener)return 新创建的 webview(而不是 nil)很重要
- 如果您使用传递给创建调用的配置,则会自动共享进程池
- 您不需要在 createWebViewWith 调用中进行额外检查。 (如果你想防止任意弹出 windows 或创建特定的 WebViewController 类型,你仍然应该将 URL 列入白名单。)
在您的主 WebViewController 中(webView.uiDelegate = self
):
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
let webView = WKWebView.init(frame: .zero, configuration: configuration)
//OPTIONAL: check navigationAction.request.url to see what site is being opened as a popup
//TODO HERE: Launch new instance of your WebViewController (within a nav controller)
//and pass it this newly created webView to be added as main subview
return webView
}
您仍然希望在弹出 WebViewController 的新实例中拥有关闭监听器(其中 webView.navigationDelegate = self
):
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let url = navigationResponse.response.url, url.absoluteString.hasPrefix("https://example.com/something-unique-to-this-popup-when-it-closes") {
dismiss(animated: true, completion: nil)
}
decisionHandler(.allow)
}
添加以下处理程序,弹出窗口 window 加载正常。
# pragma mark WKUIDelegate
- (nullable WKWebView *)webView:(WKWebView *)webView
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures
(WKWindowFeatures *)windowFeatures
{
if (navigationAction.targetFrame == nil) {
_popupWebViewObject = [[WKWebView alloc] initWithFrame:webView.frame configuration:configuration];
_popupWebViewObject.customUserAgent = webView.customUserAgent;
_popupWebViewObject.UIDelegate = self;
_popupWebViewObject.navigationDelegate = self;
[self.window.contentView addSubview:_popupWebViewObject];
_popupWebViewObject.configuration.preferences.javaScriptCanOpenWindowsAutomatically = YES;
_popupWebViewObject.configuration.preferences.javaScriptEnabled = YES;
_popupWebViewObject.configuration.preferences.plugInsEnabled = YES;
[_popupWebViewObject loadRequest:[navigationAction.request copy]];
return _popupWebViewObject;
}
return nil;
}
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"%s", __FUNCTION__);
NSLog(@"%@ TOKEN : ",webView.URL.absoluteString.lowercaseString);
[self dismissPopWindow:webView];
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
[self dismissPopWindow:webView];
decisionHandler(WKNavigationActionPolicyAllow);
}
Swift4/5 片段 - 使用 MyWebView:扩展 WkWebView:
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
let newView = MyWebView.init(frame: webView.frame, configuration: configuration)
newView.customUserAgent = webView.customUserAgent
newView.navigationDelegate = self
newView.uiDelegate = self
self.view.addSubview(newView)
newView.fit(newView.superview!)
newView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
newView.configuration.preferences.javaScriptEnabled = true
newView.configuration.preferences.plugInsEnabled = true
newView.load(navigationAction.request)
return newView
}
Swift 4 和 Swift 5
创建 WKWebView 的两个实例
var webView : WKWebView!
var newWebviewPopupWindow: WKWebView?
现在实现方法
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
newWebviewPopupWindow = WKWebView(frame: view.bounds, configuration: configuration)
newWebviewPopupWindow!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
newWebviewPopupWindow!.navigationDelegate = self
newWebviewPopupWindow!.uiDelegate = self
view.addSubview(newWebviewPopupWindow!)
return newWebviewPopupWindow!
}
func webViewDidClose(_ webView: WKWebView) {
webView.removeFromSuperview()
newWebviewPopupWindow = nil
}
如果您的代码仍然无法正常工作,请确保 Info.plist 中的此字符串。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
我的应用程序中有一个 WKWebView。一切都 运行 顺利,除了我显示的网站有社交媒体登录按钮。问题是,他们显示(或尝试显示)一个弹出窗口,您允许它访问您的社交媒体帐户。我对此进行了研究并尝试了一些方法,但 none 似乎合适。这是我尝试过的:
在 viewDidLoad 中,我尝试在 init:
上启用 JavascriptWKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
WKPreferences *thePreferences = [[WKPreferences alloc] init];
thePreferences.javaScriptCanOpenWindowsAutomatically = YES;
thePreferences.javaScriptEnabled = YES;
theConfiguration.preferences = thePreferences;
self.wkWeb = [[WKWebView alloc] initWithFrame:screenRect configuration:theConfiguration];
但是,这对我没有帮助。然后我试着和代表一起玩,然后走那条路。我尝试使用 createWebViewWithConfiguration 方法,但这似乎有点矫枉过正,因为我必须过滤掉它们是否在登录 URL 然后配置一个弹出窗口并显示它。然后这仍然没有用。我也来这里是为了 if not main frame logic,取消请求并在主视图中重新加载它,这是一个简单的单行修复,因为它膨胀到 20 多行。
这似乎是一个常见问题,但我似乎找不到很多信息。有什么想法吗?
编辑 - 添加
在玩弄了 Armands 的答案之后,我离得更近了。现在这是我的 createWebViewWithConfig 方法,它只显示一个白色的屏幕覆盖。这就像我需要一些东西来告诉新的弹出窗口加载内容。另外 - 我假设我可以将这个模式 window 放在我当前的 .m 文件中,它不需要一个全新的文件?
NSURL *url = navigationAction.request.URL;
if(!navigationAction.targetFrame.isMainFrame && [url.absoluteString rangeOfString:@"initiate_"].location != NSNotFound) {
//open new modal webview
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.processPool = [appDelegate getSharedPool];
self.popupLogin = [[WKWebView alloc] initWithFrame:self.wkWeb.bounds configuration:config];
self.popupLogin.frame = CGRectMake(self.wkWeb.frame.origin.x,
self.wkWeb.frame.origin.y,
self.wkWeb.frame.size.width,
self.wkWeb.frame.size.height);
self.popupLogin.navigationDelegate = self;
self.popupLogin.UIDelegate = self;
[webView addSubview:self.popupLogin];
[self.popupLogin loadRequest:navigationAction.request];
return nil;
}
那些登录按钮试图打开一个新的选项卡,WKWebView 不支持。据我所知,只有一种方法可以做到这一点。
首先添加WKUIDelegate
方法:
-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
NSURL *url = navigationAction.request.URL;
if (navigationAction.targetFrame == nil && [url.absoluteString rangeOfString:@"facebook.com/dialog"].location != NSNotFound) {
//Open new modal WKWebView
}
return nil;
}
在模式 WKWebView 中添加:
-(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
NSURL *url = navigationResponse.response.URL;
if ([url.absoluteString rangeOfString:@"close_popup.php"].location != NSNotFound) {
//Close viewController
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
close_popup.php
适用于 Facebook。如果您需要支持多个社交网络,请在它们的 URL 中找到一些独特的东西。
为了使其正常工作,您必须在 WKWebView 之间共享 Cookie。为此,您必须使用共享 WKProcessPool
初始化它们。
我更喜欢将它存储在 AppDelegate 中。
WKWebView可以这样创建:
- (void)createWebView
{
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.processPool = delegate.webViewProcessPool;
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
}
在 AppDelegate 中只是延迟加载它:
-(id)webViewProcessPool
{
if (!_webViewProcessPool) {
_webViewProcessPool = [[WKProcessPool alloc] init];
}
return _webViewProcessPool;
}
Armands 的回答是正确的,但可以显着改进。
- 如果您希望 webview 能够相互协作(例如,通过 window.opener)return 新创建的 webview(而不是 nil)很重要
- 如果您使用传递给创建调用的配置,则会自动共享进程池
- 您不需要在 createWebViewWith 调用中进行额外检查。 (如果你想防止任意弹出 windows 或创建特定的 WebViewController 类型,你仍然应该将 URL 列入白名单。)
在您的主 WebViewController 中(webView.uiDelegate = self
):
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
let webView = WKWebView.init(frame: .zero, configuration: configuration)
//OPTIONAL: check navigationAction.request.url to see what site is being opened as a popup
//TODO HERE: Launch new instance of your WebViewController (within a nav controller)
//and pass it this newly created webView to be added as main subview
return webView
}
您仍然希望在弹出 WebViewController 的新实例中拥有关闭监听器(其中 webView.navigationDelegate = self
):
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let url = navigationResponse.response.url, url.absoluteString.hasPrefix("https://example.com/something-unique-to-this-popup-when-it-closes") {
dismiss(animated: true, completion: nil)
}
decisionHandler(.allow)
}
添加以下处理程序,弹出窗口 window 加载正常。
# pragma mark WKUIDelegate
- (nullable WKWebView *)webView:(WKWebView *)webView
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures
(WKWindowFeatures *)windowFeatures
{
if (navigationAction.targetFrame == nil) {
_popupWebViewObject = [[WKWebView alloc] initWithFrame:webView.frame configuration:configuration];
_popupWebViewObject.customUserAgent = webView.customUserAgent;
_popupWebViewObject.UIDelegate = self;
_popupWebViewObject.navigationDelegate = self;
[self.window.contentView addSubview:_popupWebViewObject];
_popupWebViewObject.configuration.preferences.javaScriptCanOpenWindowsAutomatically = YES;
_popupWebViewObject.configuration.preferences.javaScriptEnabled = YES;
_popupWebViewObject.configuration.preferences.plugInsEnabled = YES;
[_popupWebViewObject loadRequest:[navigationAction.request copy]];
return _popupWebViewObject;
}
return nil;
}
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"%s", __FUNCTION__);
NSLog(@"%@ TOKEN : ",webView.URL.absoluteString.lowercaseString);
[self dismissPopWindow:webView];
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
[self dismissPopWindow:webView];
decisionHandler(WKNavigationActionPolicyAllow);
}
Swift4/5 片段 - 使用 MyWebView:扩展 WkWebView:
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
let newView = MyWebView.init(frame: webView.frame, configuration: configuration)
newView.customUserAgent = webView.customUserAgent
newView.navigationDelegate = self
newView.uiDelegate = self
self.view.addSubview(newView)
newView.fit(newView.superview!)
newView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
newView.configuration.preferences.javaScriptEnabled = true
newView.configuration.preferences.plugInsEnabled = true
newView.load(navigationAction.request)
return newView
}
Swift 4 和 Swift 5
创建 WKWebView 的两个实例
var webView : WKWebView!
var newWebviewPopupWindow: WKWebView?
现在实现方法
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
newWebviewPopupWindow = WKWebView(frame: view.bounds, configuration: configuration)
newWebviewPopupWindow!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
newWebviewPopupWindow!.navigationDelegate = self
newWebviewPopupWindow!.uiDelegate = self
view.addSubview(newWebviewPopupWindow!)
return newWebviewPopupWindow!
}
func webViewDidClose(_ webView: WKWebView) {
webView.removeFromSuperview()
newWebviewPopupWindow = nil
}
如果您的代码仍然无法正常工作,请确保 Info.plist 中的此字符串。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>