【问题标题】:Connecting data from different ViewController with same parents将来自不同 ViewController 的数据与相同的父级连接
【发布时间】:2015-03-31 13:45:43
【问题描述】:

所以我有 2 个使用相同数组的不同表视图(该数组最初是在 Role 表视图中创建的,如下所示)。我怎样才能连接这两个? (通常我使用prepareForSegue来传递数据,但由于没有segue,我不知道我该怎么做)

编辑 1:添加数组的位置。

【问题讨论】:

  • 你从哪里得到这个数组?
  • @fpg1503 添加了数组位置:)。谢谢你提醒我:)
  • 如果你使用标签栏控制器,你应该有不相关的应用组件显示在单独的标签上......考虑使用导航控制器并改变你的视图控制器层次
  • @user3435374 感谢您的意见。我很确定我现在在做什么:)
  • 虽然有些人可能不同意,但您可以尝试将单例类作为“全局数据对象”。

标签: ios objective-c uitableview


【解决方案1】:

什么是模型以及为什么需要它

在大多数情况下,如果您没有数据模型,则传递数据是没有用的。您可以使用一种称为数据持久性的技术来存储数据。

您可以使用的一个模式示例是 MVC。 MVC 或模型视图控制器是制作 iOS 应用程序时广泛使用的软件模式。在这种架构模式中,您的控制器是您的视图和模型之间的桥梁。

在这种特定情况下,UITableViewControllers 都将使用相同的模型,但它们会以不同的方式显示此数据。

持久化你的模型

有几种方法可以做到这一点,我最喜欢的方法是一个名为CoreData 框架,您可以查看this 问题以获得一些参考。

你也可以参考this问题看看Singletons的使用。但请记住,单例不会保存数据。如果您希望数据在应用会话之间保留在那里,则必须添加某种机制。

保留用户偏好

存储小块数据的最简单方法是使用NSUserDefaults(但它仅用于存储默认值):

假设你有一个数组

NSArray* testArray = @[@"first", @"second", @"third"];

您可以通过使用将其设置为键

[[NSUserDefaults standardUserDefaults] setObject:testArray forKey:@"myArray"];

您可以使用同步NSUserDefaults

[[NSUserDefaults standardUserDefaults] synchronize];

然后,您可以在应用中的任何位置阅读它

[[NSUserDefaults standardUserDefaults] objectForKey:@"myArray"]

通过应用传递数据

另一方面,您必须以某种方式传递数据。为此,您可以使用正式协议,特别是委托。 根据Apple documentation

在基于委托的模型中,视图控制器定义了一个协议 其代表来实施。该协议定义的方法是 由视图控制器调用以响应特定操作,例如 点击完成按钮。然后委托人负责 实施这些方法。例如,当呈现的视图 控制器完成其任务,它向呈现者发送一条消息 查看控制器,该控制器将其关闭。

使用委托来管理与其他应用对象的交互具有关键作用 与其他技术相比的优势:

  • 委托对象有机会验证或合并 视图控制器的变化。

  • 使用委托可以提升 更好的封装,因为视图控制器不必知道 关于代表班级的任何事情。这使您可以重用 该视图控制器位于应用的其他部分。

有关通过视图控制器传递数据的更多信息(这个问题的重点),请查看this SO answer。

【讨论】:

  • NSUserDefaults 不应用于存储数据或全局变量。它用于用户默认值。
  • 我知道,这就是我提到CoreData的原因
  • 没错,但我不得不说。由于它适用于 OP 的情况,因此他最终可能会使用它来存储他的数据(这似乎不是默认值),除非另有警告。
  • 使用单例实际上很棒:)。我刚刚检查了它并完全理解了正确的方法:)。谢谢你们两个:)
  • 这个方法将你的默认同步到文件中,不需要任何参数,它只是一个显式的同步请求。系统不时自动同步。
【解决方案2】:

您不应该仅仅为了通过应用传递数据而使用数据持久性。既不是用户默认值,也不是核心数据。
同样使用单例也不是好的选择。一切都会打乱你的记忆。

改为使用回调——作为委托或块。

或者使用 unwind segues。

我在这里解释代表和解开问题:Passing row selection between view controllers

这个例子传递了索引路径,因为它在那种情况下是合适的,但是传递的对象可能是任何类型或大小的,因为只有指针是传递的。

如果你在另一边使用 NSUserDefaults,数据被复制并写入磁盘——在那里数据被复制并缓慢处理——没有任何用处。


我创建了一个示例应用程序,如何将数据从一个视图控制器传递到另一个标签栏分支中的另一个视图控制器。

点击放大

TabBarController

我们需要拦截视图控制器的部分来设置一些回调机制。在这种情况下,我使用的是块,但委托也可以工作。

UITabController 有一个纯可选的委托。我创建了一个 UITabBarController 的子类来作为它自己的委托,但实际上一个单独的委托应该以相同的方式工作。

#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

我将初始标签栏控制器设置为这个子类

Role、RoleDatasource 和 RoleViewController

RoleViewController 显示一个角色列表,但它的表视图的数据源和委托是一个单独的类,我添加到情节提要中的角色视图控制器场景中,我也在其中。

角色
@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

PlayViewController

一旦在角色视图控制器上选择了一个角色,我们想要告诉我们的标签栏控制器切换到游戏视图控制器并在那里显示所选角色,请参阅标签栏控制器的代码。
GameViewController 只是一个简单的视图控制器子类,它有一个属性来保存角色,如果设置了角色,它将显示它的名称。


#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 上找到一个示例。

【讨论】:

  • 我明白你的意思,我同意你的观点,但我不认为他的问题直接涉及将数据从一个 VC 传递到另一个 VC,它主要源于缺乏模型。老实说,我认为让这两个表视图来回传递一个数组以用作模型并不是一个好主意。恕我直言,在这种特定情况下,使用类来访问模型是最佳选择,无论是 CoreData DAO、在某处写入数据的单例,还是他认为更适合他需要的任何东西。我的意思是他不是试图只传递一个 NSIndexPath,我们想转移他的整个模型。
  • 索引路径只是另一个对象。通过最适合的方法。
  • 你的回答真的触及了问题的核心(传递数据),很好:)
  • 1.感谢您回答我的问题@vikingosegundo。 2.由于我是 obj-C 的新手,你介意为我解释一下这些东西吗?一种。我根本不明白 GameTabBarController 的概念(我对委托的使用略有了解,从未使用过)。湾。我不明白为什么你有 RoleDataResources 做 UITableView 的工作? C。属性(非原子,复制) void(^selectedRole)(Role *role);我没有得到这个(包括 ^selectedRole 部分,对我来说有点奇怪)。3。您这里的版本与 github 上的版本略有不同(特别是在 GameTabBarViewController 上)
  • 抱歉,您的评论涉及 iOS 编程的许多不同方面。我怀疑我可以在 cmets 中教给你所有这些。 a)委托在可可(您使用的框架)中使用,您必须能够实现它才能继续。 b) Roledatasource 是用于填充表格视图的委托/数据源。我更喜欢将代码与视图控制器分开以遵循 SRP(单一责任原则)。 Apple 在« Advanced User Interfaces with Collection Views» 中透露他们也这样做了,尽管几乎所有示例代码都违反了 SRP。
【解决方案3】:

我认为我应该将数组放入 Tab bar Controller 并将其连接到 Role Table 视图(以保持与以前一样的行为)并将其连接到我的新 Table 视图以执行我想要的操作做。

我能想到的唯一问题是,由于我的程序很小,添加它不会是一个大问题。但是如果我有更多的vc,那就太痛苦了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 2013-09-24
    • 2016-08-18
    • 1970-01-01
    • 2021-12-03
    • 2018-02-06
    相关资源
    最近更新 更多