Protocol doesn't get read in swift xcode project - swift

i have a UITableview that has unique cells,
each cell has it's own class and they have actions that i want to connect to my main UITableviewcontroller
I attach a protocol and open it in the tableviewcontroller
but it doesn't get read
how could I initialise it or what am I doing wrong ?
here is my cell class :
import UIKit
class AddFaxHeadlineTableViewCell: UITableViewCell {
var delegate: AddFaxHeadlineProtocol?
#IBOutlet weak var addButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func onAddFaxNumberPressed(_ sender: Any) {
delegate?.faxButtonPressed()
}
}
protocol AddFaxHeadlineProtocol{
func faxButtonPressed()
}
and in my tableviewcontroller I extend the protocol:
class SummaryMainTableViewController: UITableViewController, AddFaxHeadlineProtocol, AddEmailHeadlineProtocol {
but the function itself never gets read:
func faxButtonPressed() {
var indexToInsert = 0
for forIndex in 0..<sectionsData.count {
// render the tick mark each minute (60 times)
if (sectionsData[forIndex] == "addFaxHeadline") {
indexToInsert = forIndex + 1
}
}
sectionsData.insert("addNewFax", at: indexToInsert)
mainTableView.reloadData()
}

You need to call:
cell.delegate = self
In your cellForRowAtIndex method
This is the common mistake done in protocols and delegates to forget to call delegate.
Here are few examples you can check all have missing is calling delegate:-
Swift delegate beetween two VC without segue
Delegate seems to not be working, according to the console
How to present another view controller after dismiss from navigation controller in swift?

Another way to go inside the vc without protocols
let cell = ///
cell.addButton.tag = indexPath.row
cell.addButton.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}
#objc func buttonTapped(_ sender:UIButton) {
print(sender.tag)
}

Check that you are doing this:
cell.delegate = self (It's required)
Then improve your line of code like below. Because you will not set delegate then by calling this delegate method directly will get crashed.
#IBAction func onAddFaxNumberPressed(_ sender: Any) {
if let delegateObject = delegate {
delegateObject.faxButtonPressed()
}
}
Second,
In this line, delegateObject.faxButtonPressed(), you will need to send some parameter to identify that will cell is clicked. So you can pass here button tag or you can pass cell also.

Related

UIViewControllers sharing 'generic' IBAction

I have an app with 6 UIViewControllers.
ANY viewcontroller features a function like this one:
#IBAction func onHelp(_ sender: UIBarButtonItem) {
DispatchQueue.main.async(execute: { () -> Void in
let helpVC = self.storyboard?.instantiateViewController(withIdentifier: "Help") as! HelpViewController
helpVC.starter = "MapHelp"
helpVC.helpSubtitle = "Map"
self.present(helpVC, animated: true, completion: nil)
})
}
Any IBAction in any viewcontroller presents the same HelpViewController but passing different parameters (starter and helpSubtitle).
Since I don't like to repeat code, first of all I thought this function should be converted to something more generic.
But: is there any way to create a generic IBAction, working for every viewcontroller?
Create a BaseViewController and add the generic method there.
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func genericMethod(starter: String, helpSubtitle: String){
let helpVC = self.storyboard?.instantiateViewController(withIdentifier: "Help") as! HelpViewController
helpVC.starter = starter
helpVC.helpSubtitle = helpSubtitle
self.present(helpVC, animated: true, completion: nil)
}
#IBAction func onHelp(_ sender: UIButton?) {
//You can use this method as generic IBaction if you want. It can be connected to buttons of all child View Controllers. But doing so will limit your param sending ability. On the plus side though, you won't have to define an IBAction everywhere and you can simply connect your child VC's button to Parent Class' IBAction.
}
}
Now inherit your ViewControllers from this class like:
import UIKit
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func btnTapped(_ sender: Any) {
genericMethod(starter: "View Controller", helpSubtitle: "I was triggered from VC1")
}
}
and
import UIKit
class SecondViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnTapped(_ sender: Any) {
genericMethod(starter: "View Controller 2", helpSubtitle: "I was triggered from VC2")
}
}
That's it. Both your ViewControllers can call the parent method. If you still want to use the generic IBAction, you can do that too but I'd not recommend that course given that you want to pass params that can vary. If you wanted to do it though, it would look like this:
Bear in mind, the ViewController here has been inherited from the base ViewController which is why it can access the IBActions defined in the parent class. All you have to do is drag and connect.

#IBDesignable with protocol

I have a UIview xib within a view controller, UIview class have two buttons with protocol function, but the protocol function never called when I press button, storyboard image like below
protocol method like below
import UIKit
#objc protocol TopViewDelegate: NSObjectProtocol {
#objc optional func pressRefreshButton()
#objc optional func pressMenuButton()
}
UIView class
#IBDesignable class OnJob_Top: UIView,TopViewDelegate {
weak var delegate : TopViewDelegate? = nil
#IBAction func refreshButtonTouchUpInside(_ sender: UIButton) {
delegate?.pressRefreshButton!()
}
#IBAction func menuButtonTouchUpInside(_ sender: UIButton) {
delegate?.pressMenuButton!()
print("come come")
}
view controller class
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let topView = OnJob_Top()
topView.delegate = self
}
}
extension HomeViewController:TopViewDelegate {
func pressMenuButton() {
print("come") // never come here
}
func pressRefreshButton() {
print("come") // never come here
}
}
Consider this code:
let topView = OnJob_Top()
topView.delegate = self
In the first line, you create a completely new OnJob_Top view.
In the second line, you make it the delegate.
In the third line... but there is no third line. The view vanishes in a silent puff of smoke. It is useless.
Meanwhile, the view in the storyboard never gets a delegate. So its delegate methods are never called.

Delegates is not working?

here is my protocol definition.
protocol ActivityIndicatorDelegate: class {
func showIndicator()
func hideIndicator()
func barcodeError()
func categoryError()
func descError()
func reasonError()
func costError()
}
Then in my Custom cell class I create weak reference and I call delegate function
class ProductTableViewCell: UITableViewCell {
weak var indicatorDelegate: ActivityIndicatorDelegate?
#IBAction func stockUpdate(_ sender: Any) {
indicatorDelegate?.categoryError()
}
}
Then in my UITableViewController class
class ProductTableViewController:
UITableViewController,ActivityIndicatorDelegate{
override func viewDidLoad() {
super.viewDidLoad()
let cellDelegate = ProductTableViewCell()
cellDelegate.indicatorDelegate = self
}
func categoryError() {
//self.showAlert(alertTitle: "Error!", alertMessage: "Category Should not be empty")
print("Error")
}
}
I have written all these in a single file. What I'm doing wrong here? Can some one help me to solve this. Thanks in advance.
You should not set the delegate in viewDidLoad. This will only set the delegate of the cell that you just created, instead of all the cells in the table view.
You should do this in celForRowAtIndexPath:
let cell = tableView.dequeue...
// configure the cell...
cell.indicatorDelegate = self

How to set IBAction for a UIButton in a UITableViewCell for a different ViewController?

I have subclassed UITableViewCell. Here I have made an outlet for a button. When that button have been clicked on, it should execute a function in a different ViewController. I have tried this:
override func awakeFromNib() {
super.awakeFromNib()
reloadTableView.addTarget(self, action: #selector(multiplayerChannelView.tappedOnReloadTableView), for: .touchUpInside)
}
However, this crashes with this error:
[test.CreateChannelCell tappedOnReloadTableView]: unrecognized selector sent to instance 0x7fc1198b5200
The function exists, with no typo. Why does this not work? This question looks the same only it is not written in swift 3.0 How to set Action for UIButton in UITableViewCell
multiplayerChannelView is the viewcontroller which holds the UITableView. I got a seperated .swift file with the UITableViewCell subclassed.
add this in the cellForRowAtIndexPath
cell.your_button.addTarget(self, action: #selector(self.tappedButton(sender:)), for: .touchUpInside);
And anywhere in the same UIVeiwController define the function as below
func tappedButton(sender : UIButton){
// Your code here
}
You can do it by creating delegate in your tableViewCell class.
protocol CustomTableViewCellDelegate {
func buttonPressed ()
}
then initialize your delegate like below in your tableViewCell
var delegate: CustomTableViewCellDelegate?
and for button action put below code in your tableViewCell class
#IBAction func cellButtonPressed (sender : UIButton) {
if (self.delegate != nil) {
self.delegate?.buttonPressed()
}
On button click check wether delegate is not nil , please set cell.delegate = self in cellForRowAtIndex method
In the last just add code for button action in your classes where you have used customTableViewCell class
extension ViewController : CustomTableViewCellDelegate {
func buttonPressed () {
// Perfom your code on button action
}
}
your CustomTableViewCellDelegate looks like below:
import UIKit
protocol CustomTableViewCellDelegate {
func buttonPressed ()
}
class CustomTableViewCell: UITableViewCell {
var delegate: CustomTableViewCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func cellButtonPressed (sender : UIButton) {
if (self.delegate != nil) {
self.delegate?.buttonPressed()
}
}
Hope it work for you!
write below code in file VC2
import UIKit
class tblCell : UITableViewCell
{
#IBOutlet weak var btnAction: UIButton!
#IBAction func btnAction(_ sender: AnyObject)
{
print(sender.tag) // you can identify your cell from sender.tag value
// notification is fire here and call notification from VC1
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "buttonAction"), object: nil)
}
}
class VC2: UIViewController,UITableViewDelegate,UITableViewDataSource
{
#IBOutlet weak var tblview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tblview.delegate = self
tblview.dataSource = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table veiw
func tableView(_ tblBlog: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tblBlog: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell : tblCell = tblview.dequeueReusableCell(withIdentifier: "Cell") as! tblCell
cell.btnAction.tag = indexPath.row
return cell
//
}
}
write below code in file VC1
class VC1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool)
{
// this notification is call when it fire from VC2
NotificationCenter.default.addObserver(self, selector: #selector(ButtonClick), name: NSNotification.Name(rawValue: "buttonAction"), object: nil)
}
func ButtonClick()
{
// code when button is clicked you wanto perfrom
}
}

delegate passing nil in swift

I am trying to get to grips with delegates but the delegate I have set up seems to be nil and I am not sure why. I have a HomeViewController where the game is started from, then a UITableViewController where the player selects a row from a table. The row index is then used to pull data to be used in the game. The UITableViewController segues back to the HomeViewController where the game then starts. I thought I had put the correct protocol and delegate code in place but the delegate seems to be nil.
Any help much appreciated!
import UIKit
import Foundation
class HomeViewController: UIViewController, WordListsTableViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// sets up the game here
}
func wordListSelected(selectedWordList: Int) {
// passes the index path of the table to the AppWordList class to create the wordList for the game.
controller.wordList = AppWordList(wordListNumber: selectedWordList)
}
and in the TableViewController
import UIKit
protocol WordListsTableViewControllerDelegate {
func wordListSelected(selectedWordList: Int)
}
class WordListsTableViewController: UITableViewController {
var delegate: WordListsTableViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
reloadData()
tableView.reloadData()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var selectedWordList = Int()
if (indexPath.section) == 2 {
selectedWordList = (indexPath.row) // Console shows the row is being selected ok.
delegate?.wordListSelected(selectedWordList) // IS NIL ???
// exit segue back to the HomeVC
performSegueWithIdentifier("startGameSegue", sender: nil)
}
}
You need to inform the HomeViewController class that has to be the delegate receiver for the class WordListsTableViewController, like this:
import UIKit
import Foundation
class HomeViewController: UIViewController, WordListsTableViewControllerDelegate
{
var wordListTableViewController = WordListTableViewController() // You forgot this
override func viewDidLoad() {
super.viewDidLoad()
wordListTableViewController.delegate = self // And this
// sets up the game here
}
func wordListSelected(selectedWordList: Int) {
// passes the index path of the table to the AppWordList class to create the wordList for the game.
controller.wordList = AppWordList(wordListNumber: selectedWordList)
}
You're missing a very important point about the Delegate Pattern, you need to keep a reference to the class that delegate its function and set it delegate in the class that handle the function. So let suppose you present the WordListsTableViewController by a segue from the HomeViewController like in the following example:
class HomeViewController: UIViewController, WordListsTableViewControllerDelegate {
// the reference to the class that delegate
var wordListTableViewController: WordListsTableViewController!
override func viewDidLoad() {
super.viewDidLoad()
// sets up the game here
}
func wordListSelected(selectedWordList: Int) {
// passes the index path of the table to the AppWordList class to create the wordList for the game.
controller.wordList = AppWordList(wordListNumber: selectedWordList)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// get the reference to the WordListsTableViewController
self.wordListTableViewController = segue.destinationViewController as! WordListsTableViewController
// set as it delegate
self.wordListTableViewController.delegate = self
}
}
And then you should be notified from the WordListsTableViewController, in the above example I assume the use of segues, but if you present the WordListsTableViewController you can use the same principle of keep a reference to the delegate class, like I show in the above example.
I do not apply any concept in code regarding the retain-cycles that can be happen in the use of delegates, but you can read more in my answer of this question about how to implement delegates correctly:
"fatal error: unexpectedly found nil while unwrapping an Optional value" while calling a protocol method
I strongly recommend you read more about the Delegate Pattern in this post:
How Delegation Works – A Swift Developer’s Guide
I hope this help you.
In your HomeViewController you have to set delegate to self:
override func viewDidLoad() {
super.viewDidLoad()
// get reference to your word lists table view controller
// if controller is your table view that should work
// controller.delegate = self
let wordLists = WordListsTableViewController....
// set up delegate
wordLists.delegate = self.
}