Dynamic Datasource/Delegates for Uitableview in Swift

Dynamic datasource/delegates for UITableView in swift

NSObject? doesn't conforms to UITableViewDelegate, neither to UITableViewDataSource. You should create your protocol like

protocol GeneralDataSource: UITableViewDataSource, UITableViewDelegate {}

And then all data sources should conform that protocol.

class MyDataSource: NSObject, GeneralDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return UITableViewCell()
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
}

Then you can use it like this

var myDataSource: GeneralDataSource?

override func viewDidLoad() {
super.viewDidLoad()

self.myDataSource = MyDataSource()
self.tableView.delegate = self.myDataSource
}

Dynamic DataSource swap for UITableView in Swift

UITableView's dataSource property is either unsafe_unretained or weak, depending on which version of iOS. Either way, as with any other delegate, it doesn't keep a strong reference.

So when you write a line like this:

self.tableView.dataSource = DummyDataSource()

Your newly instantiated DummyDataSource() property doesn't have any strong references pointing to it. It is therefore immediately released by ARC.

We need to keep a strong reference to the data source if we want it to stick around.

My recommendation would be to add a data source property to your view controller which can keep the strong reference. We will also use the didSet of this property to set the table view's data source property and reload its data.

var dataSource: UITableViewDataSource? {
didSet {
tableView?.dataSource = dataSource
tableView?.reloadData()
}
}

We use optional-chaining to protect against the data source being set before the view is loaded and the tableView property is populated. Otherwise, we will get a fatal error for trying to unwrap nil.

We shouldn't need to be setting the data source property on the table view anywhere else. And the only reason why we should need to called reloadData() anywhere else is if our data source itself can change the data it is representing. However, it is important that reloadData() is called in sync with resetting the dataSource to protect against some likely index-out-of-bound crashes.

How can I programmatically set dataSource of UITableView?

Change your code to:

class ViewController: UIViewController 
{
@IBOutlet weak var table: UITableView!
var dataSource: MyData?

override func viewDidLoad()
{
super.viewDidLoad()

dataSource = MyData()
table.dataSource = dataSource!
}
}

Your app breaks because the ds is deallocated as soon as viewDidLoad returns. You have to keep a reference to your data source.

Dynamic UITableView Datasource inside of Static UITableView Cell

After some more experimentation I was able to figure it out! Looking back it seems obvious, but I was doing some weird circular referencing ridiculousness when I instantiated my datasource. I dropped out the init function, and the reference to the tableView argument I was using in my dataSource subclass. This is what my dataSource class looks like now:

let dataSource = ValidatedCardsDataSource()
self.validatedCardsTable.dataSource = dataSource
self.validatedCardsTable.delegate = dataSource
class ValidatedCardsDataSource :NSObject, UITableViewDataSource, UITableViewDelegate {

var selectedRow = 0
let rowCount = 10

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100.0
}

func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100.0
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 1
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = "Title of Row: #\(indexPath.row)"
return cell
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(indexPath.row)
self.selectedRow = indexPath.row
tableView.reloadData()
}
}

Now I just have to figure out my constraints... Thanks for the responses!

Dynamic UITableView in Swift

when user done with 1) got permission for the location and 2) filtered the closest shops these 2 things just call tableview.reloadData() and your tableview will be refresh & updated with latest data.

Set UITableView Delegate and DataSource

rickster is right. But I guess you need to use a strong qualifier for your property since at the end of your viewDidLoad method the object will be deallocated anyway.

@property (strong,nonatomic) SmallTable *delegate;

// inside viewDidload

[super viewDidLoad];
self.delegate = [[SmallTable alloc] init];
[self.myTable setDelegate:myTableDelegate];
[self.myTable setDataSource:myTableDelegate];

But is there any reason to use a separated object (data source and delegate) for your table? Why don't you set SmallViewController as both the source and the delegate for your table?

In addition you are not creating the cell in the correct way. These lines do nothing:

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

// Configure the cell...
cell.textLabel.text = @"Hello there!";

dequeueReusableCellWithIdentifier simply retrieves from the table "cache" a cell that has already created and that can be reused (this to avoid memory consumption) but you haven't created any.

Where are you doing alloc-init? Do this instead:

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(!cell) {
cell = // alloc-init here
}
// Configure the cell...
cell.textLabel.text = @"Hello there!";

Furthermore say to numberOfSectionsInTableView to return 1 instead of 0:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}

Implementation of UITableView delegate and datasource in VIPER

Yes, data source and delegate are the parts of a view layer.

If you do not want your view to ask presenter for data, then you can do it like I describe it.
The data source class holds viewModels(dummy objects). You can then communicate via an interface. I mean you might understand better on some example:

protocol SomeViewProtocol {
func set(withVMS vms: [SomeViewModel])
}

final class SomeVC: SomeViewProtocol {

let dataSource: SomeDataSource
let tableView: UITableView

override func viewDidLoad() {
tableView.dataSource = dataSource
}

func set(withVMS vms: [SomeViewModel]) {
someDataSource.set(withVMS: vms)
tableView.reloadData()
}
}
protocol SomePresenterProtocol {
...
}

final class SomePresenter: SomePresenterProtocol {

fileprivate let view: SomeViewProtocol

//After view did load
func initAfterLoad() {
.
.
.

view.set(withVMS: viewModels)
}
}

But from my perspective, there is nothing wrong with View asking the presenter for the data.



Related Topics



Leave a reply



Submit