将来自不同 ViewController 的数据与相同的父级连接起来
Connecting data from different ViewController with same parents
所以我有 2 个不同的 table 视图,它们使用相同的数组(该数组最初是在 Role table 视图中创建的,如下所示)。我怎样才能把这两者联系起来?
(通常我使用 prepareForSegue 来传递数据,但由于没有 segue,我不确定我该怎么做)
编辑 1:添加数组的位置。
我认为我应该将数组放在选项卡栏控制器中并将其连接到角色 Table 视图(为了保持以前的行为)并将其连接到我的新 Table 视图做我想做的事。
我唯一能想到的问题是,由于我的程序很小,加上这个问题不大。但是如果我有更多vc,那就太痛苦了。
什么是模型以及为什么需要它
在大多数情况下,如果没有数据模型,传递数据是没有用的。您可以使用一种称为数据持久性的技术来存储数据。
您可以使用的模式示例是 MVC。
MVC 或模型视图控制器是制作 iOS 应用程序时广泛使用的软件模式。在这种架构模式中,您的控制器是您的视图和模型之间的桥梁。
在这种特定情况下,两个 UITableViewController
将使用相同的模型,但它们会以不同的方式显示此数据。
坚持你的模型
有几种方法可以做到这一点,我最喜欢的方法是一个名为 CoreData, you can see this 的 little 框架,供参考。
也可以参考this问题了解单例的使用。但请记住,单例 不会 保留数据。如果您希望数据在应用程序会话之间保留在那里,则必须添加某种机制。
保留用户首选项
存储小块数据的最简单方法是使用 NSUserDefaults
(但它 仅用于存储默认值 ):
假设您有一个数组
NSArray* testArray = @[@"first", @"second", @"third"];
您可以使用
将其设置为一个键
[[NSUserDefaults standardUserDefaults] setObject:testArray forKey:@"myArray"];
您可以使用
同步NSUserDefaults
[[NSUserDefaults standardUserDefaults] synchronize];
然后,您可以在应用程序的任何位置阅读它
[[NSUserDefaults standardUserDefaults] objectForKey:@"myArray"]
通过应用程序传递数据
另一方面,您必须以某种方式传递数据。为此,您可以使用正式协议,特别是委托。
根据 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.
有关通过视图控制器传递数据的更多信息(此问题的要点),请查看 SO 答案。
您永远不应该仅仅为了通过应用程序传递数据而使用数据持久性。既没有用户默认值也没有核心数据。
使用单例也不是好的选择。都会打乱你的记忆。
而是使用回调——作为委托或块。
或者使用 unwind segues。
我在这里解释委托和展开 segues:Passing row selection between view controllers
此示例传递索引路径,因为它适用于那种情况,但传递的对象可能是任何类型或大小,因为只有指针是传递的。
如果你在另一边使用 NSUserDefaults,数据被复制并写入磁盘——在那里复制数据并缓慢处理——没有任何用处。
我创建了一个示例应用程序如何将数据从一个视图控制器传递到另一个标签栏分支中的另一个视图控制器。
点击放大
TabBarController
我们需要截取视图控制器部分来设置一些回调机制。在这种情况下,我使用的是块,但委托可以工作 as-well.
UITabController 有一个纯可选的委托。我创建了一个 UITabBarController 的子 class 作为它自己的委托来服务,但实际上一个单独的委托应该以相同的方式工作。
#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
我将初始标签栏控制器设置为此子class
角色、角色数据源和角色视图控制器
RoleViewController 显示角色列表,但它的 table 视图的数据源和委托是一个单独的 class,我将其添加到情节提要中的角色视图控制器场景,我也在那里是不是起来了。
角色
@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
角色数据源
#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
角色视图控制器
#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
播放视图控制器
一旦在角色视图控制器上选择了一个角色,我们就会告诉我们的标签栏控制器切换到游戏视图控制器并在那里显示所选角色,请参阅标签栏控制器的代码。
GameViewController 只是一个简单的视图控制器 subclass,它有一个 属性 来保存一个角色,如果设置了一个角色,它将显示它的名字。
#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
您将在 github 上找到示例。
所以我有 2 个不同的 table 视图,它们使用相同的数组(该数组最初是在 Role table 视图中创建的,如下所示)。我怎样才能把这两者联系起来? (通常我使用 prepareForSegue 来传递数据,但由于没有 segue,我不确定我该怎么做)
编辑 1:添加数组的位置。
我认为我应该将数组放在选项卡栏控制器中并将其连接到角色 Table 视图(为了保持以前的行为)并将其连接到我的新 Table 视图做我想做的事。
我唯一能想到的问题是,由于我的程序很小,加上这个问题不大。但是如果我有更多vc,那就太痛苦了。
什么是模型以及为什么需要它
在大多数情况下,如果没有数据模型,传递数据是没有用的。您可以使用一种称为数据持久性的技术来存储数据。
您可以使用的模式示例是 MVC。 MVC 或模型视图控制器是制作 iOS 应用程序时广泛使用的软件模式。在这种架构模式中,您的控制器是您的视图和模型之间的桥梁。
在这种特定情况下,两个 UITableViewController
将使用相同的模型,但它们会以不同的方式显示此数据。
坚持你的模型
有几种方法可以做到这一点,我最喜欢的方法是一个名为 CoreData, you can see this 的 little 框架,供参考。
也可以参考this问题了解单例的使用。但请记住,单例 不会 保留数据。如果您希望数据在应用程序会话之间保留在那里,则必须添加某种机制。
保留用户首选项
存储小块数据的最简单方法是使用 NSUserDefaults
(但它 仅用于存储默认值 ):
假设您有一个数组
NSArray* testArray = @[@"first", @"second", @"third"];
您可以使用
将其设置为一个键[[NSUserDefaults standardUserDefaults] setObject:testArray forKey:@"myArray"];
您可以使用
同步NSUserDefaults
[[NSUserDefaults standardUserDefaults] synchronize];
然后,您可以在应用程序的任何位置阅读它
[[NSUserDefaults standardUserDefaults] objectForKey:@"myArray"]
通过应用程序传递数据
另一方面,您必须以某种方式传递数据。为此,您可以使用正式协议,特别是委托。 根据 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.
有关通过视图控制器传递数据的更多信息(此问题的要点),请查看
您永远不应该仅仅为了通过应用程序传递数据而使用数据持久性。既没有用户默认值也没有核心数据。
使用单例也不是好的选择。都会打乱你的记忆。
而是使用回调——作为委托或块。
或者使用 unwind segues。
我在这里解释委托和展开 segues:Passing row selection between view controllers
此示例传递索引路径,因为它适用于那种情况,但传递的对象可能是任何类型或大小,因为只有指针是传递的。
如果你在另一边使用 NSUserDefaults,数据被复制并写入磁盘——在那里复制数据并缓慢处理——没有任何用处。
我创建了一个示例应用程序如何将数据从一个视图控制器传递到另一个标签栏分支中的另一个视图控制器。
TabBarController
我们需要截取视图控制器部分来设置一些回调机制。在这种情况下,我使用的是块,但委托可以工作 as-well.
UITabController 有一个纯可选的委托。我创建了一个 UITabBarController 的子 class 作为它自己的委托来服务,但实际上一个单独的委托应该以相同的方式工作。
#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
我将初始标签栏控制器设置为此子class
角色、角色数据源和角色视图控制器
RoleViewController 显示角色列表,但它的 table 视图的数据源和委托是一个单独的 class,我将其添加到情节提要中的角色视图控制器场景,我也在那里是不是起来了。
角色@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
角色数据源
#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
角色视图控制器
#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
播放视图控制器
一旦在角色视图控制器上选择了一个角色,我们就会告诉我们的标签栏控制器切换到游戏视图控制器并在那里显示所选角色,请参阅标签栏控制器的代码。
GameViewController 只是一个简单的视图控制器 subclass,它有一个 属性 来保存一个角色,如果设置了一个角色,它将显示它的名字。
#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
您将在 github 上找到示例。