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"];
Related
Three Column(i.e Three Table Views in each Column) UI Design View.
To design the UI on the both iPhone and iPad is possible like 3 column of different tableviews
And among all of columns there is also need interaction like when ever one cell selected in the first tableview then corresponding cell details need to display in the middle table view.(Like Split View Controller.)
And, finally in third table view controller need to display some retailer names too.
Yes, it is possible to create as shown in the iProSoccer app screenshot below. Selecting a plan on left table shows the drills in the middle column. The right column contains the available drills that can be dragged into a plan (middle column). This is a single view controller containing three table controllers added as child view controllers.
EDIT
Each of the UITableViewController's defines a protocol and delegate the containing UIViewController subscribes to. When an action by the user, for example, tapping a plan in the left table view, the left table view controller tells (sends a message to) the delegate (the containing UIViewController). In this example the message includes the plan that was selected. The containing UIViewController updates the center UITableViewController with the plan. The center UITableViewController then loads the details of the plan.
Snippet of (the child) PlanTableViewController.h including protocol and delegate definition:
#import "Plan.h"
#class PlanTableViewController;
#protocol PlanTableViewControllerDelegate <NSObject>
-(void)planSelected:(Plan *)plan sender:(id)sender;
// Additional protocol methods here...
#end
#interface PlanTableViewController : UITableViewController
#property (nonatomic, weak)id <PlanTableViewControllerDelegate>delegate;
// Additional properties and methods here...
#end
Snippet of (the parent) PlanningTableViewController.m including the implementation of the delegate method:
#interface PlanningTableViewController () {
PlanTableViewController *planTableViewController;
// Additional properties declarations here...
#end
#implementation PlanningTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
planTableViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"planTableViewController"];
planTableViewController.delegate = self;
}
#pragma mark - PlanTableViewController delegate methods
- (void)planSelected:(Plan *)plan sender:(id)sender {
[drillTableViewController planSelected:plan];
}
// Additional delegate methods here...
#end
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];
I have an application where A View Controller (A)is called twice in close succession. Now each time it is called, an NSString object is created, and I need this value to be stored in an NSMutableArray that is a public property of ANOTHER View Controller (B).
In A, I create an instance of the second View Controller (B), and using that instance, add the NSString objects into the NSMutableArray which I've created as a public property. Later, when I am inside View Controller B and print the contents of the NSMutableArray property, the array is empty. Why? Here is the code that is inside View Controller A:
-(void)viewDidLoad {
ViewControllerA *aVC = [[ViewControllerA alloc] init];
if (aVC.stringArray == nil) {
aVC.stringArray = [[NSMutableArray alloc] init];
}
[aVC.stringArray addObject:#"hello"];
[aVC.stringArray addObject:#"world"];
for (NSString *wow in aVC.stringArray) {
NSLog(#"The output is: %#", wow);
}
}
Inside my View Controller B class, I have the following code:
- (IBAction)buttonAction:(UIButton *)sender {
NSLog(#"Button selected");
for (NSString *test in self.stringArray) {
NSLog(#"Here are the contents of the array %#", test);
}
}
Now the buttonAction method gets called, as I do see the line Button selected in the system output, but nothing else is printed. Why? One thing I want to ensure is that View Controller A is called twice, which means I would like to see in the output, "Hello World", "Hello World" (i.e. printed twice), and not "Hello World" printed just once.
The other thing I wish to point out is that View Controller B may not be called at all, or it may be called at a later point in time. In any case, whenever View Controller B is called, I would like to have the values inside the array available, and waiting for the user to access. How do I do this?
Your approach is not ideal, potentially leading to a memory cycle, with two objects holding strong pointers to each other.
You can instead achieve your goal in two ways;
Delegate Protocol
This method allows you to set delegates and delegate methods to pass data back and forth between view controllers
in viewControllerA.h
#protocol viewControllerADelegate <NSObject>
- (void)addStringToNSMutableArray:(NSString *)text;
#end
#interface viewControllerA : UIViewController
#property (nonatomic, weak) id <viewControllerADelegate> delegate;
in viewControllerB.m
// create viewControllerA class object
[self.viewControllerA.delegate = self];
- (void)addStringToNSMutableArray:(NSString *)text
{
[self.mutableArray addObject:text];
}
in viewControllerA.m
[self.delegate addStringToNSMutableArray:#"some text"];
Utility Classes
Alternatively you can use a utility class with publicly accessible methods (and temporary data storage). This allows both viewController classes to access a shared data store, also if you use class methods, you don't even need to instantiate the utility class.
in XYZUtilities.h
#import <Foundation/Foundation.h>
#interface XYZUtilities : NSObject
+ (void)addStringToNSMutableArray;
#property (strong, nonatomic) NSMutableArray *array;
#end
in XYZUtilities.m
+ (void)addStringToNSMutableArray
{
NSString *result = #"some text";
[self.array addObject:result];
}
+ (NSArray)getArrayContents
{
return self.array;
}
in viewControllerA.m
NSString *stringFromObject = [XYZUtilities addStringToNSMutableArray];
in viewControllerB.m
self.mutableArray = [[NSMutableArray alloc] initWithArray:[XYZUtilities getArrayContents]];
I'm not sure what kind of a design pattern you are trying to follow but from the looks of it IMHO that's not a very safe one. However, there are many, many ways this could be accomplished.
One thing though, you said that View Controller B may never get allocated and if it is alloc-ed, it will be down the road. So you can't set a value/property on an object that's never been created.
Since you already aren't really following traditional patterns, you could make a static NSMutableArray variable that is declared in the .m of your View Controller B Class and then expose it via class methods.
So it would look like this:
viewControllerB.h
+(void)addStringToPublicArray:(NSString *)string;
viewContrllerB.m
static NSMutableArray *publicStrings = nil;
+(void)addStringToPublicArray:(NSString *)string{
if (publicStrings == nil){
publicStrings = [[NSMutableArray alloc]init];
}
if (string != nil){
[publicStrings addObject:string];
}
}
Then it would be truly public. All instances of view controller B will have access to it. This, of course is not a traditional or recommended way of doing it—I'm sure that you will have many replies pointing that out ;).
Another idea would be to use a singleton class and store the values in there. Then, when or if view controller B is ever created, you can access them from there.
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.
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...