iOS 防截图需求(伪)实现
2019-12-23 本文已影响0人
山已几孑
需求: 防截图,安卓可以通过系统级的禁止截图来实现,但是iOS并不允许开发者这么做,另外,这个功能本身就是一个鸡肋,别人完全可以通过用别的手机拍照的形式获取屏幕信息,更多是一种警告意味,本文实现的防截图功能,依赖于Photos框架的performChanges方法,实现了 当用户截图时,提示用户删除,且必须删除,可以多张图片进行监听并删除
- 首先是权限问题,该功能需要相册使用权限,且必须提前进行申请,否则,第一张图片无法获取并删除。
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
switch (status) {
case PHAuthorizationStatusNotDetermined:
// User has not yet made a choice with regards to this application
break;
case PHAuthorizationStatusRestricted:// This application is not authorized to access photo data.
// The user cannot change this application’s status, possibly due to active restrictions
// such as parental controls being in place.
case PHAuthorizationStatusDenied: // User has explicitly denied this application access to photos data.
{
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"权限异常" message:@"使用此APP需要相册使用权限,请前往设置-移动C3开启权限" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
abort(); //强制退出
}]];
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:alert animated:true completion:nil];
});
}
break;
case PHAuthorizationStatusAuthorized:
break;
default:
break;
}
}];
- 初始化了一些属性来配合整个业务流转的顺序的控制
@property(nonatomic, strong)NSOperationQueue * snapshotQueue;
@property(nonatomic, assign)NSInteger snapshotCount;
@property(nonatomic, retain)dispatch_semaphore_t semaphore;
self.snapshotCount = 0;
//当做串行的queue使用
self.snapshotQueue = [[NSOperationQueue alloc]init];
self.snapshotQueue.maxConcurrentOperationCount = 1;
self.semaphore = dispatch_semaphore_create(0);
- 对系统的截屏时间进行监听,这里没啥好说的
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(receiveScreenShotNotification:) name:UIApplicationUserDidTakeScreenshotNotification object:nil];
- 收到截屏事件后,维护一个本地的统计数字
- (void)receiveScreenShotNotification:(NSNotification *)notify {
self.snapshotCount++;
}
- 统计数字改变后,根据统计数字状态,来决定是否需要激活删除的事件
- (void)setSnapshotCount:(NSInteger)snapshotCount {
BOOL needDelete = _snapshotCount < snapshotCount;
_snapshotCount = snapshotCount;
NSLog(@"setSnapshotCount changed: %ld",needDelete);
if (needDelete) {
[self deleteCounted];
}
}
- 激活删除图片, 这里,贴了两个方法,是因为,系统拍照需要处理事时间,当我们收到拍照事件时,系统相册可能还没有处理完成,此时获取到的最后一张图片是错误的,因此,我们需要尽可能的延迟代码调用顺序,这里通过弹窗来做延迟,也可以直接写延迟执行,但是时间不是很稳定,测试发现300毫秒可以稍微稳定一点,1s更好,但是流程上面就有了延迟。
-(void)deleteCounted {
[self.snapshotQueue cancelAllOperations];
[self.snapshotQueue addOperationWithBlock:^{
if (self.snapshotCount == 0) { //如果count==0,那么不用再删除了
return ;
}
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"警告" message:@"我们的APP禁止截屏,请自觉删除!" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self deleteAssets];
}]];
[UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:alert animated:true completion:^{
}];
});
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
}];
}
-(void)deleteAssets {
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
NSMutableArray <PHAsset *>* assets = [NSMutableArray arrayWithCapacity:self.snapshotCount];
if (self.snapshotCount >= 0) {
for (int i = 0; i < self.snapshotCount; i ++) {
PHAsset *asset = [assetsFetchResults objectAtIndex:i];
[assets addObject:asset];
}
}
__weak typeof(self) weakSelf = self;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetChangeRequest deleteAssets:assets];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (!success) {
[weakSelf deleteNewAssets:assets];
} else {
self.snapshotCount -= assets.count;
dispatch_semaphore_signal(self.semaphore);
}
}];
}
- 因为删除事件是系统功能,因此存在用户手动取消的情况,这里采用在取消时,继续弹出删除来解决
- (void)deleteNewAssets:(NSArray<PHAsset *> *)assets {
if (assets) {//增加一层判断,安全第一
__weak typeof(self) weakSelf = self;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetChangeRequest deleteAssets:assets];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (!success) {
[weakSelf deleteNewAssets:assets];
} else {
self.snapshotCount -= assets.count;
dispatch_semaphore_signal(self.semaphore);
}
}];
}
}