My problem is: I have a scrolling problem with my UITextView. I can scroll through the UITextView.text when I enter a large amount of data, but I cannot scroll through the UITextView.text if I go to a second view controller, hit the back button, and return to the first view controller. My setup is as follows: I have two view controllers embedded in a navigation controller with a segue between the first and second view controller. The first view controller has a title, a name label, a UITextView, a placeholder label, which is hidden or not hidden based on whether the UITextView has text or not, and a Save button. A segue to the second view controller occurs when the Save button is tapped.
The second view controller has a label that displays “Hello World”.
Lastly, I have a singleton, which is used to save the UITextView.text when the segue happens and to repopulate the UITextView.text when the first view controller gets control back. Following is my code.
First View Controller is: import UIKit class NameViewController: UIViewController {
let backgroundColor = UIColor.systemBackground
lazy var nameLabel: UILabel = {
let label = UILabel()
return label
}()
lazy var textView: UITextView = {
let textView = UITextView()
return textView
}()
private let placeholderLabel: UILabel = {
let placeholderLabel = UILabel()
return placeholderLabel
}()
lazy var saveButton: UIButton = {
let button = UIButton()
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
addSubViews()
buildConstraints()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
textView.isScrollEnabled = true
textView.isUserInteractionEnabled = true
buildPriorText()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
func setUpUI() {
setUpView()
setUpNameLabel()
setUpTextView()
setUpPlaceHolderLabel()
setUpSaveButton()
}
func setUpView() {
view.backgroundColor = backgroundColor
self.navigationItem.title = "Title"
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapToDismiss)))
}
func setUpNameLabel() {
nameLabel.backgroundColor = UIColor.white
nameLabel.textColor = UIColor.black
nameLabel.text = "Name: "
nameLabel.textAlignment = .center
nameLabel.font = UIFont(name: "Helvetica Neue", size: 20)
nameLabel.numberOfLines = 0
nameLabel.adjustsFontSizeToFitWidth = true
nameLabel.minimumScaleFactor = 0.1
nameLabel.lineBreakMode = .byWordWrapping
nameLabel.layer.cornerRadius = 5
nameLabel.layer.borderWidth = 1
nameLabel.layer.borderColor = UIColor.label.cgColor
nameLabel.sizeToFit()
}
func setUpSaveButton() {
let addSaveButtonTitle = "Save"
saveButton.setTitle(addSaveButtonTitle, for: .normal)
saveButton.backgroundColor = UIColor.systemBlue
saveButton.titleLabel?.font = UIFont(name: "Helvetica Neue", size: 20)
saveButton.titleLabel?.adjustsFontSizeToFitWidth = true
saveButton.titleLabel?.minimumScaleFactor = 0.1
saveButton.setTitleColor(.white, for: .normal)
saveButton.layer.cornerRadius = 10.00
saveButton.layer.masksToBounds = true
saveButton.addTarget(self, action:#selector(saveButtonTapped), for: .touchUpInside)
var configuration = UIButton.Configuration.plain()
configuration.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 10, bottom: 5, trailing: 10)
saveButton.configuration = configuration
}
func setUpTextView() {
textView.delegate = self
textView.isScrollEnabled = true
textView.isUserInteractionEnabled = true
textView.backgroundColor = UIColor(named: "SnowColor")
textView.font = UIFont(name: "Arial", size: 20)
textView.textColor = UIColor(named: "BlackColor")
textView.autocorrectionType = .no
textView.textContainerInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
textView.layer.cornerRadius = 5
textView.layer.borderWidth = 1
textView.layer.borderColor = UIColor.label.cgColor
textView.clipsToBounds = true
textView.sizeToFit()
}
func setUpPlaceHolderLabel() {
placeholderLabel.backgroundColor = UIColor.clear
placeholderLabel.text = NSLocalizedString("enter text", comment: "")
placeholderLabel.textColor = UIColor.lightGray
placeholderLabel.font = UIFont(name: "Arial", size: 20)
placeholderLabel.sizeToFit()
placeholderLabel.frame.origin = CGPoint(x: 5, y: (textView.font?.pointSize)! / 2)
}
func addSubViews() {
view.addSubview(nameLabel)
view.addSubview(textView)
textView.addSubview(placeholderLabel)
view.addSubview(saveButton)
}
func buildConstraints() {
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
nameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
nameLabel.heightAnchor.constraint(equalToConstant: 60).isActive = true
textView.translatesAutoresizingMaskIntoConstraints = false
textView.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 8).isActive = true
textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true
textView.bottomAnchor.constraint(equalTo: saveButton.topAnchor, constant: -8).isActive = true
placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
placeholderLabel.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 10).isActive = true
placeholderLabel.trailingAnchor.constraint(equalTo: textView.trailingAnchor).isActive = true
placeholderLabel.topAnchor.constraint(equalTo: textView.topAnchor).isActive = true
placeholderLabel.heightAnchor.constraint(equalToConstant: 60).isActive = true
saveButton.translatesAutoresizingMaskIntoConstraints = false
saveButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
saveButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
saveButton.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true
saveButton.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor, constant: -10).isActive = true
}
func buildPriorText() {
guard let holdVariableSavedTextViewData = Variables.savedTextViewData else {return}
textView.text = holdVariableSavedTextViewData
}
@objc private func didTapToDismiss() {
view.endEditing(true)
}
@objc func saveButtonTapped() {
textView.resignFirstResponder()
if textView.text == "\n" {
segueToSecondVC()
} else if textView.text.isEmpty {
segueToSecondVC()
} else {
Variables.savedTextViewData = textView.text
segueToSecondVC()
}
}
func segueToSecondVC() {
performSegue(withIdentifier: "seguefromnamevctosecondvc", sender: nil)
}
}
extension NameViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
if textView.text.isEmpty {
placeholderLabel.isHidden = false
textView.backgroundColor = UIColor.white
} else {
placeholderLabel.isHidden = true
textView.backgroundColor = UIColor.lightGray
}
}
func textViewDidBeginEditing(_ textView: UITextView) {
textView.backgroundColor = UIColor.lightGray
placeholderLabel.isHidden = true
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
placeholderLabel.isHidden = false
textView.backgroundColor = UIColor.white
} else {
placeholderLabel.isHidden = true
textView.backgroundColor = .lightGray
}
}
}
The second view controller is: SecondViewController: import UIKit class SecondViewController: UIViewController { lazy var label: UILabel = { let label = UILabel() return label }()
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
addSubViews()
addConstraints()
label.text = "Hello World"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
func addSubViews() {
view.addSubview(label)
}
func setUpUI() {
setUpLabel()
}
func setUpLabel() {
label.backgroundColor = UIColor.white
label.textColor = UIColor.black
}
func addConstraints() {
label.translatesAutoresizingMaskIntoConstraints = false
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
label.heightAnchor.constraint(equalToConstant: 100).isActive = true
label.widthAnchor.constraint(equalToConstant: 100).isActive = true
}
}
The Singleton is in a Swift file and the code is import Foundation struct Variables { static var savedTextViewData: String? }
I entered a large amount of data into the inputText.text field and was able to scroll up and down and make corrections. I selected the Save button, and my text was saved in the singleton, and the second view controller took charge and displayed the label as expected. I then selected the back button and returned to the first view controller, and my original text was displayed in the inputText.text as expected. I wanted to make corrections, but I was unable to scroll up and down to make the necessary corrections. I tried multiple variations of height and width constraints, but the scrolling problem was not fixed. I was able to correct the problem by removing the placeholder label from the subview along with its constraints, but when I added the placeholder label back into the subview the problem returned. I added logic to bring UITextView to the front, thinking the placeholder label was on top of the UITextView and that did not correct the problem. I verified the placeholder label was hidden when the inputText.text was not empty and retested, and that did not correct the problem.
Instead of add placeholderLabel
to textView
, you should add placeholderLabel
to view
of NameViewController
:
Change this line textView.addSubview(placeholderLabel)
to view.addSubview(placeholderLabel)
. There might be hidden logic in UITextView to interact with its subviews which cause your bug