Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a protocol method inside of a subclass

Consider the following code inside of a Module that is included via Cocoapods (I'm using Eureka, but the problem should not be related to that):

open class FormViewController : UIViewController {  
}

extension FormViewController : UITableViewDelegate {    
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // some default implementation
  }

  /// some other UITableViewDelegate methods follow, but _NOT_ willDisplayCell
}

I am trying to subclass FormViewController and add an implementation for tableView(_:willDisplay:forRowAt:) (which is an optional method from UITableViewDelegate). This method has no implementation in the origin FormViewController:

import UIKit
import Eureka

class MyViewController: FormViewController {
    override func viewDidLoad() {
        let tableView = UITableView(frame: self.view.bounds, style: .grouped)
        tableView.delegate = self
        tableView.dataSource = self
        self.tableView = tableView
        self.view.addSubview(self.tableView!)

        super.viewDidLoad()

        form = Form()
        let section = Section()
        section.append(EmailRow(){
            $0.tag = "email"
            $0.title = "Email"
        })
        form += [section]
    }

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        print("NOT CALLED")
    }


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("will be called")
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print("GETS CALLED")
        return super.tableView(tableView, cellForRowAt: indexPath)
    }
}

/// somewhere else:
self.navigationController?.pushViewController(MyViewController(), animated: true)

With this setup, my overriden version of tableView(_:cellForRowAt:) will be called (meaning that tableView.delegate is set up properly). tableView(_:willDisplay:forRowAt:) will not be called. As soon as I add a blank implementation in Eureka, I'm able to override the method.

Question: Why is Swift not using the method without a default implementation in the superclass?

like image 796
Alex Avatar asked Oct 12 '16 13:10

Alex


People also ask

What is protocol and how do you implement it?

A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements.

Can struct implement protocol Swift?

In Swift, protocols contain multiple abstract members. Classes, structs and enums can conform to multiple protocols and the conformance relationship can be established retroactively.

Where you can use protocol as a type?

Protocol is used to specify particular class type property or instance property. It just specifies the type or instance property alone rather than specifying whether it is a stored or computed property. Also, it is used to specify whether the property is 'gettable' or 'settable'.

How do I add a protocol in Swift?

To create a protocol, use the protocol keyword followed by the name you want and defined by the curly braces. Protocols can be of 2 types: read-only/read-write. Read-only means you can only get the variable, but you cannot set it. Read-write means you can both set and get properties.


1 Answers

I've noticed this problem myself, and it seems like a bug to me. You have two options.

First, you can implement the delegate method inside your extension and override it in your subclass. this will ensure the method is called.

extension FormViewController : UITableViewDelegate {    
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        // you can leave the implementation blank if you want
    }
}

class MyViewController: FormViewController {
    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        print("CALLED!!")
    }
}

Second, you can declare the method using the pre-Swift 3 notation and it will work. This part also feels like a bug to me (or all part of the same bug). I wouldn't recommend this option as it is likely to change in future Swift or Xcode versions and generally feels hacky.

extension FormViewController : UITableViewDelegate {    
    // no willDisplayCell method
}

class MyViewController: FormViewController {
    func tableView(_ tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        print("CALLED!!")
    }
}

EDIT: The fact that OP put the UITableViewDelegate methods in an extension isn't causing the problem. The problem exists even if the class itself decalres the delegate.

like image 83
keithbhunter Avatar answered Sep 21 '22 12:09

keithbhunter