swiftcalayeruiwindow

why does write addSublayer twice to block screenshot in swift?


I found an extension function to block screeenShot in iOS.
But I wounder to know why addSublayer twice.

self.layer.superlayer?.addSublayer(textfield.layer)
textfield.layer.sublayers?.last?.addSublayer(self.layer)

what's these code mean?
I can't figure out.
I need some explains to let me know how it works.
Thanks.

extension UIWindow {
    
    func makeSecure() {

        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

        self.layer.superlayer?.addSublayer(textfield.layer)
        textfield.layer.sublayers?.last?.addSublayer(self.layer)
    }
    
}

Solution

  • This method of preventing screenshots uses the unique behaviour of text fields with isSecureTextEntry = true. These text fields cannot be screenshotted. They appear black on screenshots.

    This is why the code adds self.layer as a sublayer of the text field's last sublayer. The text field's last sublayer happens to be the layer that doesn't get screenshotted (though I don't think this is guaranteed by the documentation). By adding self.layer to this, the entire self.layer will not appear in screenshots.

    Of course, we haven't actually added the text field's layer to the rest of the layer hierarchy yet, similar to the common mistake of creating a view, and then forgetting to addSubview to an existing view. At this point the text field is just some random thing in memory with nothing else knowing about it.

    This is why we add the text field's layer as a sublayer of self.layer.superlayer. Now the system actually knows the secure text field exists and prevents screenshots.

    Note that we can't add it to self.layer, because that would cause a cycle in the layers' relationships - self.layer would be a child of itself, and at the same time, a parent of itself. Also, by adding as a sibling of the window's layer (instead of a sublayer of the window's layer), it also doesn't appear on the screen.

    FYI, the layer hierarchy looks like this in the end:

          the rest of the layer hierarchy
                         |
                 self.layer.superlayer
                    |         \
                    |   textfield.layer
                    |      /
                    |     /
                    |    /
                    |   /
               self.layer     
                    |
               your views
    

    This diagram makes it look as if self.layer have two superlayers when it actually doesn't. self.layer.superlayer doesn't change when self.layer is added as a sublayer of textfield.layer. The lines here are only showing the sublayer relationship - the layers on the bottom of the lines are sublayers of the layers on the top of the lines.