Ios 以良好的设计原则访问UINavigationItem

Ios 以良好的设计原则访问UINavigationItem,ios,objective-c,cocoa-touch,oop,uinavigationitem,Ios,Objective C,Cocoa Touch,Oop,Uinavigationitem,我正在开发一个iOS应用程序,其中有一个名为FlowController的类,它管理一组UIViewController的流 因此,我们的基本结构是创建一个FlowController实例,该实例创建一个UINavigationController,然后创建子视图控制器,以便在用户与视图控制器的视图交互时推送到导航控制器上 如果用户与与流相关的视图交互,则视图控制器将该操作委托给流控制器,然后流控制器将管理将新视图控制器推送到导航堆栈上 这样做的目的是将流代码保持在视图控制器之外,以使视图控制器

我正在开发一个iOS应用程序,其中有一个名为FlowController的类,它管理一组
UIViewController
的流

因此,我们的基本结构是创建一个FlowController实例,该实例创建一个
UINavigationController
,然后创建子视图控制器,以便在用户与视图控制器的视图交互时推送到导航控制器上

如果用户与与流相关的视图交互,则视图控制器将该操作委托给流控制器,然后流控制器将管理将新视图控制器推送到导航堆栈上

这样做的目的是将流代码保持在视图控制器之外,以使视图控制器更加模块化

随着我们开发这一概念,以及我们的设计师开始在我们的应用程序中添加更多的
uibarbuttonims
,我意识到我可以从FlowController中向
UIViewController的
-navigationItem属性添加
UIBarButtonims
,目标是FlowController,选择器是FlowController中的选择器

例如:

//Code in FlowController

-(void) startFlow{
    UIViewController* firstViewController = [[UIViewController alloc] init];
    firstViewController.navigationItem.rightBarButtonItem = [self nextBarButtonItem];

   [self.navigationController pushViewController:firstViewController animated:TRUE completion:nil];
}

-(UIBarButtonItem*) nextBarButtonItem{
    return [[UIBarButtonItem alloc] initWithTitle:@"Next" target:self selector:@selector(next:)];
}

-(void) next:(id) sender{
    UIViewController* secondViewController = [[UIViewController alloc] init];

    [self.navigationController pushViewController: secondViewController animated:TRUE completion:nil];
}
//Code in UIViewController

-(void) addRightBarButtonItemWithTitle:(NSString*) title target:(id) target selector:(SEL) selector{
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:title target:target selector:selector];
}

//Code in FlowController

-(void) startFlow{
    UIViewController* firstViewController = [[UIViewController alloc] init];
    [firstViewController addRightBarButtonItemWithTitle:@"Next" target:self selector:@selector(next:)];

   [self.navigationController pushViewController:firstViewController animated:TRUE completion:nil];
}
通过这种方式,我认为我的代码效率很高,因为我们有几个屏幕使用相同的视图控制器,但在导航栏中有不同的栏按钮项

当我和我的一位同事谈到这个问题时,他说这违反了设计原则,因为我是从视图控制器外部访问“视图”代码的

具体来说,我在FlowController中创建了
uibarbuttonims
。他告诉我,除了
UIViewController
子类之外,我们不应该从任何外部访问
UIViewController的
-
navigationItem
属性,我所做的就是打破面向对象的封装设计原则

他建议我应该在
UIViewController
上创建方法,该方法将目标和选择器添加到视图控制器的-
navigationItem
内部。然后在FlowController中调用该方法

例如:

//Code in FlowController

-(void) startFlow{
    UIViewController* firstViewController = [[UIViewController alloc] init];
    firstViewController.navigationItem.rightBarButtonItem = [self nextBarButtonItem];

   [self.navigationController pushViewController:firstViewController animated:TRUE completion:nil];
}

-(UIBarButtonItem*) nextBarButtonItem{
    return [[UIBarButtonItem alloc] initWithTitle:@"Next" target:self selector:@selector(next:)];
}

-(void) next:(id) sender{
    UIViewController* secondViewController = [[UIViewController alloc] init];

    [self.navigationController pushViewController: secondViewController animated:TRUE completion:nil];
}
//Code in UIViewController

-(void) addRightBarButtonItemWithTitle:(NSString*) title target:(id) target selector:(SEL) selector{
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:title target:target selector:selector];
}

//Code in FlowController

-(void) startFlow{
    UIViewController* firstViewController = [[UIViewController alloc] init];
    [firstViewController addRightBarButtonItemWithTitle:@"Next" target:self selector:@selector(next:)];

   [self.navigationController pushViewController:firstViewController animated:TRUE completion:nil];
}
然而,我最初的直觉是不同意这一点,因为FlowController在我们MVC应用程序的“控制器”部分的范围内,并且应该允许控制器访问视图。此外,如果我按照同事的建议做,我将不得不添加一个

-addRightBarButtonimWithTitle:target:selector

方法,或创建每个视图控制器必须从中继承的子类

对于“封装”的概念来说,这似乎是一个很大的开销和不必要的依赖关系,并且限制性地将接口包装到navigationItem(因为现在我只能有一个带文本的-
RightBarButtonim
;没有图像或自定义视图


最后我要问的问题是:从
UIViewController
子类外部访问
UIViewController
-
navigationItem
属性是不是很糟糕的面向对象编程?如果是,苹果为什么要公开它?(我知道受保护的概念在目标C中并不存在,但在Swift中它确实存在,因为它仍然是公开的。)

IMHO,流量控制器是否应该填充
navigationItem
s取决于您如何看待它:

  • 如果(步骤)视图控制器的导航项与视图控制器的内容相关/链接/有某种关系,则应由视图控制器决定。这进一步细分为两种情况:

    • 如果步骤视图控制器被设计为一个独立的组件,它不知道流程,并且可以在没有流程的情况下生存,那么它应该提供配置导航项的方法,以及为导航项的事件分配处理程序的钩子(委托)。通过“配置”,它的意思正是您的同事建议的
    • 如果步骤视图控制器是流的一部分,并且知道其所有者流控制器,则它可以直接调用流控制器的方法(例如,
      nextStep
      previousStep
  • 如果(步骤)视图控制器的导航项完全独立于视图控制器的内容——换句话说,步骤视图控制器有能力拥有一些导航项,但它不关心其导航项——然后流控制器可以直接修改这些导航项。这是您最初做的


在Obj C和Swift中,
navigationItems
都是公共的,通常从Obj C迁移的方法/属性在Swift中成为公共的。Swift和Obj C都没有“受保护”的概念,似乎苹果不在乎()。

不要写类似问题的故事。