加载页面后无法在 UIWebview 中更改 UserAgent
Can't change UserAgent in UIWebview after loading page
我的 IOS 应用程序中有几个 UIWebView。有时,用户可能需要从移动站点更改为任何给定 URL 的桌面站点。我正在使用欺骗通过更改 UserAgent 来做到这一点:
+ (void) startSpoofingUserAgent
{
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"UserAgent" : @"Desktop" }];
[[NSUserDefaults standardUserDefaults] synchronize];
}
我还有一个杀毒方法:
+ (void)stopSpoofingUserAgent
{
NSDictionary *registeredDefaults = [[NSUserDefaults standardUserDefaults] volatileDomainForName:NSRegistrationDomain];
if ([registeredDefaults objectForKey:@"UserAgent"] != nil)
{
NSMutableDictionary *mutableCopy = [NSMutableDictionary dictionaryWithDictionary:registeredDefaults];
[mutableCopy removeObjectForKey:@"UserAgent"];
[[NSUserDefaults standardUserDefaults] setVolatileDomain:mutableCopy forName:NSRegistrationDomain];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
然后我让用户能够通过一个按钮从桌面切换到移动以及从移动切换到桌面。然后我用这个代码重新加载 URL:
self.webView.delegate = self;
self.webView.scalesPageToFit = YES;
self.webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
//Create a URL object.
NSURL *url = [NSURL URLWithString:self.convertedURL];
//URL Request Object
if(self.defaultToDesktopSite)
{
[MINNetworkingUtils startSpoofingUserAgent];
}
else
{
[MINNetworkingUtils stopSpoofingUserAgent];
}
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//Load the request in the UIWebView.
[self.webView loadRequest:requestObj];
我还缓冲了用户拥有 UIWebView 的当前状态,以便它保留跨实例的设置。应用程序第一次运行时,无论它处于哪种状态(桌面或移动),它都能很好地运行。
问题:
问题是我无法更改状态并在初始加载后将其反映在应用程序中。
场景 1:
- 用户运行应用程序
- UIWebView 设置为欺骗 UserAgent,从而强制 UIWebView 加载桌面站点
- 效果很好!
场景 2:
- 用户运行应用程序
- UIWebView 设置为不欺骗 UserAgent,从而让 UIWebView 加载可用的移动网站
- 效果很好!
场景 3:
- 用户运行应用程序
- UIWebView 被设置为欺骗 UserAgent
- 网站出现桌面网站(如预期)
- 用户通过选择按钮
将状态更改为"Mobile"
- 该应用程序使用上面的代码重新加载 UIWebView(它不欺骗连接)
- 不起作用
- UIWebView 重新加载,但它仍在使用 "Desktop" 站点而不是移动站点
- (如果颠倒过来,这个场景的工作方式相同。换句话说,如果 UIWebView 第一次应该以 "Mobile" 的形式出现。它一直有效,直到我尝试更改它并重新加载它。)
场景 4:
- 执行与方案 3 相同的步骤,但从内存中卸载应用程序并重新加载应用程序
- 由于我记录了状态,所以当应用程序启动时,UIWebView 认为它应该以 "Mobile" 模式启动(因为用户在上次 运行 应用程序时更改了它) .
- 它再次起作用了! UIWebView 以 "Mobile" 模式出现。
似乎正在发生的事情是,UIWebView 正在某处缓冲某些内容,不允许我使用不同的欺骗重新加载 UIWebView。如果卸载并重新加载应用程序,UIWebView 将按预期工作。
我尝试了以下方法来强制 UIWebView 不缓冲任何内容,但没有成功:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
... reload URL
self.convertedPageURL = [NSString stringWithFormat:@"%@?t=%@", self.convertedPageURL, randomString];
... reload URL
就像我说的,如果不卸载应用程序并重新加载它,我无法让 UIWebView 从桌面到移动或移动到桌面更改。
求助!!!
编辑
这是我用来获取当前值的代码。一旦我调用了这行代码,我就不能再更改 UserAgent:
defaultUserAgentString = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
UserAgent
user-defaults 密钥是私有的 API。因此,它没有保证的行为。所有答案都将是经验性的。
话虽如此,听起来正确的解决方案是在每次更改后终止并重新创建 UIWebView
。 UIView
不符合 NSCopying
因此您需要手动执行一些操作,例如:
UIWebView *newWebView = [[UIWebView alloc] initWithFrame:self.webView.frame];
newWebView.delegate = self;
newWebView.scalesPageToFit = YES;
// ... and the autoreseizing mask, etc — no need to do this upon every load
[self.webView.superview insertSubview:newWebView aboveSubview:self.webView];
[self.webView removeFromSuperview];
self.webView = newWebView;
如果这不起作用,您可能需要切换到已记录的设置用户代理的方法。我建议实施您自己的 NSURLProtocol
处理程序,该处理程序接受尚未看到的 http
和 https
请求,修改 header 字段并再次发送它们,即时间拒绝接受他们,以便他们下降到系统。这会很麻烦,但它允许您更改该字段或任何其他 header 字段,可靠且立即生效。
有关示例,请参阅 kifi's engineering blog。
我的 IOS 应用程序中有几个 UIWebView。有时,用户可能需要从移动站点更改为任何给定 URL 的桌面站点。我正在使用欺骗通过更改 UserAgent 来做到这一点:
+ (void) startSpoofingUserAgent
{
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"UserAgent" : @"Desktop" }];
[[NSUserDefaults standardUserDefaults] synchronize];
}
我还有一个杀毒方法:
+ (void)stopSpoofingUserAgent
{
NSDictionary *registeredDefaults = [[NSUserDefaults standardUserDefaults] volatileDomainForName:NSRegistrationDomain];
if ([registeredDefaults objectForKey:@"UserAgent"] != nil)
{
NSMutableDictionary *mutableCopy = [NSMutableDictionary dictionaryWithDictionary:registeredDefaults];
[mutableCopy removeObjectForKey:@"UserAgent"];
[[NSUserDefaults standardUserDefaults] setVolatileDomain:mutableCopy forName:NSRegistrationDomain];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
然后我让用户能够通过一个按钮从桌面切换到移动以及从移动切换到桌面。然后我用这个代码重新加载 URL:
self.webView.delegate = self;
self.webView.scalesPageToFit = YES;
self.webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
//Create a URL object.
NSURL *url = [NSURL URLWithString:self.convertedURL];
//URL Request Object
if(self.defaultToDesktopSite)
{
[MINNetworkingUtils startSpoofingUserAgent];
}
else
{
[MINNetworkingUtils stopSpoofingUserAgent];
}
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//Load the request in the UIWebView.
[self.webView loadRequest:requestObj];
我还缓冲了用户拥有 UIWebView 的当前状态,以便它保留跨实例的设置。应用程序第一次运行时,无论它处于哪种状态(桌面或移动),它都能很好地运行。
问题: 问题是我无法更改状态并在初始加载后将其反映在应用程序中。
场景 1:
- 用户运行应用程序
- UIWebView 设置为欺骗 UserAgent,从而强制 UIWebView 加载桌面站点
- 效果很好!
场景 2:
- 用户运行应用程序
- UIWebView 设置为不欺骗 UserAgent,从而让 UIWebView 加载可用的移动网站
- 效果很好!
场景 3:
- 用户运行应用程序
- UIWebView 被设置为欺骗 UserAgent
- 网站出现桌面网站(如预期)
- 用户通过选择按钮 将状态更改为"Mobile"
- 该应用程序使用上面的代码重新加载 UIWebView(它不欺骗连接)
- 不起作用
- UIWebView 重新加载,但它仍在使用 "Desktop" 站点而不是移动站点
- (如果颠倒过来,这个场景的工作方式相同。换句话说,如果 UIWebView 第一次应该以 "Mobile" 的形式出现。它一直有效,直到我尝试更改它并重新加载它。)
场景 4:
- 执行与方案 3 相同的步骤,但从内存中卸载应用程序并重新加载应用程序
- 由于我记录了状态,所以当应用程序启动时,UIWebView 认为它应该以 "Mobile" 模式启动(因为用户在上次 运行 应用程序时更改了它) .
- 它再次起作用了! UIWebView 以 "Mobile" 模式出现。
似乎正在发生的事情是,UIWebView 正在某处缓冲某些内容,不允许我使用不同的欺骗重新加载 UIWebView。如果卸载并重新加载应用程序,UIWebView 将按预期工作。
我尝试了以下方法来强制 UIWebView 不缓冲任何内容,但没有成功:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
... reload URL
self.convertedPageURL = [NSString stringWithFormat:@"%@?t=%@", self.convertedPageURL, randomString];
... reload URL
就像我说的,如果不卸载应用程序并重新加载它,我无法让 UIWebView 从桌面到移动或移动到桌面更改。
求助!!!
编辑
这是我用来获取当前值的代码。一旦我调用了这行代码,我就不能再更改 UserAgent:
defaultUserAgentString = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
UserAgent
user-defaults 密钥是私有的 API。因此,它没有保证的行为。所有答案都将是经验性的。
话虽如此,听起来正确的解决方案是在每次更改后终止并重新创建 UIWebView
。 UIView
不符合 NSCopying
因此您需要手动执行一些操作,例如:
UIWebView *newWebView = [[UIWebView alloc] initWithFrame:self.webView.frame];
newWebView.delegate = self;
newWebView.scalesPageToFit = YES;
// ... and the autoreseizing mask, etc — no need to do this upon every load
[self.webView.superview insertSubview:newWebView aboveSubview:self.webView];
[self.webView removeFromSuperview];
self.webView = newWebView;
如果这不起作用,您可能需要切换到已记录的设置用户代理的方法。我建议实施您自己的 NSURLProtocol
处理程序,该处理程序接受尚未看到的 http
和 https
请求,修改 header 字段并再次发送它们,即时间拒绝接受他们,以便他们下降到系统。这会很麻烦,但它允许您更改该字段或任何其他 header 字段,可靠且立即生效。
有关示例,请参阅 kifi's engineering blog。