Best way to handle textViewChanged in Swift 5.5 - swift

I'm trying to add attributed strings to some text in a UITextView on text change in Swift 5.5
What's the best approach, a UIView or a UIViewController?
Sample code would really help me out as I can't seem to get either approach to work atm.
Thanks

class ViewController: UIViewController, UITextViewDelegate { //If your class is not conforms to the UITextViewDelegate protocol you will not be able to set it as delegate to UITextView
#IBOutlet weak var bodyText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
bodyText.delegate = self //Without setting the delegate you won't be able to track UITextView events
}
func textViewDidChange(_ textView: UITextView) { //Handle the text changes here
print(textView.text); //the textView parameter is the textView where text was changed
}
}
#Rob Hope this boilerplate works for you :)

Related

How to set delegate in a protocol extension for a UIkit class

I'm trying to have all my TextView instances use this same delegate function from UITextPasteDelegate without copying the delegate function into each VC.
Xcode 10.3 compiler doesn't throw an error when I do this:
extension UITextView : UITextPasteDelegate {
private func textPasteConfigurationSupporting(_ textPasteConfigurationSupporting: UITextPasteConfigurationSupporting, shouldAnimatePasteOf attributedString: NSAttributedString, to textRange: UITextRange) -> Bool {
return false
}
}
However the problem I run into is when I try to set this delegate in my VC. The code looks something like this:
class viewController : UIViewController{
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.pasteDelegate = self
}
}
It breaks on the line textView.pasteDelegate = self and I understand that it breaks here because the viewController class is not implementing UITextPasteDelegate. But my question is whether it is possible to assign textView.pasteDelegate = ? to something. I've tried textView.pasteDelegate = UITextView.Type but that also fails.
Is there a way to achieve this?
Extensions cannot override methods. This is not supported. Sometimes it may happen to work, but it is undefined behavior.
From Extensions in The Swift Programming Language:
Extensions can add new functionality to a type, but they cannot override existing functionality.
(This is all true, but doesn't actually apply to this question; see more below.)
This is exactly what subclasses are for. In Interface Builder, select your subclass and it'll still wire up as a UITextView. It'll just be configured the way you want. For example:
class NonAnimatingTextView: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
self.pasteDelegate = self
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
extension NonAnimatingTextView: UITextPasteDelegate {
func textPasteConfigurationSupporting(
_ textPasteConfigurationSupporting: UITextPasteConfigurationSupporting,
shouldAnimatePasteOf attributedString: NSAttributedString,
to textRange: UITextRange) -> Bool { false }
}
For completeness, the exact thing you're trying to do is in fact possible; it's just not the right approach. (In this case it works out that it's not an override, so it's possible.)
First, you marked the method as private when it should be public and ideally #objc (#objc may not be required here, but I would add it to be very clear):
extension UITextView : UITextPasteDelegate {
#objc public func textPasteConfigurationSupporting(_ textPasteConfigurationSupporting: UITextPasteConfigurationSupporting, shouldAnimatePasteOf attributedString: NSAttributedString, to textRange: UITextRange) -> Bool {
return false
}
}
And then the UITextView is its own pasteDelegate:
class ViewController : UIViewController{
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.pasteDelegate = textView
}
}
But I still recommend the subclass. It's much clearer what you're doing.

Keyboard not being dismissed

I realize this is a trivial question with tons of answer on SO. I may just need a pair of fresh eyes as I've triple checked everything and cannot see where I am going wrong with this. I just want to dismiss the keyboard on hitting the return key. I'm setting the delegate properly and implementing the proper methods, so why won't the keyboard dismiss?
Does having a collectionView in the viewController complicate things? (text field is NOT inside collectionView)
class SearchController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UITextFieldDelegate {
#IBOutlet weak var searchBar: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
setupUI()
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
searchBar.resignFirstResponder()
return true
}
Things I've tried:
cleaning the project
restarting xcode
changing the searchBar.resignFirstResponder() to textField.resignFirstResponder()
setting the delegate for the text field inside IB instead of in viewDidLoad
What the heck am i missing here!?
You need to implement the proper text field delegate method. There is no such delegate method as textFieldShouldReturn(textField:). The proper method is textFieldShouldReturn(_:).
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchBar.resignFirstResponder()
return false
}
The _ makes a huge difference. You may have copied an old Swift 2 implementation.
It's best to let Xcode perform code completion to ensure you get the correct signature of any method you are implementing or calling.
Use this code
view.endEditing(true)

How to detect text view begin editing and end editing in swift 3

I found related questions and answers in objective-c but I couldn't find a proper way to this with swift? Is there methods to detect for text view same as text filed in swift?
I want to solve the issue hiding text view by keyboard. For that I need a way to detect start and end edit text view.
Any help would be appreciated.
Note: I'm using swift 3.
Just as Bawpotter said, "Use the delegate methods for UITextView". Here is what they look like in Swift 3:
func textViewDidBeginEditing(_ textView: UITextView) {
// Run code here for when user begins type into the text view
}
func textViewDidEndEditing(_ textView: UITextView) {
// Run code here for when user ends editing text view
}
Also make sure your UITextView's delegate is set to self or wherever these methods are in your app
Conform your ViewController class to UITextViewDelegate and then write
textView.delegate = self in your viewDidLoad(). After that write delegate methods for your textView as:
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
// your code here
}
func textViewDidBeginEditing(_ textView: UITextView) {
// your code here
}
func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
// your code here
}
func textViewDidEndEditing(_ textView: UITextView) {
// your code here
}

My scrollViewDidScroll function is not receiving calls

I'm trying to check if a user is scrolling up and down on my app but it's not calling my scrollViewDidScroll method
Any ideas why it's not printing received scroll when I scroll up and down on the app?
import UIKit
class CreateAccount: UIViewController, UITextFieldDelegate, UIScrollViewDelegate {
#IBOutlet weak var scrollViewer: UIScrollView!
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var reenterPasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func scrollViewDidScroll(scrollView: UIScrollView) {
print("received scroll")
}
}
Add the scrollview delegate. Whenever you implement a delegate method you need tell it what controller to use, usually it will be self. It's caught me out a few times.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
scrollViewer.delegate = self
}
You need to set delegate of scrollview also don't use tag to differentiate 2 scrollview instead of that create 2 outlets of scrollview and use that in UIScrollViewDelegate methods like this.
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView == scrollView1 {
print("received scrollview 1")
}
else if scrollView == scrollView2 {
print("received scrollview 2")
}
}
Delegate are given properly even if your scrollview delegate method not calling then please check you delegate method which you have added for scrollview, this may give warning due to using old swift version scrollview delegate with swift 4.0
Please correct it with below if this is the case:
Old Method:
func scrollViewDidScroll(scrollView: UIScrollView) {
}
New Method:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
}
You can set the .delegate = self as Sarcoma suggests, or:
CTRL drag from scrollView in storyboard onto the File Owner (it is located in the view hierarchy list, actually it is right by the yellow circle icon by the name of your controller: [O] Create Account and choose
delegate

What is missing from this minimal UITextFieldDelegate implementation, to cause crashing?

The crash occurs upon selecting the Text Field in the simulator. The debugger seems to tell me nothing useful.
import UIKit
class TextField: NSObject, UITextFieldDelegate {}
In the View Controller:
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = TextField()
}
#IBOutlet weak var textField: UITextField! // assigned in IB.
You can't set the delegate to TextField()
Just set the delegate to self, because you use the UITextFieldDelegate in your class:
textField.delegate = self
UITextField.delegate is unowned, so the delegates were being deallocated after viewDidLoad returned.
Assign the delegate to a property instead
first add customTextFieldDelegate as a property
var customTextFieldDelegate = TextField()
and in ViewDidload
self.textField.delegate = self.customTextFieldDelegate