Reputation: 1199
So I have 2 different table views that use the same array (the array is originally created in the Role table view, the below one). How can I connect those two? (Usually I use prepareForSegue to pass the data but since there is no segue, I'm not sure how can I do this)
EDIT 1: Add the location of the array.
Upvotes: 0
Views: 248
Reputation: 7582
In most of the cases it's useless to pass data around if you don't have a Data Model. You can store your data using a technique called Data Persistence.
An example of a pattern you could use is MVC. MVC or model-view controlelr is an software pattern widely using when making iOS Apps. In this architectural pattern your Controllers are a bridge between your View and your Model.
In this specific scenario both UITableViewController
s would use the same Model but they would display this data differently.
There are several ways to do that, the way I like the most is a little framework called CoreData, you can see this question for some reference on that.
You can also refer to this question to see the use of Singletons. But keep in mind that singletons alone do not persist the data. You'll have to add some sort of mechanism if you want the data to remain there between app sessions.
The simplest way to store small chunks of data is using NSUserDefaults
(but it's only meant to store defaults):
Let's assume you have an array
NSArray* testArray = @[@"first", @"second", @"third"];
You can set it to a key by using
[[NSUserDefaults standardUserDefaults] setObject:testArray forKey:@"myArray"];
You can sync NSUserDefaults
using
[[NSUserDefaults standardUserDefaults] synchronize];
Then, anywhere in your app you can read it doing
[[NSUserDefaults standardUserDefaults] objectForKey:@"myArray"]
On the other hand you have to pass your data around somehow. To do so you can use formal protocols, specifically delegates. As per the Apple documentation:
In a delegate-based model, the view controller defines a protocol for its delegate to implement. The protocol defines methods that are called by the view controller in response to specific actions, such as taps in a Done button. The delegate is then responsible for implementing these methods. For example, when a presented view controller finishes its task, it sends a message to the presenting view controller and that controller dismisses it.
Using delegation to manage interactions with other app objects has key advantages over other techniques:
The delegate object has the opportunity to validate or incorporate changes from the view controller.
The use of a delegate promotes better encapsulation because the view controller does not have to know anything about the class of the delegate. This enables you to reuse that view controller in other parts of your app.
For more information on passing data through view controllers (the main point of this question) take a look at this SO answer.
Upvotes: 1
Reputation: 52227
You should never use data persistence just to pass data through the app. Neither user defaults nor core data.
Also using singletons is not good choice. All will mess up your memory.
Instead use call backs — either as delegates or blocks.
Or use unwind segues.
I explain delegates and unwind segues here: Passing row selection between view controllers
this example passes index paths, as it is appropriate in that situation, but the passed object might be of any type or size, as only pointers are passes.
if you use the NSUserDefaults on the other side, data is copied and written to the disk — there for data is copied and slowly processed — without any use.
I created a sample app how to pass data from one view controller to another view controller in another tab bar branch.
We need to intercept the section of view controllers to set up some callback mechanism. In this case I am using blocks, but delegate would work as-well.
UITabController has a purely optional delegate. I create a subclass of UITabBarController to serv as it's own delegate, but actually a separate delegate should work in the same way.
#import "GameTabBarController.h"
#import "RoleViewController.h"
@interface GameTabBarController () <UITabBarControllerDelegate>
@end
@implementation GameTabBarController
-(void)viewDidLoad
{
[super viewDidLoad];
self.delegate = self;
}
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navController = (UINavigationController *)viewController;
if ([navController.topViewController isKindOfClass:[RoleViewController class]]) {
RoleViewController *rvc = (RoleViewController *)[navController topViewController];
[rvc setSelectedRole:^(Role *role) {
UIViewController *viewController = self.viewControllers[0];
[viewController setValue:role forKey:@"role"];
[self setSelectedIndex:0];
}];
}
}
return YES;
}
@end
I set the initial tab bar controller to this sub class
The RoleViewController displays a list of Roles, but the datasource and delegate for it's table view are a separate class that I add to the role view controller scene in the storyboard, where i also were it up.
Role@interface Role : NSObject
@property (nonatomic,copy, readonly) NSString *name;
-(instancetype)initWithName:(NSString *)name;
@end
#import "Role.h"
@interface Role ()
@property (nonatomic,copy) NSString *name;
@end
@implementation Role
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self) {
_name = name;
}
return self;
}
@end
RoleDatasource
#import <UIKit/UIKit.h>
@class Role;
@interface RoleDatasource : NSObject <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, copy) void(^roleSelector)(Role *role);
@end
#import "RoleDatasource.h"
#import "Role.h"
@interface RoleDatasource ()
@property (nonatomic,strong) NSArray *roles;
@end
@implementation RoleDatasource
- (instancetype)init
{
self = [super init];
if (self) {
_roles = @[[[Role alloc] initWithName:@"Magician"], [[Role alloc] initWithName:@"Soldier"], [[Role alloc] initWithName:@"Maid"]];
}
return self;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.roles.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"RoleCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
cell.textLabel.text = [self.roles[indexPath.row] name];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.roleSelector(self.roles[indexPath.row]);
}
@end
RoleViewController
#import <UIKit/UIKit.h>
@class Role;
@interface RoleViewController : UIViewController
@property (nonatomic, copy) void(^selectedRole)(Role *role);
@end
#import "RoleViewController.h"
#import "RoleDatasource.h"
@interface RoleViewController ()
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@end
@implementation RoleViewController
- (void)viewDidLoad {
[super viewDidLoad];
RoleDatasource *roleDataSource = (RoleDatasource *)[self.tableView dataSource];
[roleDataSource setRoleSelector:^(Role *role) {
self.selectedRole(role);
}];
}
@end
As soon as a role is selected on the role view controller we want to tell our tab bar controller to switch to the game view controller and show the selected role there, see the code for the tab bar controller.
The GameViewController is just a simple view controller subclass that has a property to hold a role and if a role is set, it will displays it name.
#import <UIKit/UIKit.h>
@class Role;
@interface PlayViewController : UIViewController
@property (nonatomic, strong) Role *role;
@end
#import "PlayViewController.h"
#import "Role.h"
@interface PlayViewController ()
@property (weak, nonatomic) IBOutlet UILabel *roleNameLabel;
@end
@implementation PlayViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.roleNameLabel.text = (self.role) ? self.role.name : self.roleNameLabel.text;
}
@end
You'll find an example on github.
Upvotes: 1
Reputation: 1199
I think that I should put the array in the Tab bar Controller and connect it to the Role Table view (in order to maintain the behaviour like it is before) and connect it to my new Table view to do what I want to do.
The only problem I can think of is that since my program is small, adding this will not be a big problem. But if I have more vc, it's going to be so much pain.
Upvotes: -1