Currently, I want to have a separated class for data only. However, I cannot just declare a class like this:
class DataSource: UITableViewDataSource
It would give me a lot of error. And, I have to do this instead:
class DataSource: UIViewController, UITableViewDataSource
So, why? I come from Java background, so I don't understand why I have to implement A to implement B. I tried reading the official documents from Apple, but I could not find the answer.
Edit: here is the error:
Type 'DataSource' does not conform to protocol 'UITableViewDataSource'
Type 'DataSource' does not conform to protocol 'NSObjectProtocol'
and, XCode suggests a solution to fix it is to add "#objc " at the beginning of the override function. The error still there after the fix though.
Edit 2: I am aware that I need to implement 2 functions for the data source to work. However, it will not work without implementing UIViewController.
It will work after adding UIViewController!
Protocol UITableViewDataSource comes from NSObjectProtocol. So you have to make your class inherit from NSObject to conform to the NSObjectProtocol. Vanilla Swift classes do not. But many parts of UIKit expect NSObjects. And you have to implement the required methods of this protocol. Try below code:
class DataSource: NSObject, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return UITableViewCell()
}
}
Related
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.
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.
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
}
I am learning swift 2.0, and I was wondering if you still need to add the code tableView.datasource = self and tableView.delegate = self like in Obj-C to conform to the protocols?
Example code:
class AboutViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
// conform to protocols
aboutTableView.dataSource = self
aboutTableView.delegate = self
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 2
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return 50
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
// Code here
}
}
Now the table view loads with the correct data in each cell.
However, if I remove aboutTableView.datasource = self and aboutTableView.delegate = self from viewDidLoad, my table view is blank. Why is this?
Is this code still required because I see many youtube tutorials that does not include this anymore in swift, and I'm confused as to why mine doesn't work without it?
First of all, that is completely independent of which language
you use, Swift or Objective-C.
But there are two different cases which may cause the confusion:
A UITableViewController subclass:
UITableViewController already conforms to UITableViewDataSource and UITableViewDelegate. It has a tableView property,
whose dataSource and delegate property are already set to self.
In your subclass, you typically override the
data source and delegate methods.
A UIViewController subclass with a UITableView property:
Here you have defined a UITableView property in your subclass
and initialize it in your code, or
connect it to a table view in the interface builder.
In this case you have to set the dataSource and delegate
property of the tableview, either in code or in the interface
builder, as explained in luk2302's answer.
If data source and delegate are the view controller itself,
then you have to declare the protocol conformance explicitly,
and implement the
data source and delegate methods (but without overriding
a superclass method).
Of course, in both cases, the table view data source and the delegate
can be set to a different object, it does not have to be
the view controller itself.
Yes, some assignment is still required.
Either explicitly via code
OR
What you can do instead is connect them already in the interface builder, making the explicit assignment via code obsolete. That is probably what a lot of tutorials do.
I am trying to configure a page to look something as follows - where I have a standard view controller with a table view inside of it.
I have my prototype cell denied, so I can configure these in code and add the relevant images.
However, I am trying to set the code base up to do this (i.e.) implement the required table view methods but am getting a number of errors;
Is this occurring because this only apples when you have a UITableView and not a Table in a normal view controller? If so, how do I manage my dynamic table in the code?
Thanks for your time
When using a UITableView in a viewcontroller (not in an UITableViewController) as you are doing it, your custom viewcontroller has to implement the UITableViewDataSource and the UITableViewDelegate protocols.
At least the following required functions:
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
...
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
}
Since your class is not derived from UITableViewController it does not override these functions, so remove the override directive.