관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - Swift] 1. Prevent Capture, Recording - 캡쳐 막는 방법, 화면 녹화 방지 (isSecureTextEntry) 본문

iOS 응용 (swift)

[iOS - Swift] 1. Prevent Capture, Recording - 캡쳐 막는 방법, 화면 녹화 방지 (isSecureTextEntry)

jake-kim 2022. 10. 13. 23:40

1. Prevent Capture, Recording - 캡쳐 막는 방법, 화면 녹화 방지 (isSecureTextEntry)

2. Prevent Capture, Recording - 캡쳐 감지 방법, 녹화 감지 방법

Capture를 막는 아이디어

  • 안드로이드처럼 사용자가 캡쳐를 했을때 캡쳐를 못하게 하는 방법은 불가능
  • 사용자가 캡쳐했을때, 특정 뷰의 내용이 가려지도록 하는 방법은 가능
  • UITextField에는 isSecureTextEntry라는 속성이 있는데, 이게 켜져있으면 캡쳐했을때 캡쳐의 내용이 안보이도록 할 수 있는 기능이 존재
  • 캡쳐를 막을 뷰에다가 isSecureTextEntry를 true로 한 UITextField를 삽입하면 캡쳐를 했을때, UITextField를 가지고 있는 layer의 캡쳐 내용을 막도록 구현

UITextField의 isSecureTextEntry

  • 디폴트 값은 false
  • true면, text copy를 막고 record와 캡쳐를 막음

https://developer.apple.com/documentation/uikit/uitextinputtraits/1624427-issecuretextentry

  • isSecureTextEntry를 true로 했을 때 결과
class ViewController: UIViewController {
  // MARK: UI
  private let label: UILabel = {
    let label = UILabel()
    label.text = "prevent capture 예제"
    label.textColor = .black
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
  }()
  private let secureTextField: UITextField = {
    let textField = UITextField()
    textField.placeholder = "텍스트 필드 placeholder"
    textField.isSecureTextEntry = true // <-
    textField.translatesAutoresizingMaskIntoConstraints = false
    textField.layer.cornerRadius = 8.0
    textField.layer.masksToBounds = true
    textField.layer.borderColor = UIColor.gray.cgColor
    textField.layer.borderWidth = 1.0
    return textField
  }()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .white
    
    [label, secureTextField]
      .forEach(view.addSubview(_:))
    
    NSLayoutConstraint.activate([
      label.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 32),
      label.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -32),
      label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24)
    ])
    NSLayoutConstraint.activate([
      secureTextField.leftAnchor.constraint(equalTo: label.leftAnchor),
      secureTextField.rightAnchor.constraint(equalTo: label.rightAnchor),
      secureTextField.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 16),
      secureTextField.heightAnchor.constraint(equalToConstant: 45)
    ])
  }
}
  • isSecureTextEntry가 켜져 있어도 placeholder는 그대로 표출

  • 입력 - 동그라미 형태로 입력

  • 캡쳐 - 입력한 부분이 안보이는 형태
    • 주의) 캡쳐가 안되는 테스트는 simulator가 아닌 디바이스에서 테스트해야 확인이 가능

UITextField로 Capture 막는 방법

  • 캡쳐 했을때 특정 뷰의 내용을 가리기 위해서, 특정 뷰에서 쉽게 캡쳐를 막을때 쓰는 메소드를 UIView extension으로 구현
    • isSecureTextEntry = true인 textField를 생성
    • textField를 뷰에다가 addSubview 후 center에 위치 (현재 textField는 투명색의 사이즈가 0,0인 상태)
    • 캡쳐하려는 뷰의 레이어를 textField.layer 사이에 끼워넣기
extension UIView {
  func makeSecure() {
    DispatchQueue.main.async {
      let textField = UITextField()
      textField.isSecureTextEntry = true
      
      self.addSubview(textField)
      textField.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
      textField.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
      
      // 캡쳐하려는 뷰의 레이어를 textField.layer 사이에 끼워넣기
      textField.layer.removeFromSuperlayer() // 이 코드가 없으면 run time error (layer 참조 관계에 cycle이 생성되므로)
      self.layer.superlayer?.insertSublayer(textField.layer, at: 0)
      textField.layer.sublayers?.last?.addSublayer(self.layer)
    }
  }
}

*주의) 인터넷에 있는 잘못된 코드를 보면 removeFromSuperlayer()가 없는 경우가 있는데, 이렇게되면 debug환경에서 layer cycle run time error가 나므로 주의

테스트

  • UITableView의 내용을 캡쳐하는 예제
  • UITableView 코드 준비
class ViewController: UIViewController {
  // MARK: Properties
  private var items: [String] {
    (0...30).map(String.init)
  }
  
  // MARK: UI
  ...
  private let tableView: UITableView = {
    let tableView = UITableView()
    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    tableView.translatesAutoresizingMaskIntoConstraints = false
    return tableView
  }()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    tableView.dataSource = self
    
    [label, secureTextField, tableView]
      .forEach(view.addSubview(_:))
    
	...
    NSLayoutConstraint.activate([
      tableView.leftAnchor.constraint(equalTo: label.leftAnchor),
      tableView.rightAnchor.constraint(equalTo: label.rightAnchor),
      tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24),
      tableView.heightAnchor.constraint(equalToConstant: 300)
    ])
  }
}

extension ViewController: UITableViewDataSource {
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    items.count
  }
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    cell.textLabel?.text = items[indexPath.row]
    cell.backgroundColor = .systemGray.withAlphaComponent(0.3)
    return cell
  }
}
  • tableView.makeSecure() 코드 추가
private let tableView: UITableView = {
  let tableView = UITableView()
  tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
  tableView.translatesAutoresizingMaskIntoConstraints = false
  tableView.makeSecure() // <-
  return tableView
}()

결과

  • 캡쳐 안한 경우 - UITableView가 보이는 상태

  • 캡쳐 하는 경우 - UITableView가 안보이는 상태

캡쳐했을때 UITableView가 안보이는 상태

  • Recording해도 UITableView가 안보이는 상태

Recording해도 UITableView가 안보이는 상태

cf) 캡쳐할때 캡쳐되는 화면에 특정 뷰를 띄우고 싶은 경우, 아래 makeSecureWithPlaceholder() 처럼 구현하여 사용

extension UIView {
  func makeSecure() {
    ...
  }
  
  public func makeSecureWithPlaceholder() {
    DispatchQueue.main.async {
      let backView = UIView(frame: self.frame)
      backView.backgroundColor = UIColor.lightGray
      self.superview?.insertSubview(backView, at: 0)
      
      self.makeSecure()
    }
  }
}

 

* 전체 코드: https://github.com/JK0369/ExPreventCapture

 

* 캡쳐, 녹화 감지 방법은 다음 포스팅 글 참고)

 

* 참고

https://developer.apple.com/documentation/uikit/uiscreen/2921652-captureddidchangenotification

https://developer.apple.com/documentation/uikit/uitextinputtraits/1624427-issecuretextentry

https://stackoverflow.com/questions/18680028/prevent-screen-capture-in-an-ios-app

 

Comments