Skip to content

Instantly share code, notes, and snippets.

@0xWDG
Forked from damian-rzeszot/SecureLabel.swift
Created April 16, 2022 09:46
Show Gist options
  • Save 0xWDG/5ed15f3ca9d1505860669a481d952ada to your computer and use it in GitHub Desktop.
Save 0xWDG/5ed15f3ca9d1505860669a481d952ada to your computer and use it in GitHub Desktop.
_UITextLayoutCanvasView
import UIKit
lazy var hiddenView: UIView = {
let hiddenView: UIView
// iOS 14.1 -> _UITextFieldCanvasView
// iOS 15.0 -> _UITextLayoutCanvasView
if let klass = NSClassFromString("_UITextFieldCanvasView") as? UIView.Type {
hiddenView = klass.init()
hiddenView.layer.perform(Selector(("setDisableUpdateMask:")), with: 0x12)
} else {
let textField = UITextField()
textField.isSecureTextEntry = true
hiddenView = textField.layer.sublayers?.first?.delegate as! UIView
hiddenView.subviews.forEach { $0.removeFromSuperview() }
}
addSubview(hiddenView)
hiddenView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hiddenView.leftAnchor.constraint(equalTo: leftAnchor),
hiddenView.rightAnchor.constraint(equalTo: rightAnchor),
hiddenView.topAnchor.constraint(equalTo: topAnchor),
hiddenView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
return hiddenView
}()
lazy var hiddenLabel: UILabel = {
let sublabel = UILabel()
hiddenView.addSubview(sublabel)
sublabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
sublabel.leftAnchor.constraint(equalTo: hiddenView.leftAnchor),
sublabel.rightAnchor.constraint(equalTo: hiddenView.rightAnchor),
sublabel.topAnchor.constraint(equalTo: hiddenView.topAnchor),
sublabel.bottomAnchor.constraint(equalTo: hiddenView.bottomAnchor)
])
return sublabel
}()
}
@0xWDG
Copy link
Author

0xWDG commented Oct 25, 2022

Hi @alidinc,

please try this (please note I have not tested this on iOS 16).

import UIKit
extension UIView {
    func preventScreenshot() {
        guard superview != nil else {
            for subview in subviews {
                subview.preventScreenshot()
            }
            return
        }

        let guardTextField = UITextField()
        guardTextField.backgroundColor = .red
        guardTextField.translatesAutoresizingMaskIntoConstraints = false
        guardTextField.tag = Int.max
        guardTextField.isSecureTextEntry = true

        addSubview(guardTextField)
        guardTextField.isUserInteractionEnabled = false
        sendSubviewToBack(guardTextField)

        layer.superlayer?.addSublayer(guardTextField.layer)
        guardTextField.layer.sublayers?.first?.addSublayer(layer)

        guardTextField.centerYAnchor.constraint(
            equalTo: self.centerYAnchor
        ).isActive = true

        guardTextField.centerXAnchor.constraint(
            equalTo: self.centerXAnchor
        ).isActive = true
    }
}

@alidinc
Copy link

alidinc commented Oct 25, 2022

this works like a charm, 🎉 thanks @0xWDG 🙌

@0xWDG
Copy link
Author

0xWDG commented Oct 25, 2022

😃

@alidinc
Copy link

alidinc commented Oct 25, 2022

Just noticed, this doesn't work when it's applied to any views on a viewController :/

@0xWDG
Copy link
Author

0xWDG commented Oct 26, 2022

Just noticed, this doesn't work when it's applied to any views on a viewController :/

Is the view used in SwiftUI? i was not able to get it to work in any SwiftUI views.

For (classic) viewcontrollers, it should work on elements (maybe on the whole view, but never tested that).

What i did was:

class VC: UIViewController {
    @IBOutlet var label1: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func hide(_ sender: Any) {
        self.label1.preventScreenshot()
    }
}

@alidinc
Copy link

alidinc commented Oct 26, 2022

I tried it on a view controller's view in UIKit or any other view covering the entire space as that's what I'm trying to block screenshots for. it only works on a single element like the label you mentioned but not for the entire view.

@nthnhung23
Copy link

I want to prevent taking screenshots from the view-controller. Could you give me some advice or guidance?
Because when I tried to add it to view-controller by command self.view.makeSecure(), my app was crashed.

@0xWDG
Copy link
Author

0xWDG commented Jun 18, 2023

I want to prevent taking screenshots from the view-controller. Could you give me some advice or guidance?
Because when I tried to add it to view-controller by command self.view.makeSecure(), my app was crashed.

Hi @nthnhung23,

It does not work on a whole screen, the screen will turn black if you try that.

What does work is:

  1. Create a view (in your main view), and hide all sensitive stuff in there. see image below

image

  1. Use something like this
// Register that view as outlet.
    @IBOutlet var view1: UIView!

// Hide the outlet
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.view1.preventScreenshot()
    }

Hope this helps :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment