GVKun编程网logo

防止iOS在通话状态栏中推出PhoneGap UIWebView屏幕(苹果通话防误触怎么关闭)

12

想了解防止iOS在通话状态栏中推出PhoneGapUIWebView屏幕的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于苹果通话防误触怎么关闭的相关问题,此外,我们还将为您介绍关于iOSAP

想了解防止iOS在通话状态栏中推出PhoneGap UIWebView屏幕的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于苹果通话防误触怎么关闭的相关问题,此外,我们还将为您介绍关于iOS API - UIWebView.h、ios uiwebview wkwebview注意点小记、iOS UIWebView 和 WKWebView 的 cookie 获取,设置,删除、ios – ARC中的UIWebViewDelegate手册发布?的新知识。

本文目录一览:

防止iOS在通话状态栏中推出PhoneGap UIWebView屏幕(苹果通话防误触怎么关闭)

防止iOS在通话状态栏中推出PhoneGap UIWebView屏幕(苹果通话防误触怎么关闭)

我的问题是这样的:每当一个iPhone用户在通话或正在使用他或她的手机作为热点时,iOS 7的状态栏将被放大,从而将我的PhoneGap应用程序的UIWebView从屏幕底部推开.放大的状态栏称为“通话状态栏”.见下图:

堆栈溢出的答案我已经尝试补救这一点:

Iphone- How to resize view when call status bar is toggled?

How In-Call status bar impacts UIViewController’s view size ? (and how to handle it properly)

此外,Phonegap似乎没有任何类型的事件通知我状态栏的更改.聆听电话间隙“暂停”事件是无用的,如1)it’s known to have quirks in iOS和2)它并没有真正涵盖热点情况.

我的Objective-C技能非常小,我只需要提出这样的问题,然后再提供4小时的谷歌搜索,堆栈溢出,哭泣等等.

堆栈溢出的神,向我发出你的巨大的书呆子愤怒.

解决方法

根据Jef的建议,提出以下解决方案.你想要做的是以下几点:

>观察本机didChangeStatusBarFrame委托
>通过native statusBarFrame获取有关状态栏的大小信息
通过触发通过它的事件,将信息暴露给您的Webview

我已经设置了一个Github repo与你在这个答案中找到的所有代码.

在AppDelegate中设置通知

// Appdelegate.m
- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame
{
  NSMutableDictionary *statusBarChangeInfo = [[NSMutableDictionary alloc] init];
  [statusBarChangeInfo setobject:@"statusbarchange" 
                     forKey:@"frame"];

  [[NSNotificationCenter defaultCenter] postNotificationName:@"statusbarchange" 
                                        object:self 
                                        userInfo:statusBarChangeInfo];
 }

使statusBarChange选项可用

// MainViewController.h
@protocol StatusBarChange <NSObject>
    -(void)onStatusbarChange:(NSNotification*)notification;
@end

设置侦听器.当它更改并触发通过此数据的WebView中的事件时,它会从statusBarFrame获取原始和大小字典.

// MainViewController.m
- (void)onStatusbarChange:(NSNotification*)notification
{
    // Native code for
    NSMutableDictionary *eventInfo = [self getStatusBarInfo];
    [self notifiy:notification.name withInfo:eventInfo];
}

- (void)notifiy:(Nsstring*)event withInfo:(NSMutableDictionary*)info
{
    Nsstring *json = [self toJSON:info];
    Nsstring *cmd = [Nsstring stringWithFormat:@"cordova.fireWindowEvent('\%@\',%@)",event,json];
    [self.webView stringByEvaluatingJavaScriptFromString:cmd];
}

- (NSMutableDictionary *)getStatusBarInfo
{
    CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];

    NSMutableDictionary *statusBarInfo = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *size = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *origin = [[NSMutableDictionary alloc] init];

    size[@"height"] = [NSNumber numberWithInteger:((int) statusBarFrame.size.height)];
    size[@"width"] = [NSNumber numberWithInteger:((int) statusBarFrame.size.width)];

    origin[@"x"] = [NSNumber numberWithInteger:((int) statusBarFrame.origin.x)];
    origin[@"y"] = [NSNumber numberWithInteger:((int) statusBarFrame.origin.y)];

    statusBarInfo[@"size"] = size;
    statusBarInfo[@"origin"] = origin;

    return statusBarInfo;
}

- (Nsstring *) toJSON:(NSDictionary *)dictionary {
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
    return [[Nsstring alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

所有这些都允许您收听window.statusbarchange事件,例如喜欢这个:

// www/js/index.js
window.addEventListener('statusbarchange',function(e){
  // Use e.size.height to adapt to the changing status bar      

},false)

iOS API - UIWebView.h

iOS API - UIWebView.h

//
//  UIWebView.h
//  UIKit
//
//  Copyright (c) 2007-2015 Apple Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIView.h>
#import <UIKit/UIKitDefines.h>
#import <UIKit/UIDataDetectors.h>
#import <UIKit/UIScrollView.h>

NS_ASSUME_NONNULL_BEGIN

// 获取用户在网页上的行为
typedef NS_ENUM(NSInteger, UIWebViewNavigationType) {
    UIWebViewNavigationTypeLinkClicked, // 用户触击了一个链接
    UIWebViewNavigationTypeFormSubmitted, // 用户提交了一个表单
    UIWebViewNavigationTypeBackForward, // 用户触击前进或返回按钮
    UIWebViewNavigationTypeReload, // 用户触击重新加载的按钮
    UIWebViewNavigationTypeFormResubmitted, // 用户重复提交表单
    UIWebViewNavigationTypeOther // 发生其它行为
} __TVOS_PROHIBITED;

typedef NS_ENUM(NSInteger, UIWebPaginationMode) { // 在web视图的布局内容决定了页面流的方向
    UIWebPaginationModeUnpaginated, // 内容显示为一个长的滚动视图,没有不同的页面
    UIWebPaginationModeLeftToRight, // 内容分为页面流从左到右
    UIWebPaginationModeTopToBottom, // 内容分为页面流从上到下
    UIWebPaginationModeBottomToTop, // 内容分为页面流从底到上
    UIWebPaginationModeRightToLeft // 内容分为页面流从右到左
} __TVOS_PROHIBITED;

typedef NS_ENUM(NSInteger, UIWebPaginationBreakingMode) { // 列或分页符方法
    UIWebPaginationBreakingModePage, // 内容方面分页符相关的CSS属性
    UIWebPaginationBreakingModeColumn // 内容方面与column-breaking相关的CSS属性
} __TVOS_PROHIBITED;

@class UIWebViewInternal;
@protocol UIWebViewDelegate;


NS_CLASS_AVAILABLE_IOS(2_0) __TVOS_PROHIBITED @interface UIWebView : UIView <NSCoding, UIScrollViewDelegate> 


@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;


// 设置UIWebView的代理才能使用代理方法
webView.delegate = self;

@property (nonatomic, readonly, strong) UIScrollView *scrollView NS_AVAILABLE_IOS(5_0);


- (void)loadRequest:(NSURLRequest *)request; // 加载内容,连接到一个给定的URL启动异步客户机请求


- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL; // 为了避免受到安全攻击,一定要使用这个方法来加载本地HTML文件,不要使用loadRequest


- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL; // 设置loadData:主页内容、MIMEType:MIME类型、textEncodingName:内容编码、baseURL:URL


@property (nullable, nonatomic, readonly, strong) NSURLRequest *request; // URL请求识别的位置加载内容,只读属性


- (void)reload; // 刷新


- (void)stopLoading; // 取消加载内容


- (void)goBack; // 上一步


- (void)goForward; // 下一步


@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack; // 是否能向后移动


@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward; // 是否能向前移动


@property (nonatomic, readonly, getter=isLoading) BOOL loading; // 指示是否接收方完成加载内容,如果为YES则接收机继续加载内容,如果为NO则没有加载内容。


- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script; // 获得webview页面内容信息,但最佳的使用方法是


@property (nonatomic) BOOL scalesPageToFit; // 自动对页面进行缩放以适应屏幕


@property (nonatomic) BOOL detectsPhoneNumbers NS_DEPRECATED_IOS(2_0, 3_0); // 自动检测网页上的电话号码,点击可以拨打


@property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0); // 类型的数据转换为可点击的网址在web视图的内容。使用这个属性来指定类型的数据(http链接、电话号码、电子邮件等等)应该自动转换为web视图中点击url。当点击时,web视图中打开应用程序负责处理URL类型和通过其URL。看到的UIDataDetectorTypes枚举类型的数据可以自动检测。


@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0); // 手机浏览器默认为YES,平板浏览器默认为NO。决定是否HTML5 视频玩内联或使用本机全屏控制器


@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0); // 手机和平板都默认为YES,决定是否HTML5是屁你可以自动或要求用户开始播放


@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay NS_AVAILABLE_IOS(5_0); // 手机和平板默认为YES,决定了是否允许你来自视图的进行播放


@property (nonatomic) BOOL suppressesIncrementalRendering NS_AVAILABLE_IOS(6_0); // 手机和平板默认为NO,当设置为yes时,指示是否web视图抑制内容呈现知道它完全被加载到内存中


@property (nonatomic) BOOL keyboardDisplayRequiresUserAction NS_AVAILABLE_IOS(6_0); // 默认为YES,表示web内容是否能以编程方式显示键盘


@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0); // 在web内容的布局视图


@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode NS_AVAILABLE_IOS(7_0); // 列或分页符的方式


@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0); // 每个页面的大小


@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0); // 两点之间差距的大小


@property (nonatomic, readonly) NSUInteger pageCount NS_AVAILABLE_IOS(7_0); // 页面的数量


@property (nonatomic) BOOL allowsPictureInPictureMediaPlayback NS_AVAILABLE_IOS(9_0); // 确定是否允许从画中画中播放


@property (nonatomic) BOOL allowsLinkPreview NS_AVAILABLE_IOS(9_0); // 属性默认为NO,决定是否紧迫的一个链接显示的目的地的预览链接


@end

__TVOS_PROHIBITED @protocol UIWebViewDelegate <NSObject>

@optional


- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; // 获取它加载的网页上面的事件,比如点击了图片或者按钮等等


- (void)webViewDidStartLoad:(UIWebView *)webView; // 当网页视图已经开始加载一个请求后得到通知


- (void)webViewDidFinishLoad:(UIWebView *)webView; // 当网页视图已经结束加载一个请求后得到通知


- (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error; // 当在请求加载中发生错误时,得到通知。会提供一个NSError对象标识所发生错误类型


@end

NS_ASSUME_NONNULL_END

 

ios uiwebview wkwebview注意点小记

ios uiwebview wkwebview注意点小记

概述

wkwebview是苹果公司推出的替代uiwebview的方案,它在内存占用和稳定性方面有很大的优势,性能对比此篇文章就不讲了。
但是就目前情况而言,uiwebview还有有一些不能被完全替代的原因,比如wkwebview无法用NSURLProtocol拦截请求,因此无法通过NSURLProtocol实现加载离线化资源。
本文主要是记录自己在使用的时候碰到的一些坑。

方法注入

uiwebview目前的方式就是直接通过JS定义方法,然后使用JSC来获得JS方法的回调。
wkwebview可以直接使用addScriptMessageHandler来添加需要监听的方法,然后在userContentController中处理监听事件。
主要的区别是,uiwebview的注入只对当前界面生效,在加载新的url或者界面刷新后就会失效。而wkwebview的注入对整个wkwebview生效,界面刷新不会对其有影响。
所以在uiwebview上如果有注入全局方法的需求,通过直接运行JS代码注入不可行,一般可以使用拦截自定scheme和host的方式来做方法注入。

cookie

uiwebview的cookie与NSHTTPCookieStorage 同步,每次访问都会带上NSHTTPCookieStorage 中的内容,包括在页面中输入document.cookie也能获取到NSHTTPCookieStorage 中的cookie。

wkwebview不是及时同步

但是wkwebview的cookie和NSHTTPCookieStorage 就不能及时同步,注意是不能及时同步,并不是不同步。主要体现在以下两个方面:
1、当NSHTTPCookieStorage 的中的cookie被修改了,cookie是会同步到wkwebview的,但是不是及时同步的,比如说我修改了NSHTTPCookieStorage的值之后然后马上打开一个wkwebview,wkwebview不一定能获取到我刚刚修改的cookie。
2、当我使用document.cookie在wkwebview中设置cookie的时候,我当前设置的cookie是会回写到NSHTTPCookieStorage中,但是也不是及时的。

由于不及时同步,我们就说一下可能会有的问题,举例两个场景:
1、wkwebview没有获取到cookie,然后触发登陆逻辑后修改NSHTTPCookieStorage 跳回wkwebview,这时候wkwebview很有可能还是没有cookie的,因为wkwebview的cookie不是及时同步的。
2、某一模块为了满足自己的需求,修改了NSHTTPCookieStorage 中的一个cookie,而这个cookie刚好和其他模块重名了,由于wkwebview会回写cookie到NSHTTPCookieStorage 中,因此它会把原来这个名字的cookie给覆盖掉。而不仅仅存在cookie的value被修改,导致其他模块cookie错误的的问题,如果expire被修改了,也同时会给其他模块带去cookie过期的问题。

wkwebview对cookie的处理

目前网上的处理方法主要有以下两种:
1、在webview发起请求的时候附带cookie。
2、在webview创建的时候js注入cookie。

这两个方法都能解决wkwebview不能及时同步NSHTTPCookieStorage 的问题,但是无法解决wkwebview的cookie修改后不能及时回写到NSHTTPCookieStorage 的问题。
还是举个例子:
第一个wkwebview中的JS修改了一段cookie之后,没过多久又打开了第二个wkwebview,第二个wkwebview是很可能获取不到第一个wkwebview对cookie的修改的。最根本原因就是由于wkwebview的cookie无法及时回写到NSHTTPCookieStorage 。

那么这种情况如何解决呢?答案就是wkProcessPool。

WKProcessPool

使用同一个WKProcessPool的wkwebview可以共享cookie数据,但是WKProcessPool中的cookie并不和NSHTTPCookieStorage 一样会本地存储。在APP重启后WKProcessPool中的cookie会被重置。

cookie仍然存在的问题

1、 第一个wkwebview中的JS修改了一段cookie之后,没过多久又打开了一个uiwebview,uiwebview如何能及时同步到wkwebview对cookie的修改?
2、如果某个模块在wkwebview中修改了cookie的值,导致NSHTTPCookieStorage 中的cookie被篡改或者过期,如何定位到该模块的问题?

iOS UIWebView 和 WKWebView 的 cookie 获取,设置,删除

Cookie简介 说到Cookie,或许有些小伙伴会比较陌生,有些小伙伴会比较熟悉。如果项目中,所有页面都是纯原生来实现的话,一般Cookie这个东西或许我们永远也不会接触到。但是,这里还是要说一下Cookie,因为它真的很重要,由它产生的一些坑也很多。

Cookie 在 web中应用比较多,主要是记录一个状态,比如我在网页上登录了,我就可以拿到网页登录后 Cookie,下次再 Cookie 的生效期内我就可以不用输入账号密码,直接跳转登录状态,在App中,Cookie最常用的也就是维持登录状态了.因为笔者最近就在做这个,其中也遇到过很多坑,这里说先踩坑和用法

iOS Cookie 的管理

NSHTTPCookie和NSHTTPCookieStorage iOS中进行HTTP网络请求Cookie管理主要由两个类负责,一个类是NSHTTPCookieStorage类,一个是NSHTTPCookie类。

NSHTTPCookieStorage

NSHTTPCookieStorage类采用单例的设计模式,其中管理着所有HTTP请求的Cookie信息 官方解释:NSHTTPCookieStorage 是一个用来管理 cookie 存储的单例。一个 NSHTTPCookie 单例代表一个 cookie。通常来讲,cookie 可以在应用间共享,并且在进程之间保持同步。 对于单进程,Session cookies (这里的 cookie 对象的 isSessionOnly 方法返回 YES)是局部的并且不能被共享。

常用方法

// 获取单例对象
+ (NSHTTPCookieStorage *)sharedHTTPCookieStorage;

// 所有Cookie数据数组 其中存放NSHTTPCookie对象
@property (nullable , readonly, copy) NSArray<NSHTTPCookie *> *cookies;

// 手动设置一条Cookie数据
- (void)setCookie:(NSHTTPCookie *)cookie;

// 删除某条Cookie信息
- (void)deleteCookie:(NSHTTPCookie *)cookie;

// 删除某个时间后的所有Cookie信息 iOS8后可用
- (void)removeCookiesSinceDate:(NSDate *)date NS_AVAILABLE(10_10, 8_0);

// 获取某个特定URL的所有Cookie数据
- (nullable NSArray<NSHTTPCookie *> *)cookiesForURL:(NSURL *)URL;

// 为某个特定的URL设置Cookie
- (void)setCookies:(NSArray<NSHTTPCookie *> *)cookies forURL:(nullable NSURL *)URL mainDocumentURL:(nullable NSURL *)mainDocumentURL;

// Cookie数据的接收协议
枚举如下:
typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
    NSHTTPCookieAcceptPolicyAlways,                     //接收所有Cookie信息
    NSHTTPCookieAcceptPolicyNever,                      //不接收所有Cookie信息
    NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain  //只接收主文档域的Cookie信息
};
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy;

系统下面的两个通知与Cookie管理有关:
据说,在Mac OS是cookie可以共享的(Session cookies 不能共享),在Mac OS app中更改cookie的接收策略会影响到其他正在运行的在使用cookie storage的app.这时NSHTTPCookieStorage会发出两个通知:

// Cookie数据的接收协议改变时发送的通知
FOUNDATION_EXPORT NSString * const NSHTTPCookieManagerAcceptPolicyChangedNotification;
// 管理的Cookie数据发生变化时发送的通知
FOUNDATION_EXPORT NSString * const NSHTTPCookieManagerCookiesChangedNotification;

NSHTTPCookie介绍

NSHTTPCookie是具体的HTTP请求Cookie数据对象.

// 下面两个方法用于对象的创建和初始化 都是通过字典进行键值设置
- (nullable instancetype)initWithProperties:(NSDictionary<NSString *, id> *)properties;
+ (nullable NSHTTPCookie *)cookieWithProperties:(NSDictionary<NSString *, id> *)properties;

// 返回Cookie数据中可用于添加HTTP头字段的字典
+ (NSDictionary<NSString *, NSString *> *)requestHeaderFieldsWithCookies:(NSArray<NSHTTPCookie *> *)cookies;

// 从指定的响应头和URL地址中解析出Cookie数据
+ (NSArray<NSHTTPCookie *> *)cookiesWithResponseHeaderFields:(NSDictionary<NSString *, NSString *> *)headerFields forURL:(NSURL *)URL;

// Cookie数据中的属性字典
@property (nullable, readonly, copy) NSDictionary<NSString *, id> *properties;

// 请求响应的版本
@property (readonly) NSUInteger version;

// 请求相应的名称
@property (readonly, copy) NSString *name;

// 请求相应的值
@property (readonly, copy) NSString *value;

// 过期时间
@property (nullable, readonly, copy) NSDate *expiresDate;

// 请求的域名
@property (readonly, copy) NSString *domain;

//请求的路径
@property (readonly, copy) NSString *path;

// 是否是安全传输
@property (readonly, getter=isSecure) BOOL secure;

// 是否只发送HTTP的服务
@property (readonly, getter=isHTTPOnly) BOOL HTTPOnly;

// 响应的文档
@property (nullable, readonly, copy) NSString *comment;

// 相应的文档URL
@property (nullable, readonly, copy) NSURL *commentURL;

// 服务端口列表
@property (nullable, readonly, copy) NSArray<NSNumber *> *portList;

HTTP cookie的属性键 属性 | 解读

NSHTTPCookieName        |	Cookie的名字
NSHTTPCookieValue       |	Cookie的值
NSHTTPCookieOriginURL   |	和域名一样,NSHTTPCookieDomain或NSHTTPCookieOriginURL必须指定一个值
NSHTTPCookieVersion     |	接收器的版本
NSHTTPCookieDomain      |	域名
NSHTTPCookiePath        |	Cookie 存放路径
NSHTTPCookieSecure      |   Cookie是否只应通过安全通道发送,设置Cookie的secure属性为true。
                            只会在HTTPS和SSL等安全协议中传输此类Cookie。默认为false
NSHTTPCookieComment     |	包含Cookie的评论,只有有效的版本1的cookies或更高版本。 这头字段是可选的
NSHTTPCookieCommentURL  |	接收器的评论URL
NSHTTPCookieDiscard     |   Cookie是否应在会议结束时丢弃NSString,字符串值必须是“true”或“假”。 
                            这个字段是可选的。 默认为“假”,除非这是Cookie是第1版或以上,
                            NSHTTPCookieMaximumAge未指定,在这种情况下,它被假定为“TRUE”
NSHTTPCookieMaximumAge  |	NSString对象,包含一个整数,在Cookie内保持最多几秒 。
                            仅适用于第1版和更高版本的有效。 默认为“0”。 此字段是可选的
NSHTTPCookiePort        |	接收机的端口
  • UIWebView的 Cookie 机制

UIWebView 在浏览网页后会将网页中的 cookie 自动存入 NSHTTPCookieStorage 标准容器中,[NSHTTPCookieStorage sharedHTTPCookieStorage]这个单例管理,在后续访问中会将 cookie 自动带到 request 请求当中。并且在同一个app内多个UIWebView之间共享。

  • webView 中获取 cookie
//加载成功
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSString *requestUrl = webView.request.URL.absoluteString;
    NSLog(@" requestUrl: %@",requestUrl);
    
    //设置原始 cookie 根据key 存储本地
    NSMutableArray *cookieArray = [[NSMutableArray alloc] init];
    
    //网页加载完成取出 cookies
    NSArray *nCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    NSHTTPCookie *cookie;
    for (id c in nCookies) {
        if ([c isKindOfClass:[NSHTTPCookie class]]) {
            cookie=(NSHTTPCookie *)c;
            //我这里是cookie存入字典中 去重
            if ([cookie value]) {
                //如果 vaule 值不为 nil 存入字典中,
                [self.mutableDic setValue:[cookie value] forKey:[cookie name]];
            }
            //设置原始 cookie
            NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
            [cookieProperties setObject:cookie.name forKey:NSHTTPCookieName];
            [cookieProperties setObject:cookie.value forKey:NSHTTPCookieValue];
            [cookieProperties setObject:cookie.domain forKey:NSHTTPCookieDomain];
            [cookieProperties setObject:cookie.path forKey:NSHTTPCookiePath];
            [cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];
            [cookieArray addObject:cookieProperties];
            
            [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }
}

//cookie 存入本地
[[NSUserDefaults standardUserDefaults] setObject:cookieArray forKey:@"cookieArray"];
[[NSUserDefaults standardUserDefaults] synchronize];

//下面 cookie 去重是为了得到 key=Value;形式的字符串,这里由于我有需求这样做,实际中下面可以忽略
NSMutableString *cookieValue = [NSMutableString stringWithFormat:@""];
// cookie重复,先放到字典进行去重,再进行拼接
for (NSString *key in self.mutableDic) {
    NSString *appendString = [NSString stringWithFormat:@"%@=%@;", key, [self.mutableDic valueForKey:key]];
    [cookieValue appendString:appendString];
}
NSLog(@"######################## %@ ####################",cookieValue);
  • 设置 cookie
NSMutableArray* cookieDictionary = [[NSUserDefaults standardUserDefaults] valueForKey:@"cookieArray"];

NSLog(@"cookie dictionary found is %@",cookieDictionary);

if (cookieDictionary) 
{
    for (NSInteger i = 0; i < cookieDictionary.count; i++) 
    {
        NSLog(@"cookie found is %@",[cookieDictionary objectAtIndex:i]);
        
        NSDictionary *cookieDic = [cookieDictionary objectAtIndex:i];
        NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieDic];
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }
}
//设置请求之前加载 cookie 确保 cookie 在请求头之前设置
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.URLString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

//加载网页
[self.webView loadRequest:request];

  • 删除 cookie
// 清空 cookie
- (void)deleteCookie {
    // 清空 cookie
    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSArray *_tmpArray = [NSArray arrayWithArray:[cookieJar cookies]];
    for (id obj in _tmpArray) {
        [cookieJar deleteCookie:obj];
    }
}
  • WKWebView 的 Cookie 机制

NSURLCache和NSHTTPCookieStroage无法操作(WKWebView)WebCore进程的缓存和Cookie WKWebView实例将会忽略任何的默认网络存储器(NSURLCache, NSHTTPCookieStorage, NSCredentialStorage) 和一些标准的自定义网络请求类(NSURLProtocol,等等.),WKWebView实例不会把Cookie存入到App标准的的Cookie容器(NSHTTPCookieStorage)中,因为 NSURLSession/NSURLConnection等网络请求使用NSHTTPCookieStorage进行访问Cookie,所以不能访问WKWebView的Cookie,现象就是WKWebView存了Cookie,其他的网络类如NSURLSession/NSURLConnection却看不到。这是很多人的说法。 还有一种是说法是通过实践发现 WKWebView 实例其实也会将 Cookie 存储于 NSHTTPCookieStorage 中,但存储时机有延迟,因为WKWebView内也有cookie的容器,而且每隔一段时间就和app侧NSHTTPCookieStorage进行同步,而且这个同步是进程级别的同步,而且这个同步是单向。 至于以上两种说法,最终WKWebView Cookie 问题在于 WKWebView 发起的请求不会自动带上存储于 NSHTTPCookieStorage 容器中的 Cookie。

  • iOS11 iOS11 的 API 可以解决该问题,只要是存在 WKHTTPCookieStore 里的 cookie,WKWebView 每次请求都会携带,存在 NSHTTPCookieStorage 的cookie,并不会每次都携带。于是会发生首次 WKWebView 请求不携带 Cookie 的问题。

  • ios 11 WKWebView cookie 的注入

在执行 -[WKWebView loadReques:] 前将 NSHTTPCookieStorage 中的内容复制到 WKHTTPCookieStore 中,以此来达到 WKWebView Cookie 注入的目的。示例代码如下:

[self copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:^{
    NSURL *url = [NSURL URLWithString:@"https://www.v2ex.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [_webView loadRequest:request];
}];

- (void)copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:(nullable void (^)())theCompletionHandler; {

    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
    if (cookies.count == 0) {
        !theCompletionHandler ?: theCompletionHandler();
        return;
    }
    for (NSHTTPCookie *cookie in cookies) 
    {
        [cookieStroe setCookie:cookie completionHandler:^{
        if ([[cookies lastObject] isEqual:cookie]) 
        {
            !theCompletionHandler ?: theCompletionHandler();
            return;
        }
    }];
    }
}

  • ios11 之前 注入 Cookie 就是从之前保存 cookie 的 NSHTTPCookieStorage 中取出相关 Cookie,然后在再次请求访问的时候在 request 中注入 Cookie。注入Cookie同样有多种方式。

  • 1.JS注入1

//取出 storage 中的cookie并将其拼接成正确的形式

NSArray<NSHTTPCookie *> *tmp = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; 
NSMutableString *jscode_Cookie = [@"" mutableCopy];

[tmp enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) 
{
    NSLog(@"%@ = %@", obj.name, obj.value);
    [jscode_Cookie appendString:[NSString stringWithFormat:@"document.cookie = ''%@=%@'';", obj.name, obj.value]];
}];

WKUserContentController* userContentController = WKUserContentController.new;
WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];

[userContentController addUserScript:cookieScript];
WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig];
  • JS注入2
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation 
{
    [webView evaluateJavaScript:@"document.cookie =''TeskCookieKey1=TeskCookieValue1'';" completionHandler:^(id result, NSError *error) {
        //...
    }];
}

  • NSMutableURLRequest 注入Cookie
NSURL *url = request.URL;
NSMutableString *cookies = [NSMutableString string];
NSMutableURLRequest *requestObj = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

NSArray *tmp = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
NSDictionary *dicCookies = [NSHTTPCookie requestHeaderFieldsWithCookies:tmp];
NSString *cookie = [self readCurrentCookie];
[requestObj setValue:cookie forHTTPHeaderField:@"Cookie"];
[_webView loadRequest:requestObj];


-(NSString *)readCurrentCookie
{
    NSMutableDictionary *cookieDic = [NSMutableDictionary dictionary];
    NSMutableString *cookieValue = [NSMutableString stringWithFormat:@""];
    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [cookieJar cookies]) {
        [cookieDic setObject:cookie.value forKey:cookie.name];
    }

    // cookie重复,先放到字典进行去重,再进行拼接
    for (NSString *key in cookieDic) {
    NSString *appendString = [NSString stringWithFormat:@"%@=%@;", key, [cookieDic valueForKey:key]];
        [cookieValue appendString:appendString];
    }
    return cookieValue;
}

获取Cookie

由于 WKWebView 的 Cookie 存储容器 WKWebsiteDataStore 是私有存储,所以无法从这里获取到Cookie,目前的方法是(1)从网站返回的 response headerfields 中获取。(2)通过调用js的方法获取 cookie。

  • 1.从网站返回的 response headerfields 中获取,因为cookie都存在http respone的headerfields,找到能获得respone的WKWebView回调,打印
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

    NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
    NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
    
    //读取wkwebview中的cookie 方法1
    for (NSHTTPCookie *cookie in cookies) {
        // [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        NSLog(@"wkwebview中的cookie:%@", cookie);
    }
    
    //读取wkwebview中的cookie 方法2 读取Set-Cookie字段
    NSString *cookieString = [[response allHeaderFields] valueForKey:@"Set-Cookie"];
    NSLog(@"wkwebview中的cookie:%@", cookieString);

    //看看存入到了NSHTTPCookieStorage了没有
    NSHTTPCookieStorage *cookieJar2 = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in cookieJar2.cookies) {
       NSLog(@"NSHTTPCookieStorage中的cookie%@", cookie);
    }

    //下面是将 原始cookie本地 化
    NSMutableArray *cookieArray = [[NSMutableArray alloc] init];

    for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
    
        NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
        [cookieProperties setObject:cookie.name forKey:NSHTTPCookieName];
        [cookieProperties setObject:cookie.value forKey:NSHTTPCookieValue];
        [cookieProperties setObject:cookie.domain forKey:NSHTTPCookieDomain];
        [cookieProperties setObject:cookie.path forKey:NSHTTPCookiePath];
        [cookieProperties setObject:[NSNumber numberWithInt:cookie.version] forKey:NSHTTPCookieVersion];
        [cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];
        [cookieArray addObject:cookieProperties];
    }

    [[NSUserDefaults standardUserDefaults] setValue:cookieArray forKey:@"cookieArray"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    decisionHandler(WKNavigationResponsePolicyAllow);
}

// 页面加载完成之后调用需要重新给WKWebView设置Cookie防止因为a标签跳转,导致下一次跳转的时候Cookie丢失。
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{

    //取出cookie
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

    //js函数
    NSString *JSFuncString =
    @"function setCookie(name,value,expires)\
    {\
        var oDate=new Date();\
        oDate.setDate(oDate.getDate()+expires);\
        document.cookie=name+''=''+value+'';expires=''+oDate+'';path=/''\
    }\
    function getCookie(name)\
    {\
        var arr = document.cookie.match(new RegExp(''(^| )''+name+''=({FNXX==XXFN}*)(;|$)''));\
        if(arr != null) return unescape(arr[2]); return null;\
    }\
    function delCookie(name)\
    {\
        var exp = new Date();\
        exp.setTime(exp.getTime() - 1);\
        var cval=getCookie(name);\
        if(cval!=null) document.cookie= name + ''=''+cval+'';expires=''+exp.toGMTString();\
    }";
    
    //拼凑js字符串
    NSMutableString *JSCookieString = JSFuncString.mutableCopy;
    for (NSHTTPCookie *cookie in cookieStorage.cookies) {
        NSString *excuteJSString = [NSString stringWithFormat:@"setCookie(''%@'', ''%@'', 1);", cookie.name, cookie.value];
        [JSCookieString appendString:excuteJSString];
    }
    //执行js
    [webView evaluateJavaScript:JSCookieString completionHandler:^(id obj, NSError * _Nullable error) {
        NSLog(@"%@",error);
    }];
}

  • 通过 JS 获取 cookie
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {

[webView evaluateJavaScript:[NSString stringWithFormat:@"document.cookie"] completionHandler:^(id _Nullable response, NSError * _Nullable error) {
    if (response != 0) {
        NSLog(@"\n\n\n\n\n\n document.cookie%@,%@",response,error);
    }
}];
}

##注意 document.cookie 的方法获取 cookie并不支持跨越获取,如果设置 httponly则获取不到 cookie 不论是(1)还是(2)方法,似乎都无法解决302请求的 Cookie 问题。举例来说,假设你要访问网站A,在A中点击登录,跳转页面到B地址,在B中完成登录之后302跳转回A网站。此时cookie是存在于B地址的 response 中的,在A地址的 response 中并没有 cookie 的字段。然而我们只能获取到A地址的 response ,无法截获到B地址的response。因此获取不到该类型网站的 cookie 。 由于我并没有遇到302这样的问题,所有看了下网上的资料,希望对遇到这个问题的小伙伴一下办法,网上给出的解决办法是:

  • 1.加载一个本地为空的html,域名指向你的第一次加载的url的域名。
//加载本地html
[self.webView loadHTMLString:@"" baseURL:[NSURL URLWithString:@"https:/a.com"]];
  • 2.通过以下方法,在第一次加载完成后,将需要设置的Cookies设置到WKWebView中,因为是加载的本地的html以下方法会立即执行。
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{

    if (isFirstLoaded) {
        NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        //js函数
        NSString *JSFuncString =
        @"function setCookie(name,value,expires)\
        {\
            var oDate=new Date();\
            oDate.setDate(oDate.getDate()+expires);\
            document.cookie=name+''=''+value+'';expires=''+oDate+'';path=/''\
        }";
        
        //拼凑js字符串,按照自己的需求拼凑Cookie
        NSMutableString *JSCookieString = JSFuncString.mutableCopy;
        for (NSHTTPCookie *cookie in cookieStorage.cookies) {
            if (![cookie.name isEqualToString:@"__cust"]) {
                NSString *excuteJSString = [NSString stringWithFormat:@"setCookie(''%@'', ''%@'', 3);", cookie.name, cookie.value];
                [JSCookieString appendString:excuteJSString];
            }
        }
    
        //执行js
        [webView evaluateJavaScript:JSCookieString completionHandler:^(id obj, NSError * _Nullable error) 
        {
            //加载真正的第一次Request
            [self loadRealRequest];
        }];
    }
}

如果cookie 存储到本地获取本地的 cookie

//修改从 storage 中读取 cookie 的方法
-(NSString *)readCurrentCookie{

    NSMutableArray* cookieDictionary = [[NSUserDefaults standardUserDefaults] valueForKey:@"cookieArray"];
    NSLog(@"cookie dictionary found is %@",cookieDictionary);
    
    for (int i=0; i < cookieDictionary.count; i++) {
        NSLog(@"cookie found is %@",[cookieDictionary objectAtIndex:i]);
        NSMutableDictionary* cookieDictionary1 = [[NSUserDefaults standardUserDefaults] valueForKey:[cookieDictionary objectAtIndex:i]];
        NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieDictionary1];
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }
    
    NSMutableDictionary *cookieDic = [NSMutableDictionary dictionary];
    NSMutableString *cookieValue = [NSMutableString stringWithFormat:@""];
    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];

    for (NSHTTPCookie *cookie in [cookieJar cookies]) {
        [cookieDic setObject:cookie.value forKey:cookie.name];
    }
    
    // cookie重复,先放到字典进行去重,再进行拼接
    for (NSString *key in cookieDic) 
    {
        NSString *appendString = [NSString stringWithFormat:@"%@=%@;", key, [cookieDic valueForKey:key]];
        [cookieValue appendString:appendString];
    }
    return cookieValue;
}

清除 cookie

#pragma mark - 清空cookie
-(void)deleCookie {
    // NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    // NSArray *_tmpArray = [NSArray arrayWithArray:[cookieJar cookies]];
    // for (id obj in _tmpArray) {
    //  [cookieJar deleteCookie:obj];
    // }
    if (@available(iOS 9.0, *)) {//iOS9及以上
        WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore];
        [dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]
        completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) 
        {
            for (WKWebsiteDataRecord *record in records)
            {
                //取消备注,可以针对某域名做专门的清除,否则是全部清除
                //if ( [record.displayName containsString:@"baidu"])             
                //{
                    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes
                    forDataRecords:@[record] completionHandler:^
                    {
                        NSLog(@"Cookies for %@ deleted successfully",record.displayName);
                    }];
                //}
            }
        }];
    }
    else { //iOS9以下
        NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *cookiesFolderPath = [libraryPath stringByAppendingString:@"/Cookies"];
        NSError *errors;
        [[NSFileManager defaultManager] removeItemAtPath:cookiesFolderPath error:&errors];
    }
}

##最后 UIWebView 和 WKWebView 所遇到问题 cookie 同步,获取,删除,设置这些问题,目前就这么多解决办法吧,我一开始尝试用 WKWebView 获取 cookie 但是最后遇到网页跨域问题以及低版本兼容问题我还是换回 UIWebView 了,如果有更好的解决办法可以在下面留言,谢谢!

来自:https://www.cnblogs.com/ningmengcao-ios/p/9578218.html

原文出处:https://www.cnblogs.com/benxiaokang/p/ios-uiwebview-he-wkwebview-de-cookie-huo-qu-she-zh.html

ios – ARC中的UIWebViewDelegate手册发布?

ios – ARC中的UIWebViewDelegate手册发布?

UIWebViewDelegate协议参考声明:

Before releasing an instance of UIWebView for which you have set a delegate,you must first set the UIWebView delegate property to nil before disposing of the UIWebView instance. This can be done,for example,in the dealloc method where you dispose of the UIWebView.

ARC还有必要吗?

解决方法

如果委托被定义为WEAK属性,则无需委托代理,因为ARC将为您执行此操作.但是,如果该类未使用ARC编译且委托标记为“assign”,则必须小心并将其设置为nil.

今天关于防止iOS在通话状态栏中推出PhoneGap UIWebView屏幕苹果通话防误触怎么关闭的分享就到这里,希望大家有所收获,若想了解更多关于iOS API - UIWebView.h、ios uiwebview wkwebview注意点小记、iOS UIWebView 和 WKWebView 的 cookie 获取,设置,删除、ios – ARC中的UIWebViewDelegate手册发布?等相关知识,可以在本站进行查询。

本文标签: