iOS 防截图需求(伪)实现

需求: 防截图,安卓可以通过系统级的禁止截图来实现,但是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 * 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 *)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);
            }
        }];
    }
}

你可能感兴趣的:(iOS 防截图需求(伪)实现)