DeviceTableViewCell
I create a protocol on my custom DeviceTableViewCell
protocol DeviceTableViewCellDelegate : NSObjectProtocol {
func printMe(_ text : String)
}
I also declared my delegate in
weak var delegate: DeviceTableViewCellDelegate?
DevicesViewController
I had this
extension DevicesViewController: DeviceTableViewCellDelegate {
func printMe(_ text : String) {
let text = "Protocol & Delegate"
print("........")
print(text)
print("........")
}
}
I don't know how to trigger my print() statement.
How would one trigger it ?
Do I need to call my printMe() somewhere ?
Did I missing something here ?
First, do this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellIdentifier", for: indexPath) as! DeviceTableViewCell
cell.delegate = self
}
Then, you must call printMe from your cell to handle your action
You just declare how the delegate function working, you haven't called it yet.
Based on the context, you can decide when to call delegate.printMe(_ text : "Foo").
I suggest a simple example you could refer: passing data back to previous view controller from current view controller using delegate.
Let's take example to understand the concept. So as you already create delegate variable for your protocol.
weak var delegate: DeviceTableViewCellDelegate?
Now to call protocol method you need to assign your delegate to some viewController or class. Let's assign in same view controller in viewDidLoad method.
override func viewDidLoad(){
delegate = self
}
Now let's say need to call protocol method when some button pressed. So what you need to do is call this method like this in button press method.
delegate?.printMe("Button Pressed")
Related
I have a textView embedded in a cell in a tableView and am trying to determine whether the text has been changed when the user presses done.
When a user taps on a note, it loads the note from the database, they can make edits, and press done. When they press done I need to invoke didChange to make sure that they changed something to avoid writing the same information back to the database. I've set my cell as a UITextFieldDelegate and tried setting didChange.
import UIKit
class NoteEditTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var editorView: UITextView!
var origionalContent = ""
var didContentChange = false
func didChange<Value>(_ changeKind: NSKeyValueChange, valuesAt indexes: IndexSet, for keyPath: __owned KeyPath<NoteEditTableViewCell, Value>) {
debugPrint("Content Changed")
}
}
I can't get didChange to invoke though. No matter what I change in that field, it never prints the debug message.
You can use callback function here. You can also use callback function like this for button tap action in tableViewCell. That will respond in tableview.
1)Declare callback function in your tableViewCell file. Here this callback function pass String, you can use any type you want or you can leave it empty[()->()] if you just want to detect this function call in tableview.
var textViewTextChangeCallback:((String) -> Void)?
2) use UITextViewDelegate with textViewDidChange in your tableViewCell file. Put this extension into your tableViewCell file. call the callback function here & pass anything you want.
extension NoteEditTableViewCell: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
textViewTextChangeCallback?(textView.text)
}
}
3)Define your callback closure in cellForRawAt of UITableViewDataSource in your ViewController file.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourCellIdentifire") as! NoteEditTableViewCell
cell.textViewTextChangeCallback = { [unowned self] text in
print(text)
// if you want to save text on buttonClick when textViewDidChange call, create callback function for buttonClick & call its's closure here. i already show you how to define callback function follow as abov for any click you want from tableViewCell ;)
}
}
return cell
}
By using delegates, I'm trying to pass data from a view controller to a TableView class that will display the correct number of cells. First, I needed to pass the array to that class (using a String here instead for debugging)
Inside my ViewController:
protocol GroupBillVCDelegate{
func passFriendArray(string: String)
}
...
class GroupBillViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
var delegate:GroupBillVCDelegate? // for delegate passing message
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (tableView == self.FriendTableView){
let friendListCell = tableView.dequeueReusableCell(withIdentifier: "friendListCell") as! CategoryRow
// transfer friends array to CategoryRow
self.delegate?.passFriendArray(string: "Hello There")
return friendListCell
}
}
}
Inside the TableViewCell class:
class CategoryRow: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, GroupBillVCDelegate {
var s:String = String()
func passFriendArray(string: String) {
s = string
print(string)
}
...
// used for horizontal scrolling
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
passFriendArray(string: s) // message supposed to appear here
print ("TEST")
return 10
}
...
}
I have trouble setting up the delegate. When I run, the console does not display the message. Why is it not being passed?
Think of a delegate as an intercom on your desk that can be connected to your assistant's office.
This line:
var delegate:GroupBillVCDelegate? // for delegate passing message
Defines the intercom, but doesn't connect it to anything. If you press the button on the intercom, it doesn't connect to anything.
The code self.delegate?.passFriendArray(string: "Hello There")
uses "optional chaining" to send a message to the delegate if there is one, and drop the message if there is no delegate.
The thing you're missing is assigning something to the delegate:
self.delegate = someObjectThatConformsToGroupBillVCDelegateProtocol
A delegate is a one-to-one relationship.
All that being said, it doesn't really make sense to make a table view cell the delegate of a view controller. The table view has more than one cell. Which cell should be the view controller's delegate? Why that cell and not another one?
In my iOS Switch app I have a BEMCheckBox in each table cell. When dequeuing a cell I want to set a delegate that gets called.
My problem is that the checkbox works fine but the delegate is not never called. How to add a delegate to each checkbox?
I want to know which indexPath for checkbox. The plan is to pass model object to the delegate and update it accordingly.
Table cell
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
cell.doneCheckbox.delegate = DoneBEMCheckBoxDelegate()
return cell
Delegate is very simple
class DoneBEMCheckBoxDelegate: NSObject, BEMCheckBoxDelegate {
#objc func didTap(_ checkBox: BEMCheckBox) {
print("Checkbox tapped")
}
}
cell.doneCheckbox.delegate = DoneBEMCheckBoxDelegate() is creating a new DoneBEMCheckBoxDelegate object in a local variable and assigning that as the delegate. Since the delegate property is weak, it will be released as soon as the function exits because there is no strong reference remaining.
I would suggest that having a separate object class to be the delegate probably isn't what you want anyway.
I would set the cell to be the check box delegate and then declare another protocol so that the cell can have its own delegate, which would be your table view controller.
protocol MyCellDelegate {
func checkBox(for cell: MyCell, isOn: Bool)
}
class MyCell: UITableViewCell, DoneBEMCheckBoxDelegate {
var delegate: MyCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
self.doneCheckBox.delegate = self
}
#objc func didTap(_ checkBox: BEMCheckBox) {
print("Checkbox tapped")
self.delegate?.checkBox(for: self, isOn: checkBox.isOn)
}
}
class YourViewController: MyCellDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.delegate = self
return cell
}
func checkBox(for cell: MyCell, isOn: Bool) {
guard let indexPath = tableView.indexPath(for: cell) else {
return
}
// Now do whatever you need to with indexPath
}
}
This way you avoid creating additional objects and datastructures and you won't have a problem if cells are re-ordered as there is no dependency on index path.
I noticed that delegate is a weak reference in checkbox class, as it is supposed to be :) So my delegate was freed after method scope ended.
I fixed this by storing delegates in view controller during their usage.
var checkboxDelegates: [IndexPath:DoneBEMCheckBoxDelegate] = [:]
...
let checkboxDelegate = DoneBEMCheckBoxDelegate(realm: realm, set: set)
checkboxDelegates[indexPath] = checkboxDelegate
cell.doneCheckbox.delegate = checkboxDelegate
I have a Social Network Feed in form UItableView which has a cell. Now each cell has an image that animates when an even is triggered. Now, This event is in form of a string, will be triggered at every cell. the options for the event are defined in another class(of type NSObject).
My issue:
I constructed a protocol delegate method in table view, which will be called whenever the event is triggered for each cell. Then, I define this function in UITableViewCell Class, since my the image will be animating on that.
All is working well but I am unable to figure out how to assign the delegate of TableView class to cell class. What I mean is, how can I use UITableView.delegate = self in cellView class. I have tried using a static variable, but it doesn't work.
I have been playing around the protocols for a while now but really unable to figure out a solution to this.
I hope I am clear. If not, I will provide with an example in the comments. I am sorry, This is a confidential project and I cant reveal all details.
If I understand you correctly, you are trying to make each of your cells conform to a protocol that belongs to their UITableView? If this is the case then this cannot be done. The Delegation design pattern is a one to one relationship, i.e only one of your UITableViewCells would be able to conform to the UITableView's delegate.
Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object.
Quote from the Apple Docs
I would suggest that your UITableViewCell should call a block (Objective-C) or a closure (Swift) whenever your specified event is triggered to achieve what you are looking for. Set up this closure in your tableView:cellForRowAtIndexPath function.
EXAMPLE
TableViewController
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCellID", for: indexPath) as! MyTableViewCell
cell.eventClosure = {
//Do something once the event has been triggered.
}
return cell
}
TableViewCell
func eventTriggered()
{
//Call the closure now we have a triggered event.
eventClosure()
}
If I correctly understood your question, maybe this could help:
class ViewController: UIViewController, YourCustomTableDelegate {
#IBOutlet weak var tableView: YourCustomTableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.customTableDelegate = self
}
// table delegate method
func shouldAnimateCell(at indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.animate(...)
}
}
}
Try something like this:
Define your delegate protocol:
protocol CustomCellDelegate: class {
func animationStarted()
func animationFinished()
}
Define your CustomCell. Extremely important to define a weak delegate reference, so your classes won't retain each other.
class CustomCell: UITableViewCell {
// Don't unwrap in case the cell is enqueued!
weak var delegate: CustomCellDelegate?
/* Some initialization of the cell */
func performAnimation() {
delegate?.animationStarted()
UIView.animate(withDuration: 0.5, animations: {
/* Do some cool animation */
}) { finished in
self.delegate?.animationFinished()
}
}
}
Define your view controller. assign delegate inside tableView:cellForRowAt.
class ViewController: UITableViewDelegate, UITableViewDataSource {
/* Some view controller customization */
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: CustomCell.self)) as? CustomCell
cell.delegate = self
cell.performAnimation()
return cell
}
}
I want to use a delegate to make my cells (from a UICollectionView) communicate with my ViewController.
In my Cell.swift file, I am declaring the protocol needed like this (outside the Cell class):
protocol CellDelegate: class {
func someMethod(param1: String?, param2 param2: Bool)
}
In the same file I am declaring the delegate as follows:
class Cell: UICollectionViewCell {
weak var delegate: CellDelegate?
// ... some code ...
#IBAction func someAction(sender: AnyObject) {
delegate?.someMethod(param1, param2: true)
}
}
Now in my ViewController, I am implementing someMethod:
extension ViewController: CellDelegate {
func someMethod(param1: String?, param2 param2: Bool) {
// ... some code ...
}
}
Problem : I can not link the protocol with its implementation, cmd + click in the protocol leads nowhere. In my #IBAction, someMethod is not crashing, but it does nothing.
I saw this topic about the subject, but I do not understand where to implement the Step 6.
Can you help me ?
Thank you for your time.
You are missing the final step: populating the delegate property of the Cell class. I usually do that in cellForRowAtIndexPath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.table.dequeueReusableCellWithIdentifier("myCellId") as! Cell
cell.delegate = self
...
return cell
}
Note that there is no magic or automated behavior when using delegates:
a delegate is a protocol (your CellDelegate protocol)
you implement the protocol in the class you want to respond to the delegate invocations (you did in your ViewController class)
you create a delegate property in the class where the delegate methods are invoked (you did in your Cell class)
you initialize the delegate property with the actual instance of the class
You just missed the last step, leaving that property uninitialized, so any invocation using optional chaining evaluates to nil (like you did in the someAction method), and nothing happens.