This question already has answers here:
Overriding methods in Swift extensions
(6 answers)
Closed 1 year ago.
With new Xcode 8.3, I get the error:
Cannot override a non-dynamic class declaration from an extension
from the line of code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
How I can avoid this warning?
Extensions can add new functionality to a type, but they cannot override existing functionality.
You have two options:
Move your method inside the Class-scope, instead of extension.
Add dynamic type to your method (where it's defined), like example.
Example:
#objc open dynamic func onLog(_ message: String) -> Void {
print("Info: \(message)");
}
which means, you can't not override like super class delegate method in extension.
the way you can do is mv extension override method to subclass declare.
final class DEMO, UITableViewDataSource, UITableViewDelegate {
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return sectionHeader
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
}
Related
I'm trying to adopt the new iOS 13 UITableViewDiffableDataSource and I've hit a snag; I can't work how to implement
func sectionIndexTitles(for tableView: UITableView) -> [String]?
That's a data source method, not a delegate method. So, now that the data source is the UITableViewDiffableDataSource, it needs to implement that method. But it doesn't.
I tried subclassing UITableViewDiffableDataSource and adding an implementation of sectionIndexTitles, but my implementation was never called:
class MyDataSource : UITableViewDiffableDataSource<String,String> {
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return self.snapshot().sectionIdentifiers // not called
}
}
Has anyone solved this one? I'll file it as a bug just in case.
You need to subclass UITableViewDiffableDataSource and then overwrite
func sectionIndexTitles(for tableView: UITableView)
by yourself.
To enable the index' functionality though, you have to also overwrite
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int)
Here is an example how I implemented it:
import UIKit
import MediaPlayer
class TableViewDiffableDataSource: UITableViewDiffableDataSource<String, MPMediaItemCollection> {
var sectionTitles = [String]()
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return sectionTitles
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionTitles[section]
}
override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return sectionTitles.firstIndex(of: title) ?? 0
}
}
Credits go to Steve Breen, who led me in the right direction.
After you init self.dataSource to UITableViewDiffableDataSource (which sets itself to the tableView.dataSource) set the tableView.dataSource back to self, i.e. the UITableViewController subclass. Now in your numberOfSectionsInTableView and numberOfRowsInSection methods forward those to self.dataSource and return its info (this is the composition pattern). Now your UITableViewController just implements its section titles as normal since it is the table's data source.
I believe UITableViewDiffableDataSource should not be setting itself as the dataSource if one is already set but I guess they designed it to work in the least error prone way because with UITableViewController added to a storyboard it's set as the dataSource and delegate by default.
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 ...
}
}
I have some questions with Swift 3 function calling. Below is an example.
Old Swift:
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell
Swift 3:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
That's fine with the above syntax. But now Xcode shows me an error and asks me to do like below:
#objc(tableView:cellForRowAtIndexPath:) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
I do not understand why I have to declare #objc(tableView:cellForRowAtIndexPath:).
This is happening only when I am trying to implement table view datasource methods under an extension. Also this is not happening for numberOfRowsInSection or viewForHeaderInSection.
Can anyone help me to understand why this is happening?
While I am not sure what triggers the #objc, I can suggest the following approach:
Store the tableView variable somewhere in the viewDidLoad:
let tv = tableView!
Then hover over the tableView variable and press the command button in conjunction with a click.
This should take you to the interface of a UITableView.
Then, hover over either UITableViewDelegate or UITableViewDataSource and press the command button in conjunction with a click.
Now you can see the new signatures.
A lot has changed...Happy upgrade!
Swift compiler forced to write Objc(funcName) before function if you are using this function from Objective c. According to app doc
Use the #objc(name) attribute to provide Objective-C names for
properties and methods when necessary. For example, you can mark a
property called enabled to have a getter named isEnabled in
Objective-C like this:
var enabled: Bool {
#objc(isEnabled) get {
// ...
}
}
To void this, use extension to write TableView Datasource and delegate
extension YourViewControllerName:UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell() as SplitAddContactCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80.0
}
}
Let say I want to make Class TableViewDataSourceImpl like this :
class TableViewDataSourceImpl<T, U: UITableViewCell where U: ReusableCell>:NSObject, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
}
then xcode show me error type 'TableViewDataSource<T,U>' does not conform to protocol 'UITableViewDataSource'
is this a bug in xcode? or swift cannot make class with generic that implement protocol? because I gonna make tableView.datasource = tableViewDataSource
i know that we cant convert our swift generic into objective-c, but I not gonna use any objective-c here
I do this in xcode 7 beta 1
just update my xcode to beta 2 and it works!
Today I migrated to Xcode 6 GM seed and now I get the following error:
Type 'ProfilesTableViewController' does not conform to protocol
'UITableViewDataSource'.
I've overrided numberOfRowsInSection, cellForRowAtIndexPath and numberOfSectionsInTableView. In fact everything worked fine till today. I noticed that when I remove UITableViewDataSource everything is working fine, and no errors occured. So .. Is it necessary to use 'UITableViewDataSource' anymore, or just override the functions from it?
This code compiles fine:
class ProfileTableViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("foo", forIndexPath: indexPath) as UITableViewCell
return cell
}
}
So, you can definitely subclass UITableViewController and define its UITableViewDataSource protocol implementation.
Take notice that I am using the override keyword and also that I am not using auto-unwrapped arguments -- i.e., not like this:
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
which was correct in previous Xcode 6 betas.
My guess is that you're not implementing all the required methods, based on the error, at least.
EDIT: Because of the Xcode 6 beta syntax:
override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!){
We had to check the unwrapped tableView. So I used:
if let tblView = tableView {
//code
}
and
if let theIndexPath = indexPath {
//code
}
So ... in order not to change my entire project, I overrided the functions like this:
override func tableView(tableView: UITableView?, didSelectRowAtIndexPath indexPath: NSIndexPath?){
and everything worked fine, except that with my changes the ProfileTableViewController doesn't conform UITableViewDataSource, and I've had to remove the data source from the class definition.