How to change Datasource from custom cell class - ios

this should be straight forward but i m finding hard time in understanding.
i have a class
class Field:NSObject {
var name:String?
var answer:String?
.................
}
Now i also have a viewcontroller in which there is a tableview. I am using Field array as datasource for this tableview. i am passing relavent object from Field array to my custom cell class (see below) which have a UItextfield .
cell.field = self.fields[indexPath.row]
What i want is that when text inside UItextfield is changed this should update our main Field array. Also custom cell class should have fresh copy of Field instance. I hope i have made it clear, please ask if you didn't understand anything. Much obliged.
here is cell class code
class SpeechCell: UITableViewCell, UITextFieldDelegate {
public var field:Field?{
didSet{
self.lblField.text = self.field?.name
self.txtAnswer.placeholder = self.field?.name
}
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
self.field?.answer = textField.text!
self.commandDelegate?.didUpdateField(field: self.field!, textField: textField)
}
override func prepareForReuse() {
self.txtAnswer.text = self.field?.answer
}
here is code for tableview
func didUpdateField(field: Field, textField: UITextField) {
self.fields[textField.tag].answer = textField.text!
}

You should use custom delegate method to notify the view controller about the changes in the textfield. In the custom delegate method you should update the fields array. You should change the field in the cell too before calling the custom delegate method.
You should call this delegate method in the textFieldDidChangeText.

Related

Swift 4 - identifying only one text field as delegate for editingDidEnd etc

I've kept this question free of specific context because I'm sure it will be helpful for others :
I have 2 IB outlet text fields :
#IBOutlet weak var textField1: UITextField!
#IBOutlet weak var textField2: UITextField!
I want to be able to disable buttons on my page until both have been filled out properly so I'm using the following delegates in ViewDidLoad() (I have added TextFieldDelegate to my VC.)
override func viewDidLoad() {
textField1.delegate = self
textField2.delegate = self
}
I then have some functions I will use to do the form validation and to take specific actions but to keep it simple let's say it simply prints to the console.
What I want to do is only check for validation in textField1 and not in textField2. I.e. the desired output is that this prints for when user begins editing textField1 but if user edits textField2 nothing is printed.
I'm currently using :
func textFieldDidBeginEditing(_ textField1: UITextField) {
print("TextField did begin editing method called")
}
But that is printing when either textField is edited.
I thought I've specified _ textField1 so not sure why both are triggering it?
There are some answers solving similar problems for Swift 3 and earlier. In particular one answer referenced this link http://sourcefreeze.com/uitextfield-and-uitextfield-delegate-in-swift/ which i've found useful but am stuck on this error.
UITextField delegate method allows you to identify that which textField is begin editing so you just need to check whether it's your 1st textField or not like this.
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.textField1 {
print("TextField did begin editing method called")
// Do your Validate for first text field
} else {
//Do Nothing
}
}
That's why the delegate function textFieldDidBeginEditing has a textField parameter. Renaming it does nothing.
If you want to discern your text fields, compare the textField parameter to your IBOutlets, like so:
func textFieldDidBeginEditing(_ text Field: UITextField) {
if textField == textField1 {
// Validate first text field
} else if textField == textField2 {
// Validate second text field
}
}
simply set tag for both textFields as 0 and 1 then in your delegate method just check the tag of your textfield.
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.tag == 0 {
print("TextField did begin editing method called for text field 1")
}
}
Let's first analyse your piece of code and see what is wrong:
func textFieldDidBeginEditing(_ textField1: UITextField) {
print("TextField did begin editing method called")
}
You renamed textField to textField1. However, that does not name the UI element that should be responsible for this action. Instead, that is a function parameter which lets you access information about the object that called the action from within your function, regardless of the outer scope. Instead, you should use the === operator, which checks whether two references point to the same object instance. So your code should become:
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField === self.textField1 {
print("TextField 1 did begin editing method called")
}
}
You have named the parameter that receives the UITextField reference that is passed to the delegate method textField1; the name of this parameter is nothing to do with the name of your property. You could have called it textField and your code would still compile, despite not having a property called textField.
You need to compare the instance of UITextField that was passed to the delegate method:
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField === self.textField1 {
print("TextField did begin editing method called for text field 1")
}
}
you can do both ways either you can put if condition on textfield like paul said or if you don't want textfield2 to work with any of the textfield delegate you can remove the line
textField2.delegate = self

Firebase update

I am relatively new on firebase. I want to update existing data on my firebase database. I have UITableView and UITableViewCells on my Xcode project, when user touch (tap gesture), for example a outlet on cell view, I want to update Firebase Database but this could be any cell on tableview. How to find this cell which user touched on screen, on firebase database and update its messageVoteScore value. There are assigned keys but my cells do not know those keys,
I could not figure it out how to match them.(Database/Messages/{"sender": "email"},{"messageBody":"text.."},{"messageVoteScore":"100"}
#objc func voteUpTapped(sender: UITapGestureRecognizer) {
// Update score //
self.messageVoteScore.text = String(Int(self.messageVoteScore.text!)! + 1 )
//observeSingleEventOfType listens for a tap by the current user.
Database.database().reference().child("Messages").observe(.value){
(snapshot) in
if let snapshotValue = snapshot.value as? [String : String] {
print(snapshotValue)
}
}
}
Are you passing class objects to your cell? If so you could use a protocol/delegate for this. A UITableView Cell is a View and shouldn't function as a controller. your UITableViewController should be updating firebase.
you should start by adding something like this to the top of your Cell class:
protocol MyTableViewCellDelegate: class {
func thingDidDo(object: Object, text: String...) //object is the class object you are changing.
then add the delegate to your properties:
weak var delegate: MyTableViewCellDelegate?
then add the IBAction:
#IBAction func thingDidDo(_ sender: Any) {
delegate?.thingDidDo(object: object, text: textView.text...)
}
Now back to your viewController Class:
In your "cellForRowAt" write in the delegate:
cell.delegate = self
then add an extension
extension MyTableViewController: MyTableViewCellDelegate {
func thingDidDo(object: Object, text: String) {
Do whatever you want with object, text...
}
Not sure what kind of function you are trying to perform so I made this as generic as I could

How to change value of dictionary in View Controller from UiTableViewCell

So I have TableViewCell's that are being populated from 2 Dicitionaries in my view controller.
var categories : [Int : [String : Any]]!
var assignments : [Int: [String : Any]]!
I have a UiTextField in my cell that the user is supposed to be able to edit. I then want to be able to change the values of certain keys in that dictionary-based off what the user changes and re-display the table with those changes. My main problem is that I don't know how I will be able to access theese variables from within my cell. I have a method in my view controller that takes the row that the text field is in, along with the value of the textField, and updates the dictionaries. What I need is to be able to instantiate the view controller that the cell is in but I need the original instance that already has values loaded into the categories and assignments Dictionaries. If you have any other ideas on how I could accomplish this please post.
You can use delegate for sharing cell-data to your VC:
protocol YourCellDelegate() {
func pickedString(str: String)
}
class YourCell: UITableViewCell {
var delegate: YourCellDelegate! = nil
#IBOutlet weak var textField: UITextField!
.....//some set-method, where you can handle a text
func textHandle() {
guard let del = delegate else { return }
del.pickedString(textField.text)
}
.....
}
And usage in your VC: When you create cell, set its delegate self:
...
cell.delegate = self
...
and sure you VC supported your Delegate Protocol:
class YourVC: UIViewController, YourCellDelegate {
}
And now, you MUST implement protocol method:
class YourVC: UIViewController, YourCellDelegate {
....
func pickedString(str: String) {
}
....
}
All times, when you use textHandle() in your cell, pickedString(str: String) activates in yourVC with string from textField.
Enjoy!

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.

Resources