Swift 3 ObjC Optional Protocol Method Not Called in Subclass - ios

I have the following class hierarchy:
class ScrollableViewController: UIViewController, UITableViewDelegate { // ... }
That implements one UITableViewDelegate protocol method, e.g. tableView:willDisplayCellAt:
In my class SpecificScrollableViewController, which inherits from ScrollableViewController, new optional protocol methods don't get called any more, e.g. tableView(_:heightForRowAt:)

tl;dr you need to prefix the function declaration with its Objective-C declaration, e.g.
#objc(tableView:heightForRowAtIndexPath:)
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// return stuff
}
I was tipped off to this being a solution thanks to the Swift 3 Migration Guide which states:
If you implement an optional Objective-C protocol requirement in a subclass of a class that declares conformance, you’ll see a warning, “Instance method ‘…’ nearly matches optional requirement ‘…’ of protocol ‘…’”
• Workaround: Add an #objc(objectiveC:name:) attribute before the implementation of the optional requirement with the original Objective-C selector inside.
I'm fairly certain this is a bug: it appears that the the runtime dynamism that allows checking for selector capability does not get properly bridged in the Grand Swift Renaming when the protocol method is in the subclass. Prefixing the function declaration with the Objective-C name properly bridges the Swift to Objective-C and allows Swift 3 renamed methods to be queried with canPerformAction:withSender: from within Objective-C

This appears to be fixed in Swift 3.0.1 for normal subclasses, but is not fixed for generic subclasses:
class A: NSObject, UITableViewDelegate {}
class B: A {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
}
class C<T>: A {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
}
print(#selector(B.tableView(_:didSelectRowAt:))) // tableView:didSelectRowAtIndexPath:
print(#selector(C<Int>.tableView(_:didSelectRowAt:))) // tableView:didSelectRowAt:
See: https://bugs.swift.org/browse/SR-2817
To fix it: https://stackoverflow.com/a/39416386/1109892
#objc(tableView:heightForRowAtIndexPath:)
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// return stuff
}

Related

Cannot adopt UITableViewDataSource protocol - Method does not override any method from its superclass

I'm playing around with the UITableView in XCode 10.2.1 with Swift 5. According to the Apple Developer docs, adopting the UITableViewDataSource is the most straightforward way to populate a UITableView with dynamic data.
So I copied the necessary methods to override into a custom class:
import Foundation
import UIKit
class MyDataSource : NSObject, UITableViewDataSource {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Fetch a cell of the appropriate type.
let cell = tableView.dequeueReusableCell(withIdentifier: "cellTypeIdentifier", for: indexPath)
// Configure the cell’s contents.
cell.textLabel!.text = "Cell text"
return cell
}
}
But my code won't compile. All I get is a "Method does not override any method from its superclass" error. What? I even did used the autocomplete feature from XCode and it generated the stubs for me, yet I still cannot build my project. What is the solution?
Your class' superclass is NSObject which does not have those table view data source methods to override in a subclass. I believe that is what that compiler is saying.
I think if you take off the override keyword on those function declarations that could help.

UITableViewDelegate and UITableviewDataSource : Cannot override a non-dynamic class declaration from an extension - Swift 4

I am trying to override the UITableViewDelegate methods inside extension.The base class has already implemented the methods. Please find the details below:
Base Class:
class BaseTableViewController:UITableViewDelegate,UITableViewDataSource{
//Base Class. The delegate and datasource methods has been implemented
}
class ChildViewController: BaseTableViewController{
//Inherited class
}
extension ChildViewController {
//Trying to override the Tableview Delegate and Datasource methods but getting error.
}
Error Detail:
I am trying to do the conversion from Swift 3.0 to Swift 4.0.
The implementation was working fine with Swift 3.0 but got error in Swift 4.0.
I have looked into below links:
Override non-dynamic class delaration
Please suggest the right approach for the above implementation.
The right approach seems to override methods from protocols inside the class and not into an extension:
class BaseTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
}
}
class ChildViewController: BaseTableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
}
}
Extensions are meant to add new functionalities, not override existing ones. Another approach would be to delegate those functions on an external object.

Make an extensions of generic class in Swift4

Lets assume we have a simple generic class:
class Foo<T> {
}
next add to this class an extension which implements UITableViewDatasoure:
extension Foo: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//Code here
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Code here
}
}
This construction causes a compiler error with message:
#objc is not supported within extensions of generic classes or classes
that inherit from generic classes Non-'#objc' method
'tableView(_:numberOfRowsInSection:)' does not satisfy requirement of
'#objc' protocol 'UITableViewDataSource'
Anyone can tell me why? And how to fix that?
The error message seems very clear. This isn't supported. You can't attach an #objc method to a generic class in an extension. You need to define these methods in the class definition, not an extension. The "why" is "the compiler doesn't support it today." (It's likely hard to support because of specializations, but the real answer is "the compiler can't do it.")
Currently the Xcode doesn't support attach an #objc method to a generic class in an extension, you can fix it by define the method inside the class definition, as following:
class Foo<T>: UIViewController, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//Code here ...
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Code here ...
}
}

Don't run delegate method in didSelectRowAt

HogeViewController.swift
protocol HogeDelegate: class {
func huga()
}
class HogeViewController: UIViewController,UITableViewDelegate, UITableViewDataSource{
weak var delegate:
func tableView(didSelectRowAt){
self.delegate?.huga()
}
}
HogeTableViewCell.swift
class HogeTableViewCell: UITableViewCell, HogeDelegate{
func huga(){
print("huga")
}
}
don't run this code...
Is this writing impossible?
func tableView(didSelectRowAt) isn't the signature of the UITableViewDelegate's method, so no, it's never getting called. Your function's signature needs to be
func tableView(_ tableView: UITableView, didSelectRowAt rowIndex: Int)
Even better, instead of having your class extend UIViewController and implement the UITableView data source and delegate, just make your class extend UITableViewController, which already implements the data source and delegate, and will thus force you to use the override modifier, like
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
Since the cell is variable it seems to be a specification that can not implement methods via delegate.

Swift 3.0 Conversion UITableView Not Calling Methods In UITableViewController [duplicate]

I have updated my code to swift 3.0 and get a warning on the following line:
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
When I try each of the suggestions to either silence the warning with #nonobjc or make it a private function the table no longer loads.
The error reads:
Instance method 'tableView(:cellForRowAtIndexPath:)' nearly matches optional requirement 'tableView(:canFocusRowAt:)' of protocol 'UITableViewDelegate'
Does anyone know what causes this error and how to fix it?
Many thanks!
Just add the declaration of implementing UITableViewDataSource protocol to the class definition like this:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {}
In swift 3.0 the signature for the datasource changed to:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
Notice the difference between cellForRowAtIndexPath indexPath: IndexPath and cellForRowAt indexPath: IndexPath
I'm using the new method without any warnings, hope this will solve your problem.
Cheers.
I had a similar problem, and found that if you remove the space between the underscore before tableView, from this
func tableView(_ tableView: ...
to this
func tableView(_tableView: ...
strangely the warning goes away...

Resources