Swift - Connect Delegate to Custom Xib Cell

How to use custom delegate for xib in swift?

You can use delegates or closures to get call back from CustomKeyboard to ViewController. Example below uses delegate method and function rowTapped will be called when user taps on view.

import Foundation
import UIKit

protocol CustomKeyBoardProtocol:AnyObject{
fun rowTapped(title:String)
}
class CustomKeyboard:UIView {

weak var delegate:CustomKeyBoardProtocol?
@IBAction func rowButtonAction(_ sender: UIButton) {
print(sender.currentTitle)
delegate.rowTapped(title: sender.currentTitle)
}

}

import UIKit
class ViewController: UIViewController, CustomKeyBoardProtocol{
@IBOutlet weak var textField1: UITextField!
@IBOutlet weak var textField2: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let customView = UINib(nibName: "CustomKeyboard", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! CustomKeyboard
customView.delegate = self// he
textField1.inputAccessoryView = customView//customView
textField1.autocorrectionType = .no
textField2.autocorrectionType = .no
}

func rowTapped(title:String){//it will be called on tap
}
}

Connecting UITextField delegate to File's Owner of Custom Table view cell not working Swift 3

You have never set the owner of the nib to your MyViewController.

You must be calling registerNib:forCellReuseIdentifier: in your MyViewController class, where you are providing the UINib object.

You must have created this UINib object, by calling init(nibName:bundle:). There, you have not provided the owner of this nib as MyViewController.

About FileOwner, this is from Apple's documentation:

File’s Owner object is a placeholder object that is not created when
the nib file is loaded. Instead, you create this object in your code
and pass it to the nib-loading code.

In case of UIViewController nib/storyboard, FileOwner is already set by XCode template so you can make direct IBOutlet connections.

In case of your custom tableviewCell, the fileowner needs to be provided through code.

This can be done using [instantiate(withOwner:options:)][2] method on nib object (I have shamelessly copied the code from Fransesco's answer above).

let nib = UINib(nibName: "MyCustomTableViewCell", bundle: nil) 
nib.instantiate(withOwner: self, options: nil)
myTableView.register(nib, forCellReuseIdentifier: "mycustomcellreuseidentifier")


Earlier, I thought the fileowner can be set using UINib method of instantiate(withOwner:) method, but although this method is for setting the nib owner, it does not work in case of tableview.register(nib:) method.

This is perhaps, because when tableview tries to dequeue the cell for the first time and it does not find one, it creates the cell from the registered nib but set its owner as nil. I have not found the official doc supporting this, but this is what is perhaps happening which is resulting in issues in the sample project.

I have removed the registerNib code from the sample, and created the cell in cellForRowAtIndexPath:, and this worked.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "mycellreuseidentifier") as? MyTableViewCell
if cell == nil {
let array = Bundle.main.loadNibNamed("MyTableViewCell", owner: self, options: nil)
cell = array!.first as! MyTableViewCell
}
return cell!
}

This alone, though is not enough. You have to change the File Owner inside nib as MyViewController. This is to be done in Identity inspector tab for the nib. Just set MyViewController as the FileOwner.

Although, this worked, but I guess if you need to write this much in cellForRowAtIndexPath:, its better you set delegate there itself as you have done in your commented code.

Protocol Delegate between XIB and View Controller

Your problem is that you set the delegate for a shared instance here

ErrorMessage.shared?.delegate = self / here shared?. is nil

but here

guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
self.view.addSubview(viewErrorMessage)

you create a separate instance and add it

You need

var viewErrorMessage:ErrorMessage! // add to the vc

viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as! ErrorMessage 
viewErrorMessage.delegate = self
self.view.addSubview(viewErrorMessage)

Also completely git rid of

static weak var shared: ErrorMessage?

How to add an action button to a custom XIB cell?

Check instance of IBAction from your cell may be it is being called. Remove this function and it will work

@IBAction func pressedTimeButton(_ sender: UIButton) {
}

Swift custom xib delegate

You need to set the delegate

let allViewsInXibArray = Bundle.main.loadNibNamed("MyModalVC2", owner: self, options: nil)
let dobView = allViewsInXibArray?.first as! MyModalVC2
dobView.delegate = self
self.view.addSubview(dobView)

//

also it's supposed that you only need the date , because self should be deallocated as you'll remove the picker view from it's parent after getting the date

protocol MyModalDelegate2 {
func myModularDidFinish(date: String)
}

class MyModalVC2: UIView {

var delegate: MyModalDelegate2?
@IBOutlet weak var datePicker: UIDatePicker!
@IBAction func datePicker(_ sender: Any) {
delegate?.myModularDidFinish(date: "\(datePicker.date)")
}

}

Passing data from one Custom TableView Cell to another using delegates and protocols

The proper way of updating a tableview/collectionviewcell is to reload the particular cell. And you should always maintain a data represent your table view cells since your cells are not persistent and re-usable. You can use that data to populate the properties of your cell when it is created.

In your case, date is the data needs to be persisted. In this scenario, you could simply have that as a field in your controller.

import UIKit

class DateLabelCell: UITableViewCell {

@IBOutlet var dateLabel: UILabel!

override func awakeFromNib() {
super.awakeFromNib()
}

func setDate(_ text: String) {
dateLabel.text = text
}

override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
class TableViewController: UITableViewController {
var date: String = "Date"

.......

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "DateLabelCell", for: indexPath) as! DateLabelCell
cell.setDate(self.date)
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "DatePickerCell", for: indexPath) as! DatePickerCell
cell.dateDelegate = self
return cell
}
}

}
extension TableViewController: DatePickerDelegate {
func setDate(_ text: String) {
self.date = text
tableView.reloadRows(at: [IndexPath(item: 0, section: 0)], with: .automatic)
}
}


Related Topics



Leave a reply



Submit