I'm still learning Swift, and I'm trying to create an extension to add a placeholder to UITextView.
My idea for it is to create 2 UITextViews, one with a text as a placeholder, and when the user starts editing the text, it's actually hidden and the user is modifying the other UITextView.
However, my question here is not for having a placeholder in UITextView, but it's in regards of how-to use extensions to solve this problem.
My question is, what do I have to change in my implementation to create an extension that would look from the caller side as:
myTextView.placeholder("a placeholder text..")
So far, I have created it in my UIViewController, and need to move it to an extension, but I have a lot of stored properties, so it wouldn't work.
Here's my code:
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
let myTextView: UITextView = {
let textView = UITextView()
textView.tag = 0
textView.backgroundColor = UIColor.yellow
textView.layer.cornerRadius = 8
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let textViewPlaceHolder: UITextView = {
let textViewPlaceHolder = UITextView()
textViewPlaceHolder.tag = 1
textViewPlaceHolder.text = "Placeholder text.."
textViewPlaceHolder.textColor = UIColor.lightGray
textViewPlaceHolder.backgroundColor = UIColor.clear
textViewPlaceHolder.translatesAutoresizingMaskIntoConstraints = false
return textViewPlaceHolder
}()
func textViewDidChange(_ textView: UITextView) {
myTextView.becomeFirstResponder()
if textView.tag == 1 && (myTextView.text != nil || myTextView.text != "") {
textView.isHidden = true
textViewPlaceHolder.resignFirstResponder()
} else if textView.tag == 0 {
if myTextView.text == nil || myTextView.text == "" {
textViewPlaceHolder.becomeFirstResponder()
myTextView.resignFirstResponder()
textViewPlaceHolder.isHidden = false
textViewPlaceHolder.text = "Placeholder text.."
}
}
}
func textViewDidBeginEditing(_ textView: UITextView) {
DispatchQueue.main.async {
self.textViewPlaceHolder.selectedRange = NSMakeRange(0, 0)
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
view.addSubview(myTextView)
textViewConstraints()
view.addSubview(textViewPlaceHolder)
myTextViewPHConstraints()
myTextView.delegate = self
textViewPlaceHolder.delegate = self
}
func textViewConstraints() {
myTextView.widthAnchor.constraint(equalTo: self.view.widthAnchor, constant: -16).isActive = true
myTextView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
myTextView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
myTextView.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
func myTextViewPHConstraints() {
textViewPlaceHolder.widthAnchor.constraint(equalTo: self.view.widthAnchor, constant: -16).isActive = true
textViewPlaceHolder.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
textViewPlaceHolder.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
textViewPlaceHolder.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
}
As I mentioned above, I'm still learning Swift and this question is not for solving a very specific problem, it's more than that, it's meant to learn how to solve problems in Swift extensions.
Note: Don't use the code above to solve placeholder problem as it's no perfectly working.
You can do something like this:
extension UITextView {
private struct AssociatedKeys {
static var placeholder = "placeholder"
}
var placeholder: String! {
get {
guard let placeholder = objc_getAssociatedObject(self, &AssociatedKeys.placeholder) as? String else {
return String()
}
return placeholder
}
set(value) {
objc_setAssociatedObject(self, &AssociatedKeys.placeholder, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}