UITextView delegate methods not called - ios

Swift beginner here. I am having quite a tough time getting my UITextView delegate methods to call. I have looked through many other questions, to no avail.
I have a UITextView set up in a ViewConroller. It looks like this:
There is a UIImageView directly above the UITextView, and everything is wrapped in a navigation controller, should be no big deal.
I made sure to connect the View from the storyboard to my ViewController:
From the storyboard I CTRL+Drag the UITextView right below the class declaration. This produces the line:
#IBOutlet weak var Description: UITextView!
Class declaration:
class ImageTextViewController: UIViewController, UITextViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
(I need the other delegates for my NavigationController and UIImageView)
I then declare the delegate as so:
override func viewDidLoad() {
super.viewDidLoad()
self.Description.delegate = self
Now the following delegate method should be called when I finish editing the UITextView. But for some reason it is not firing!
private func textViewDidEndEditing(_ textView: UITextView) -> Bool{
print("we are done editing")
spotDescription.resignFirstResponder()
return true
}
How do I get that to fire properly for my UITextView?

textViewDidEndEditing method do not have return value. You have to write this
func textViewDidEndEditing(_ textView: UITextView) {
print("we are done editing")
}

Jack is right.
In the image titled Storyboard Connection your viewController seems to be titled TextImageViewController, while in the class declaration example is titled ImageTextViewController.
The ViewController in the Storyboard and the ViewController should both be the same.
Андрей is also right.
TextViewDidEndEditing method does not have a return value.
lindanordstrom also makes a good point:
TextViewDidEndEditing shouldn't be private.

Related

UIView to UIViewController communication via protocol not working

maybe I am missing something really fundamental here, but after staring at the code for an hour or so, my brain is going trough cycles and I would appreciate a fresh glance at this problem.
I have the following UIView:
import UIKit
protocol DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool)
}
class locationXIBController: UIView {
#IBOutlet weak var loationLabel: UILabel!
#IBOutlet weak var vsedniOteviraciDobaLabel: UILabel!
#IBOutlet weak var prijmajiKartyLabel: UILabel!
#IBOutlet weak var detailViewButtonOutlet: UIButton!
#IBOutlet weak var backgroundViewButton: UIButton!
let openedBool = true
var detailViewWillShowUpDelegate: DetailViewWillShowUpDelegate?
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func vecerkaDetailButtonPressed(_ sender: UIButton) {
detailViewWillShowUpDelegate?.sendDetailOpened(openedBool)
print("pressed")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = detailViewButtonOutlet.hitTest(convert(point, to: detailViewButtonOutlet), with: event) {
return result
}
return backgroundViewButton.hitTest(convert(point, to: backgroundViewButton), with: event)
}
}
Now the problem is, that when I call/press the vecerkaDetailButtonPressed function I get "pressed" output in the console but the protocol for some reason doesn't go trough.
The other side looks like this (stripped for simplicity):
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
let locationXIB = locationXIBController()
let isVecerkaDetailOpened = false
override func viewDidLoad() {
locationXIB.detailViewWillShowUpDelegate = self
}
extension MapViewController: DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool) {
isVecerkaDetailOpened = openedBool
print("success")
}
}
I know the protocol value at the moment of execution is nil. As I said, any help is appreciated, thanks!
First, a couple of naming convention issues:
The name locationXIBController is a bad choice for a UIView object. It is a view object, not a controller object.
Second, class names in Swift should start with an upper-case letter. So LocationXIBView would be a much better name for that view class.
Next, your code
let locationXIB = locationXIBController()
...is wrong. That creates a brand-new instance of your locationXIBController class that you never install in your view hierarchy. You should make that line an IBOutlet:
#IBOutlet weak var locationXIB: locationXIBController!
And then you should control-drag from the locationXIBController in your StoryBoard onto the outlet in your view controller. That will cause Interface Builder to connect the outlet.
Now when you run your program the variable locationXIB will be connected to the locationXIBController view from your storyboard/XIB when it's loaded.
In addition to the answer of #Duncan C, you might check whether you need super.viewDidLoad() at the top of the viewDidLoad() method in the MapViewController class? Not doing that can lead to quirky things in your app.
I asked:
So does detailViewWillShowUpDelegate actually point at anything, or is it nil?
And you replied:
I just tried debugging and it is actually nil
So that's the problem... you need to set detailViewWillShowUpDelegate to point to a valid delegate object. This is often done in the .xib file or storyboard, and sometimes people forget to make that connection, so check there if it makes sense. Else you'll just need to get a reference to the delegate at some point before the code in question can run and set it up.
Answer to the credit of #Paulw11
I finally managed to get it working by communicating like so:
step 1) 1:1 communication via protocol between MKAnnotation and MKAnnotationView
step 2) 1:1 communication via protocol between MKAnnotationView and MapViewController passing the same data
Finally works like a charm, thanks!

Delegate of TextField

I faced something and I cannot understand it.
In the tutorial which I watched, instructor press (cntrl + click) delegate option and drag it into ViewController class. Then, textfield options become like the photo. I searched for a lot and I found an explanation ,but I couldn't understand it exactly.
Explanation : To be able to use UITextFieldDelegate methods, ViewController class must adopt this protocol. However, before using any method, we have to choose this delegate option.
I cannot understand this option. What is the benefit of it ?
so in order to use it and understand it try out these steps:
1 adopt UITextFieldDelegate protocol to your class like so
class MyViewController: UIViewController, UITextViewDelegate {
2 implement callback methods for the UITextViewDelegate. For example:
//MARK: UITextViewDelegate
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print(textField.text ?? "")
return true
}
These methods are made for managing and validating text from the TextView.
Maybe you should read about protocols.
Or if you don't understand why you are setting your delegate form the storyboard directly it's because this way you will not have an extra property declared in your class. I will be done seamlessly and you will not have this in your class:
#IBOutlet weak var myTextField: UITextField!
//...
override func viewDidLoad() {
super.viewDidLoad()
myTextField.delegate = self
}
First add delegate to your view controller as
UITextFieldDelegate
Only then the delegate functions can be used in the view controller.
And for the functions to work as expected,
You have to set the textfield's delegate as self. Eg
textfield.delegate = self
Setting the delegate of textfield as self is the programatic way of doing the same thing the instructor did (as you said).

How to get the UITextField's delegates events while subclassing it, without losing the delegation if the user of it set itself as the delegate?

For simplicity, let's say I want to create a custom UITextField and I want to add a simple behaviour to it; Which is, if the textfield becomes the first responder, the background color would be changed to green.
To do so, in my custom class I have to set the class as the delegate to receive the event of becoming first responder. But the thing is that if the user of this custom textfield set itself as the delegate the events are not sent to the custom textfield(Since only one object can be the delegate of another object)
I can manually forward all the events, but I'm looking for a cleaner and more scalable solution.
Here's a sketch of the situation:
class MyTextField: UITextField {
override func awakeFromNib() {
super.awakeFromNib()
delegate = self
}
}
extension MyTextField: UITextFieldDelegate {
func textFieldDidBeginEditing(textField: UITextField) {
backgroundColor = UIColor.greenColor()
}
}
but if the user of MyTextField do this:
class ViewController: UIViewController {
#IBOutlet weak var myTextField: MyTextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.delegate = self
}
}
the behaviour won't work; because the delegation relationship to MyTextField is gone.
NOTE: I'm not only interested in becoming first responder problem, rather it's about using any methods of the delegate, with capability of the user of my custom UITextField setting itself as the delegate, at the same time.
Thanks, in advance.
As you say, most delegation is restricted to a single object as the delegate.
Since a text field is a responder, you should be able to override func becomeFirstResponder() -> Bool to change the color, while letting the user of the object handle the delegation as it expects.
UIResponder docs: "Subclasses can override this method to update state or perform some action such as highlighting the selection."

How to clear multiple textviews upon Editing?

I am trying to clear multiple textviews on editing. I know how do so with one textView (IE):
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var myTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
myTextView.delegate = self
}
func textViewDidBeginEditing(textView: UITextView) {
myTextView.text = ""
}
How would I use the same concept for multiple textviews?
func textViewDidBeginEditing(textView: UITextView) {
myTextView.text = ""
}
The above delegate method will get called when a text view begins editing. And this method holds the reference to the textView that called in the textView object. You can use that reference to clear the text instead of using a separate reference/outlet to the textView.
So the method would be:
func textViewDidBeginEditing(textView: UITextView) {
textView.text = ""
}
From the Documentation:
Description:
Tells the delegate that editing began in the specified text field.
This method notifies the delegate that the specified text field just
became the first responder. Use this method to update state
information or perform other tasks. For example, you might use this
method to show overlay views that are visible only while editing.
Implementation of this method by the delegate is optional.
Parameters:
textView
The text view in which an editing session began.

Unable to connect IBAction to UITextField in XIB (Swift)

On one of my ViewControllers, I declared:
#IBOutlet var priceTextField : UITextField!
Then I tried to add the following based on the same UITextField:
#IBAction func priceTextFieldChanged(sender: UITextField) { updateOutput() }
When I looked at the XIB's File Owner and at the Received Actions, I was not able to connect the UITextField to this function for some reason. I even tried to drag that UITextField to the ViewController, but was not able to see the "Action" option. I checked the class. It's linked to the ViewController. Not sure what to do here.
Any advice?
A UITextField does not provide an action (sendAction:), that is why you can't hook-it-up.
What you need is to create a delegate, for textFieldDidBeginEditing or another delegate method of UITextField and set the UITextField delegate to your class instance.
#Zaph, something like this:
class ViewController: UIViewController,UITextFieldDelegate {
#IBOutlet var priceTextField : UITextField!
func textFieldDidEndEditing(textField: UITextField) {
if let value = sender.textIntegerValue() {
pricetepper.ifInRangeSetValue(value)
}
updateOutput()
}
}
Not sure if I pick the right method. Thanks.

Resources