Search code examples
iosswiftcalayer

Unable to remove a CALayer from UITextField


So, I have this custom UITextField and I have two methods to add CALayer and remove the CALayer but remove is not working.

@IBDesignable class AppTextField : UITextField {

    private let bottomLine = CALayer()

    override func layoutSubviews() {
        self.font = .systemFont(ofSize: 20)
        self.addBottomLine()
        self.clearButtonMode = .unlessEditing
        super.layoutSubviews()
    }

    func removeBttomLine() {
        bottomLine.removeFromSuperlayer()
    }

    private func addBottomLine() {
        bottomLine.frame = CGRect(origin: CGPoint(x: 0, y: self.frame.height + 4), size: CGSize(width: self.frame.width, height: 1))
        bottomLine.backgroundColor = UIColor.init(hexString: "#DCCFCA")?.cgColor
        self.borderStyle = .none
        self.layer.addSublayer(bottomLine)
    }
}

Solution

  • The only thing you should do in layoutSubviews() is update frames as necessary.

    This will show the red line when the field is NOT being edited, and will remove it while the field IS being edited:

    @IBDesignable class AppTextField : UITextField {
    
        private let bottomLine = CALayer()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            commonInit()
        }
        func commonInit() -> Void {
            self.font = .systemFont(ofSize: 20)
            self.backgroundColor = .white
            self.clearButtonMode = .unlessEditing
            self.borderStyle = .none
            bottomLine.backgroundColor = UIColor.red.cgColor
            addBottomLine()
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
            var r = bounds
            r.origin.y = bounds.maxY
            r.size.height = 4.0
            bottomLine.frame = r
        }
    
        func removeBottomLine() {
            bottomLine.removeFromSuperlayer()
        }
        private func addBottomLine() {
            self.layer.addSublayer(bottomLine)
        }
    
        override func resignFirstResponder() -> Bool {
            super.resignFirstResponder()
            addBottomLine()
            return true
        }
        override func becomeFirstResponder() -> Bool {
            super.becomeFirstResponder()
            removeBottomLine()
            return true
        }
    }