Swift UIKit에서 화면 캡쳐 방지하기
화면 캡쳐 방지
- iOS 앱에서 콘텐츠 보호를 위해 화면 캡쳐를 방지해야 하는 경우가 있습니다.
- 스크린샷(이미지)과 화면 녹화(영상)를 통틀어 화면 캡쳐라고 표현을 해봤는데요.
- 애플의
isSecureTextEntry
를 활용한 트릭을 사용해 화면 캡쳐 방지를 구현해보겠습니다. - 더불어, 스크린샷과 화면 녹화를 구분해 방지하는 방법도 알아보겠습니다.
isSecureTextEntry 트릭
UITextField
의isSecureTextEntry
속성이true
면, 해당TextField
의 내용은 캡쳐했을때 가려집니다.- 이는 원래 비밀번호 등 민감한 정보를 캡쳐하는 것을 방지하기 위한 것으로 알려져있는데요.
- 이를 활용해 화면 캡쳐를 방지하고 싶은
View
에TextField
를 추가하면 캡쳐를 막을 수 있습니다.
UIView 확장
- Swift 파일을 추가해 아래와 같이 UIView를 확장합니다.
- 코드에 대한 설명은 코드내 주석으로 추가했습니다.
import UIKit
private var AssociatedObjectHandle = 0
extension UIView {
// 트릭을 위한 임의의 UITextField
private var secureTextField: UITextField {
if let textField = objc_getAssociatedObject(self, &AssociatedObjectHandle) as? UITextField {
// 추가된 TextField가 있다면 해당 객체 반환
return textField
} else {
// 추가된 TextField가 없다면 생성해서
let textField = UITextField()
// 캡쳐하려는 UIView에 추가하고
addSubview(textField)
textField.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
textField.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
textField.layer.removeFromSuperlayer()
// 캡쳐하려는 UIView의 Layer를 TextField의 Layer 사이에 끼워 넣고
layer.superlayer?.insertSublayer(textField.layer, at: 0)
textField.layer.sublayers?.last?.addSublayer(layer)
// 마지막으로 set 해서 return
objc_setAssociatedObject(self, &AssociatedObjectHandle, textField, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return textField
}
}
// 트릭의 활성화를 제어하는 메소드
func secureMode(enable: Bool) {
secureTextField.isSecureTextEntry = enable
}
}
사용 방법
조금 더 유연한 화면 캡쳐 대응을 위해 스크린샷과 화면 녹화를 구분하는 방법도 추가해봤습니다. 해당 코드는 스크린샷은 허용하고 화면 녹화는 방지하도록 작성했습니다.
- 구현한 Extension을 실제
View
와ViewController
에서 사용해보겠습니다. - 코드에 대한 설명은 코드내 주석으로 추가했습니다.
import UIKit
class ViewController: UIViewController {
// 예시 콘텐츠
private lazy var label = {
let label = UILabel()
label.text = "캡쳐 보호가 필요한 콘텐츠"
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
// 캡쳐 감지용 노티피케이션 센터 추가
NotificationCenter.default.addObserver(
self,
selector: #selector(captureAction),
name: UIScreen.capturedDidChangeNotification,
object: nil
)
// 예시 콘텐츠 추가
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
// 캡쳐시 실행되는 액션 메소드
@objc private func captureAction() {
if UIScreen.main.isCaptured {
// 캡쳐 중이면 = 화면 녹화 중이면
view.secureMode(enable: true)
} else {
// 캡쳐 중이 아니면 = 화면 녹화가 끝났으면
view.secureMode(enable: false)
}
}
}
- 핵심적인 사용 코드는 결국 아래 부분입니다.
if UIScreen.main.isCaptured {
view.secureMode(enable: true)
} else {
view.secureMode(enable: false)
}
적용한 결과
- 스크린샷
- 화면 녹화
주의사항
- 스크린샷과 화면 녹화와 같은 화면 캡쳐는 실기기에서만 정상적으로 테스트가 가능합니다.