separating UITableView delegate and datasource from tableViewController - ios

My tableView controller is getting huge due to the delegate and data source methods and the fact that I have multiple subclasses of UITableViewCells and different cell layouts for each one. I was looking at this post but it really just goes over how to implement the methods in the dataSource/delegate class, not how to connect it to your view controller.
So I created a class that conforms to the tableView delegate and data source protocols, added a tableView property to be used as a reference to the tableView, and a posts property to be used as the data source array.
#interface SPPostsDataSouceAndDelegate : NSObject <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic) NSArray *posts;
#end
I cut my delegate and data source methods from my tableView controller into the implementation of that class. then in my tableView controller I added a property for that dataSource and delegate class.
#property (nonatomic) SPPostsDataSouceAndDelegate *postsDataSourceAndDelegate;
and in view did load set the data source and delegate as that property, and set that properties tableView and posts properties to that of my view controller.
self.postsDataSourceAndDelegate = [[SPPostsDataSouceAndDelegate alloc] init];
self.tableView.delegate = self.postsDataSourceAndDelegate;
self.tableView.dataSource = self.postsDataSourceAndDelegate;
self.postsDataSourceAndDelegate.posts = self.posts;
However it does not work. Is there anything im missing? Like I said I tried to follow along with the answer I linked but it doesnt really explain this part, and everything else I found doesnt really have anything on what to do once you actually create the separate class so im sort of lost here.
edit: I refactored my code so that the delegate/datasource no longer has a reference back to the tableView. so thats good. still not doing anything though

figured it out. needed to update the posts property of the data source after retrieving them from the backend. was able to refactor a lot of my custom cell code as well to get rid of all the if blocks for all the different cells in my table using the dynamic selector method mentioned here. everything is working great now
[allPostsQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
self.postsDataSourceAndDelegate.posts = objects;
[self.tableView reloadData];

Related

Sharing data between two table views

I have two table views that contain a mutable array of objects that can be instantiated by the user with various properties. The class and its properties are within separate .h and .m files that the table views access.
I would like the two table views to look identical so that when a user adds, deletes, or moves an object in one table view, the second table view is immediately updated. I understand that they will be sharing a mutable array, but I'm not sure where to put it so that changes in one table view occurs in the other table view.
Additionally, is this a situation in which I would make one of the table views a subclass of the other?
Suppose there is a controller owns the NSMutableArray object and the two table views.
Use the KVO feature, let the two table view retain (use strong as property) the NSMutableArray object.
Create a new delegates array property like NSArray< id<MyArrayNotifier> > *delegates for the NSMutableArray object's owner, implement the MyArrayNotifier protocol in two table view classes, add the table objects to delegates array. Now you could get notified when your focused message arrived.
First you should make a singleton class that contains that mutable array so you can edit/access it anywhere , whenever you update that array reload the visible tableView , if the other tableView is also visible reload it(either with delegate , observer , notificationCenter), otherwise it will be updated with the last edit when you open the VC that contains it , also you can make a use of viewDidAppear to reload the table inside it as another choice instead of delegates if it's the logic of your app
// .h
#import <Foundation/Foundation.h>
#interface GlobalData : NSObject
#property(nonatomic,retain)NSMutableArray*masterArray;
+(GlobalData*)shared;
#end
// .m
#import "GlobalData.h"
#implementation GlobalData
static GlobalData *instance = nil;
+(GlobalData *)shared
{
#synchronized(self)
{
if(instance==nil)
{
instance = [GlobalData new];
instance.masterArray = [NSMutableArray new];
}
}
return instance;
}
#end
Usage
[GlobalData.shared.masterArray addObject:"strData"];

IBOutlet UILabel not updating after delegate call

Screencast: https://www.youtube.com/watch?v=ZwehDwITEyI
This is really bizarre. The problem is to do with a label outlet sitting in a custom-designed table cell. That cell is of my CustomCell class. (actually called RA_FormCell if you watch the screencast).
CustomCell.h
#property (weak, nonatomic) IBOutlet UILabel *dayOutlet;
-(void)controller:(id<CustomCellDelegate>)controller didUpdateDay:(NSString *)theDay;
CustomCell.m
// Method is called by a view controller
// (which is itself a delegate of the CustomCell class,
// hence the identifier you see below)
-(void)controller:(id<CustomCellDelegate>)controller didUpdateDay:(NSString *)theDay;
{
NSLog(#"Method called") // confirms to me that method is called
self.dayOutlet.text = #"Goodmorning";
NSLog(#"%#", self.dayOutlet.text); // displays (null)
}
That final log does actually appear, so the method is definitely being called. I have discounted the following:
self.dayOutlet.text is not written to elsewhere by any other method in the project
dayOutlet is connected to the label in the storyboard (and the label is not connected to anything else)
The label is not hidden underneath some accidental static label on the storyboard
The cell attributes on the storyboard include its class as CustomCell
No warnings or alerts in Xcode (I have been careful to avoid any circular imports)
The problem was that the controller:didUpdateDay: message was not sent to the correct instance of the cell class.
This occurred because I had not correctly assigned this cell to be the delegate for the view controller. For anyone interested, in my screencast at 3:50, you can see that I have the following in cellForRowAtIndexPath:
RA_FormCell *cell = [tableView dequeueReusableCellWithIdentifier:self.formCellArray[indexPath.row] forIndexPath:indexPath];
self.delegate = cell
However, this means that self.delegate got continually overwritten as the table cells were generated. As a result, my controller:didUpdateDay message was sent to the bottom cell of the table, and not the top one as I required.
The solution was simple - there's no need to have this second delegate at all. Instead, when the cell delegates to the view controller, it should pass self into the message it delegates:
id<CustomCellDelegate> strongDelegate = self.delegate;
if ([strongDelegate respondsToSelector:#selector(customCell:didChangeDay1:)])
[strongDelegate customCell:self didChangeDay1:[sender value]];
Then, in the implementation of this method by the delegate, simply end it by changing the outlet directly:
-(void)customCell:(RA_FormCell *)customCell didChangeDay1:(double)value
// put logic here
customCell.dayOutlet.text = #"No problem!";
In general, there should rarely be a need for a two-way delegate structure. Keep it one way, from A to B, and just remember to have A pass self in any messages it sends to B. That way, B will know the object the message came from, and be able to communicate back to A.
Thanks to Paulw11

Where to initialise an object

I have a UITableView in my main view and an 'add' button in a UINavigationBar that will push to another view that allows the user to add another object to the tableview. I have a protocol in this view that allows the information to be sent back to the main view.
My problem is that whenever I try to add this new object sent from the protocol (which is a NSMutableDictionary) to a NSMutableDictionary property in the main view, it does not add. I have tried adding an NSLog and it says that this object is null. If I initialise this object in the viewDidLoad method, it will run whenever the UINavigationController pops the view, and resets everything in the dictionary. I do not know where to initialise the object to make sure that it keeps everything stored in it.
The protocol works fine, but I cannot do anything with the object it sends.
In AddCellViewController.h:
#import <UIKit/UIKit.h>
#protocol AddCellDelegate <NSObject>
#required
-(void)passCellInfo:(NSMutableDictionary *)cellInfo;
#end
#interface AddCellViewController : UITableViewController <UITextFieldDelegate> {
id <AddCellDelegate> delegate;
}
#property (strong) id <AddCellDelegate> delegate;
#end
in AddCellViewController.m (the method that utilises the protocol):
-(void)sendObject{
[[self delegate] passCellInfo:newCellInfo];
[self.navigationController popToRootViewControllerAnimated:YES];
}
in MainView.m:
-(void)passCellInfo:(NSMutableDictionary *)cellInfo{
[self.cellInformation setValue:cellInfo forKey:[cellInfo objectForKey:#"cell_title"]];
[self.cells addObject:[cellInfo objectForKey:#"cell_title"]];
[self.tableView reloadData];
NSLog(#"%#: cellInfo - cellInformation: %# - cells: %#",cellInfo ,self.cellInformation,self.cells); //logs the object passed from the protocol (this works), the cellInformation object, and cells object (these return null)
}
You can use a NSMutableDictionary property in a static or global class, and access it very easy from the tableview or any other view:
You feed your table from that dictionary
And you add elements from your other view into that dictionary (the protocol is not needed anymore).
Everytime your tableView appears, you should refresh the data of the table from this global class.
Some example of how to use it:
How to implement global static class
In your code you do this. You using same key every time. So it will get replaced.
[self.cells addObject:cellInfo];
I will tell you a simple way instead. Send your mutableDictionary to secondView from init method. Copy in class level mutableDictionary. Do not allocate or initialize. Add new item in that Dictionary. It will reflect in mainView dictionary. Call [tableView reloadData] in mainView viewWillAppear method.

Array + UIView Controller + UITableViewController

I have a UIViewController class and a UITableViewController class. Within the UIViewController class I have an NSMutableArray.
I now have the issue of how to load data into my table view, a separate class, I must access the NSMutableArray I used to populate the previous UIViewController class.
I tried using a delegate to access the array in the UIViewControllerClass however the array had "0 objects" and was NULL
I would appreciate some guidance in the right direction here.
You could have one view controller hold a reference to the other view controller and query the public NSMutableArray on it for data. Aaron suggested this and it might be your best solution.
Or.. you have multiple view controllers trying to access the same set of data. Potentially you have other classes which will want to access this data also. You might want to consider pulling the data out of the view controller and storying it in a neutral location. You could store it in the AppDelegate and then reference the app delegates from any place you need it.
id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
NSMutableArray *myData = appDelegate.data;
You could also consider pulling all the logic of your data and the data itself into a separate class and use a Singleton It would allow you to access/manipulate the data fairly easy from anywhere.
The last 2 methods would insulate data from user interface controller objects and prevent the need from potentially unrelated objects needing to hold references to one another. Used properly it will reduce code complexity and mage future changes easier to manage.
Create an NSMutableArray property on your UITableViewController class like so:
#interface CustomTableViewController : UITableViewController
#property (strong, nonatomic) NSMutableArray *dataFromOtherClass;
#end
And then when you transition, perhaps like this, you can set the dataFromOtherClass property:
CustomTableViewController *controller = [[CustomTableViewController alloc] initWithNibName:#"CustomTableViewController" bundle:nil];
controller.dataFromOtherClass = myNSMutableArrayData; // <-- Set data like this
[self.navigationController controller animated:YES];
// Or ...
[self presentViewController:controller animated:YES];
// Etc...

iOS 5, Storyboards,ARC: Creating and setting custom delegate and datasource to program. created UITableView

The prequel of this problem is here.
I am trying to create and set a custom delegate and datasource to my programmatically created UITableView. I've googled around, but couldn't find any clear solution for my problem.
Meanwhile, I've created a new class that conforms to UITableViewDelegate,UITableViewDataSource
protocols. In this class:
tableView numberOfRowsInSection: 20
tableView cellForRowAtIndexPath: cell.textLabel.text=#"Nominals";
Class that contains UIViews:
Method that creates UITableView:
-(IBAction)segmentValueChaged:(id)sender
{
if(self.segment.selectedSegmentIndex==0)
{
[self.coinageView removeFromSuperview];
[self.view addSubview:nominalsView];
[self populateNominals:self.subCountryID];
}
else
{
[self.nominalsView removeFromSuperview];
[self.view addSubview:coinageView];
[self populateCoinages:self.subCountryID];
}
}
-(void)populateNominals:(int)subCountryID
{
NominalsTableViewDelegate *del=[[NominalsTableViewDelegate alloc]init];
UITableView *nominalsTableView=[[UITableView alloc]initWithFrame:CGRectMake(0, 0, 320, 372) style:UITableViewStylePlain];
[nominalsTableView setDelegate:del];
[nominalsTableView setDataSource:del];
[self.nominalsView addSubview:nominalsTableView];
[nominalsTableView reloadData];
}
Finally, I'm getting EXC_BAD_ACCESS.
The evil is in [nominalsTableView setDelegate:del]; [nominalsTableView setDataSource:del]; rows. What's wrong with them.
Help is much appreciated. Thanks in advance.
I am not sure if you are adding a new UITableView to your current view or not, but I will assume you are doing so.
If you have a class that conforms to the UITableViewDelegate. But when i needed to create a UITableView programmatically, I create a new UITableView class (with the .h and .m) then make a MutableArray and exposing it as a property to the parent view so it can set the data source.
From there, you can create an instance of the class along with the datasource (which you exposed from the child object). Finally, you then add the view onto your current view. This method you dont need to set the delegate because your child class conforms to the tableview delegate protocol.
If you are just modifying the data inside the current tableview then, you use a NSMutableArray and then change the data in it. After then do a
[self.tableView reloadData];
Hope this helps!
EDITED
I might have misunderstood your question, what you (most likely) need to do is create a property of the delegate class then create an instance of your delgate class and assign it to the property.
Then do a
[myTableView setDelegate:self.myProperty];
[myTableView setDatasource:self.myProperty];
This, I believe, would solve you bad access problem.
EDITED AGAIN
Create a property inside your .h of the tableview class as such:
#property (nonatomic, retain) NominalsTableViewDelegate *myDelegate;
From there then inside your .m file, you do something similar to this:
NominalsTableViewDelegate* delegateClass = [[NominalsTableViewDelegate alloc] init];
[self setMyDelegate:delegateClass];
[delegateClass release];
Then you can set your tableview datasource as such:
[myTableView setDelegate:self.myDelegate];
[myTableView setDatasource:self.myDelegate];
Note: I currently have no access to a machine to test this, but just something to point you towards.

Resources