I have done some research and found this post: Add placeholder text inside UITextView in Swift?
Incorporating the above example in my code, I have the following in a blank xcode UIKit project:
import UIKit
class ViewController: UIViewController {
    var sampleTextView       = UITextView()
    let placeholderText      = "Type Something"
    let placeholderTextColor = UIColor.lightGray
    let normalTextColor      = UIColor.label
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemGray
        
        sampleTextView.delegate     = self
        sampleTextView.text         = placeholderText
        sampleTextView.textColor    = placeholderTextColor
        sampleTextView.becomeFirstResponder()
        sampleTextView.selectedTextRange = sampleTextView.textRange(from: sampleTextView.beginningOfDocument, to: sampleTextView.beginningOfDocument)
        view.addSubview(sampleTextView)
        sampleTextView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            sampleTextView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
            sampleTextView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10),
            sampleTextView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
            sampleTextView.heightAnchor.constraint(equalToConstant: 100)
        ])
    }
}
extension ViewController: UITextViewDelegate {
    
    func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text.isEmpty {
            textView.text = placeholderText
            textView.textColor = placeholderTextColor
        }
    }
    
    
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        // Combine the textView text and the replacement text to
        // create the updated text string
        let currentText:String = textView.text
        let updatedText = (currentText as NSString).replacingCharacters(in: range, with: text)
        // If updated text view will be empty, add the placeholder
        // and set the cursor to the beginning of the text view
        if updatedText.isEmpty {
            textView.text = placeholderText
            textView.textColor = placeholderTextColor
            textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
        }
        // Else if the text view's placeholder is showing and the
        // length of the replacement string is greater than 0, set
        // the text color to black then set its text to the
        // replacement string
         else if textView.textColor == placeholderTextColor && !text.isEmpty {
            textView.textColor = normalTextColor
            textView.text = text
        }
        // For every other case, the text should change with the usual
        // behavior...
        else {
            return true
        }
        // ...otherwise return false since the updates have already
        // been made
        return false
    }
    
    
    func textViewDidChangeSelection(_ textView: UITextView) {
        if self.view.window != nil {
            if textView.textColor == placeholderTextColor {
                textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
            }
        }
    }
}
But I have two bugs with this that for the life of me, I can't work out:
- This first is using the predictive text above the keyboard toolbar. The first selected work is repeated - see screenshot below:
- If typing, the keyboard's SHIFT key is pressed as expected for the first letter, but then stays pressed for the second one as well - see screenshot below:
Apologies if these are basic but I'm stumped.


 
    