Setting the delegate of a custom UITextView Delegate in Xcode

Viewed 155

I have a class that creates a custom UITextView and initializes it with hint text. The problem that I am having is that when I try to set the delegate of the UITextView inside of the initializer, the delegate functions are not called. I am also not completely sure if the textView class should be the delegate. Below is the custom UITextView class:

class TextView: UITextView, UITextViewDelegate {
    var test: TextView?
    var hintLabel, smallHintLabel: UILabel!
    var underline: UIView!

    // MARK: - Init
    
    /// Initialize text field
    /// - Parameters:
    ///   - text: Text field text
    ///   - hintText: Text field hint text (a.k.a. placeholder/description)
    ///   - isLight: Whether text field is light style
    convenience init(text: String? = nil, hintText: String, isLight: Bool = false) {
        self.init(frame: .zero, textContainer: .none)
        
        self.heightAnchor.constraint(equalToConstant: 57).isActive = true
        //setting the delegate
        test = TextView()
        self.delegate = test
        
        // hint
        hintLabel = UILabel()
        hintLabel.text = hintText
        hintLabel.font = UIFont.systemFont(ofSize: largeSize, weight: weight)
        hintLabel.textColor = hintColor
        hintLabel.alpha = text == nil || text!.isBlank ? 1 : 0
        self.addSubview(hintLabel)
        
        hintLabel.translatesAutoresizingMaskIntoConstraints = false
        hintLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: insets.top / 2).isActive = true
        hintLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        hintLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
        
        // small hint
        smallHintLabel = UILabel()
        self.smallHintLabel.text = hintText
        self.smallHintLabel.font = UIFont.systemFont(ofSize: smallSize, weight: weight)
        self.smallHintLabel.textColor = hintColor
        self.smallHintLabel.alpha = text == nil || text!.isBlank ? 0 : 1
        self.addSubview(smallHintLabel)
        
        smallHintLabel.translatesAutoresizingMaskIntoConstraints = false
        smallHintLabel.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        smallHintLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        smallHintLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
        
        // text
        self.text = text
        self.font = UIFont.systemFont(ofSize: largeSize, weight: weight)
        self.textColor = textColor
        self.tintColor = isLight ? .white : Color.blue
        self.keyboardAppearance = isLight ? .dark : .light
        
        // underline
        underline = UIView()
        underline.backgroundColor = lineColor
        self.addSubview(underline)
        
        underline.translatesAutoresizingMaskIntoConstraints = false
        underline.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        underline.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        underline.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
        underline.heightAnchor.constraint(equalToConstant: 1).isActive = true
        
    }
    
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

/// Adjusts the position of the hint text when text is changed in field
    func textViewDidChange(_ textView: UITextView) {
        print(textView.text)
        if self.text?.isEmpty ?? true {
            moveHintUp()
        } else {
            moveHintUp()
        }
    }

I am creating an instance of this function and adding it to a view.


class MessageComposeView: UIView, UITextViewDelegate {
    
    var textField: TextView!
    private var sendButton: SendButton!
    
    weak var delegate: MessageComposeViewDelegate?

    init() {
        super.init(frame: .zero)
        
        // background
        
        // text field
        textField = TextView(hintText: "Type a message")
        textField.delegate = self
        self.addSubview(textField)
        
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.topAnchor.constraint(equalTo: self.topAnchor, constant: 30).isActive = true
        textField.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -30).isActive = true
        textField.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
        textField.heightAnchor.constraint(equalToConstant: 50).isActive = true
        textField.sizeToFit()
        
        // send button
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    
    // MARK: - Actions
    
    /// Called when the message text changes
    func textViewDidChange(_ textView: UITextView) {
        sendButton.setState(textField.text?.isBlank == true ? .disabled : .normal)
        
        let size = CGSize(width: 200, height: 999999)
        let estimatedSize = textField.sizeThatFits(size)

        textField.constraints.forEach { constraint in
            if constraint.firstAttribute == .height {
                constraint.constant = estimatedSize.height
            }
        }
    }

1 Answers

My guess is that you aren't actually calling the initializer that you mean to be.

For example, if you call TextView(), your convenience init will not be called. It will, however, if you call TextView(hintText: "") or any other variations of the initializer that you've created.

Secondly, although it's not directly related to the above issue, I question why you're creating another TextView and assigning it to be the delegate. Why not just assign the delegate to self?

Related