Oct
9
用安卓手机的时候注意到不少内容app可以阻止用户截屏,通过为当前windows设置FLAG_SECURE或者一些其他的trick手段,用户就无法截屏了。
iOS目前还没有提供类似的功能,不过注意到系统有提供类似的组件:UITextField在设置为secureTextEntry即密码模式后,整个UITextField的文本区在截屏时是完全截不进去的,估计是考虑到点点点也能暴露用户密码位数的缘故。
当然目前iOS大部分应用做的方法是监听UIApplicationUserDidTakeScreenshotNotification事件,在用户截屏后给出安全提示,不过毕竟不是最好的方法。
因此注意到一些传统app有做,针对敏感文本信息,有做闪烁截屏保护。
实现路径与效果
简单来说,就是通过不停的交错闪烁间隔的文字来让用户看到完整的信息。而截屏的时候只能截取到不完整的信息,防止误传敏感信息(本来这个方案就是防君子不防小人的)。
实际看起来效果如下:
(iPhone SE 拍摄视频 模拟器)
(Android mi note 3拍摄照片 模拟器+真机)
代码很简单:
首先代码角度:
1. 通过CADisplayLink来构成闪烁timmer,保证文字尽可能快/同步的刷新,别无他选。
2. 文字截取的时候务必使用rangeOfComposedCharacterSequenceAtIndex而不是substringToIndex保证异常字符可以正确截取,否则有炸的风险。
3. 虽然上面的代码不完整,self.attributedText设置之后,需要重新拷贝设置一次textColor,textAlignment,lineBreakMode之类的属性,attributedText设置会重置相关内容。
4. 最好钩一下当前类的setAttributedText方法避免错误使用。同时这里利用到特性,UILabel展示的是setText和setAttributedText两者最后一次设置的内容。
同时从如上实现效果来看。首先主观判断效果还是可行,上面拍摄的倒是因为刷新率问题看起来有明暗:
1. 目前文字设置的black,但是闪烁起来看起来就是灰色的了,因此该方案必然无法实现大红大绿(rgb到上下极限值)的视觉颜色,需要一定妥协。
2. 因为是CADisplayLink,因此屏幕掉帧,特别是无规律掉帧的时候看起来就能看出来闪烁了。不过实际看多半是在页面切换/前后台切换等场景,页面本身写得靠谱问题不大。
3. 如果之后屏幕刷新率普遍提升到120hz,这个方案可行问题会小不少。
其他讨论:
1. 除了交错闪烁(均匀屏蔽掉50%)内容,是否有别的处理方法?目前判断如果不进行均匀的闪烁,或者闪烁背景不是容器背景色的话,闪烁感会非常明显。测试了一个随意区域隐藏的闪烁逻辑,效果明显不理想:
2. 图片可以使用该方法么?原理上可以,但是闪烁颗粒得落到像素级别。区域级别或者其他交错方式在图片上都会有明显的交错纹理。另外图片变淡就比文字变淡处理起来麻烦多了。
iOS目前还没有提供类似的功能,不过注意到系统有提供类似的组件:UITextField在设置为secureTextEntry即密码模式后,整个UITextField的文本区在截屏时是完全截不进去的,估计是考虑到点点点也能暴露用户密码位数的缘故。
当然目前iOS大部分应用做的方法是监听UIApplicationUserDidTakeScreenshotNotification事件,在用户截屏后给出安全提示,不过毕竟不是最好的方法。
因此注意到一些传统app有做,针对敏感文本信息,有做闪烁截屏保护。
实现路径与效果
简单来说,就是通过不停的交错闪烁间隔的文字来让用户看到完整的信息。而截屏的时候只能截取到不完整的信息,防止误传敏感信息(本来这个方案就是防君子不防小人的)。
实际看起来效果如下:
(iPhone SE 拍摄视频 模拟器)
(Android mi note 3拍摄照片 模拟器+真机)
代码很简单:
@implementation LRDScreenShotSafeLabel {
CADisplayLink *_timer;
BOOL _timerSwitch;
}
- (CADisplayLink *)displayLink {
if (!_timer) {
_timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateDisplayLabel)];
_timer.paused = YES;
[_timer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
return _timer;
}
- (void)willMoveToWindow:(nullable UIWindow *)newWindow {
if (newWindow) {
self.displayLink.paused = NO;
} else {
self.displayLink.paused = YES;
}
}
- (void)updateDisplayLabel {
_timerSwitch = !_timerSwitch;
NSRange range;
NSMutableAttributedString *displayStr = [NSMutableAttributedString new];
NSDictionary *lightDic = @{NSForegroundColorAttributeName: self.safeForegroundColor ?: [UIColor blackColor]};
NSDictionary *darkDic = @{NSForegroundColorAttributeName: self.safeBackgroundColor ?: [UIColor whiteColor]};
NSUInteger count = 0;
for (int i = 0; i < self.text.length ; i += range.length, count++ ) {
range = [self.text rangeOfComposedCharacterSequenceAtIndex:i];
[displayStr appendAttributedString: [[NSAttributedString alloc] initWithString:[self.text substringWithRange:range] attributes:((count + (_timerSwitch ? 1 : 0)) % 2) ? lightDic : darkDic]];
}
self.attributedText = displayStr;
}
@end
首先代码角度:
1. 通过CADisplayLink来构成闪烁timmer,保证文字尽可能快/同步的刷新,别无他选。
2. 文字截取的时候务必使用rangeOfComposedCharacterSequenceAtIndex而不是substringToIndex保证异常字符可以正确截取,否则有炸的风险。
3. 虽然上面的代码不完整,self.attributedText设置之后,需要重新拷贝设置一次textColor,textAlignment,lineBreakMode之类的属性,attributedText设置会重置相关内容。
4. 最好钩一下当前类的setAttributedText方法避免错误使用。同时这里利用到特性,UILabel展示的是setText和setAttributedText两者最后一次设置的内容。
同时从如上实现效果来看。首先主观判断效果还是可行,上面拍摄的倒是因为刷新率问题看起来有明暗:
1. 目前文字设置的black,但是闪烁起来看起来就是灰色的了,因此该方案必然无法实现大红大绿(rgb到上下极限值)的视觉颜色,需要一定妥协。
2. 因为是CADisplayLink,因此屏幕掉帧,特别是无规律掉帧的时候看起来就能看出来闪烁了。不过实际看多半是在页面切换/前后台切换等场景,页面本身写得靠谱问题不大。
3. 如果之后屏幕刷新率普遍提升到120hz,这个方案可行问题会小不少。
其他讨论:
1. 除了交错闪烁(均匀屏蔽掉50%)内容,是否有别的处理方法?目前判断如果不进行均匀的闪烁,或者闪烁背景不是容器背景色的话,闪烁感会非常明显。测试了一个随意区域隐藏的闪烁逻辑,效果明显不理想:
2. 图片可以使用该方法么?原理上可以,但是闪烁颗粒得落到像素级别。区域级别或者其他交错方式在图片上都会有明显的交错纹理。另外图片变淡就比文字变淡处理起来麻烦多了。