Crocobag
Crocobag

Reputation: 2340

Swift 4 - Snapshotting error with custom UIView

In my project, I made a subclass of a UIView, which is basically a container for a UITextField. Everything works fine except that I get a strange error message in my console when I am selecting the textfield on screen, and I don't know how to do to make it disapear :

[Snapshotting] Snapshotting a view (0x119d2d240, _UIReplicantView) that has not been rendered at least once requires afterScreenUpdates:YES.

I tried to place some view.layoutIfNeeded() at some points but the message remains. Here is my code :

import UIKit

class BTShimmerTextField: BTShimmerView {

    let nestedTextField = UITextField()

    var didPressReturn: (() -> ())? = nil

    var placeholder: String? {
        get {
            return nestedTextField.placeholder
        }
        set {
            nestedTextField.placeholder = newValue
        }
    }

    var darkColor: UIColor? {
        get {
            return darkView.backgroundColor
        }
        set {
            darkView.backgroundColor = newValue
        }
    }

    var lightColor: UIColor? {
        get {
            return lightView.backgroundColor
        }
        set {
            lightView.backgroundColor = newValue
        }
    }

    var isSecureTextEntry: Bool {
        get {
            return nestedTextField.isSecureTextEntry
        }
        set {
            nestedTextField.isSecureTextEntry = newValue
        }
    }

    var autocorrectionType: UITextAutocorrectionType {
        get {
            return nestedTextField.autocorrectionType
        }
        set {
            nestedTextField.autocorrectionType = newValue
        }
    }

    override init(){
        super.init()
        setupTextField()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupTextField()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupTextField()
    }

    fileprivate func setupTextField(){

        nestedTextField.backgroundColor = UIColor.white // UIColor(named: "BTDark")
        nestedTextField.textAlignment = .center
        nestedTextField.tintColor = UIColor(named: "BTDark")
        nestedTextField.font = UIFont(name: "Metropolis-Regular", size: 16)
        nestedTextField.keyboardAppearance = .dark
        nestedTextField.delegate = self

        self.addSubview(nestedTextField)

        nestedTextField.anchor(top: self.topAnchor, leading: self.leadingAnchor, trailing: self.trailingAnchor, bottom: self.bottomAnchor, padding: UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2))

    }

    @discardableResult
    override func becomeFirstResponder() -> Bool {
        return nestedTextField.becomeFirstResponder()
    }

}

extension BTShimmerTextField: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if textField == nestedTextField {
            nestedTextField.resignFirstResponder()
            if let didPressReturn = didPressReturn { didPressReturn() } 
            return false
        }
        return true
    }
}

Note: The BTShimmerView is also a subclass of a UIView, in which I set 2 UIView with 2 different backgrounds, with a CAGradientLayer to make some kind of reflection effect.

Here is how I add it to my view :

import UIKit

class LoginViewController: UIViewController {

    override var prefersStatusBarHidden: Bool {
        return true
    }


    var loginView = BTShimmerTextField()
    var passwordView = BTShimmerTextField()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor(named: "BTDark")
        setupLoginViews()

    }


    fileprivate func setupLoginViews(){

        loginView.darkColor = UIColor(named: "BTDarkGold")
        loginView.lightColor = UIColor(named: "BTGold")
        loginView.placeholder = "Identifiant"
        loginView.didPressReturn = { self.passwordView.becomeFirstResponder() }
        loginView.autocorrectionType = .no
        view.addSubview(loginView)

        loginView.translatesAutoresizingMaskIntoConstraints = false
        loginView.heightAnchor.constraint(equalToConstant: 30).isActive = true
        loginView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.7).isActive = true
        loginView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        loginView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -25).isActive = true


        passwordView.darkColor = UIColor(named: "BTDarkGold")
        passwordView.lightColor = UIColor(named: "BTGold")
        passwordView.placeholder = "Mot de passe"
        passwordView.isSecureTextEntry = true
        passwordView.autocorrectionType = .no
        view.addSubview(passwordView)

        passwordView.translatesAutoresizingMaskIntoConstraints = false
        passwordView.heightAnchor.constraint(equalToConstant: 30).isActive = true
        passwordView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.7).isActive = true
        passwordView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        passwordView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 25).isActive = true


    }

}

Does anyone know how I can resolve this error? Is this error really problematic?

Upvotes: 1

Views: 1907

Answers (1)

Crocobag
Crocobag

Reputation: 2340

Found the solution. I had to set my constraints by overriding updateConstraints method, considering it's a subclass, like so :

[...]
// Constraints
override func updateConstraints() {
    if(shouldSetupConstraints) {
        setupTextFieldConstraints()
    }
    super.updateConstraints()
}

fileprivate func setupTextFieldConstraints(){
     nestedTextField.anchor(top: self.topAnchor, leading: self.leadingAnchor, trailing: self.trailingAnchor, bottom: self.bottomAnchor, padding: UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2))
}
[...]

Upvotes: 1

Related Questions