【问题标题】:Override UIViewController.view with specific type用特定类型覆盖 UIViewController.view
【发布时间】:2013-02-05 01:56:41
【问题描述】:

让我们考虑一个具有高度自定义或复杂视图的应用程序。

我们将有一种特定类型的视图控制器向特定类型的 UIView 发送方法,其中 UIView 本身由许多其他视图组成。

视图应该有一个丰富的、特定于域的接口,允许控制器行动是它和一个同样丰富的模型之间的薄“胶水”层。

所以我们重写控制器的视图属性如下:

@interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
    NSMutableArray* _sections;
    LightingMode _lightingMode;
}

@property (nonatomic, strong) PlaybackView* view; // <------ Specific type of view

#pragma mark - injected
@property (nonatomic, strong) id<OscClient> oscClient;
@property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;

@end

覆盖比定义另一个访问器更有意义,我可以将消息发送到特定类型的 UIView 而无需强制转换。

问题:唯一的问题是它会导致编译器警告:

属性类型“PlaybackView *”与继承自“UIViewController”的类型“UIView *”不兼容

。 .我喜欢构建没有任何警告的代码。这样,一个有效的警告就不会被埋在其他警告中而错过。

问题:

  • 有没有办法抑制这个特殊的警告?
  • 为什么这部分是默认设置,而大多数现代 OO 语言会很高兴地允许覆盖子类中的属性或方法,以便返回在超类中声明的类型的更具体的子类?

【问题讨论】:

  • 你为什么不创建一个视图子类并将主 VC 视图用作容器?苹果建议总是组合子类
  • UITableViewController 设法做你正在谈论的事情,因此它不灵活到几乎没用的地步。我会认真考虑这种方法。它可能稍后会回来咬你。
  • 这是一个快速而肮脏的方法,只是为了抑制警告,尝试在这些行之间包装你的代码#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" //YOUR CODE #pragma clang diagnostic pop 或 -Wall
  • @JasperBlues 我已经编辑了我的答案,但当然,之前尝试过。
  • @KirbyTodd 我同意 UITableViewController 的用途有限,但我不认为它是因为它与 UITableView 紧密耦合。 .在许多情况下,视图控制器与特定类型的视图交互。 .这样做有助于控制器承担其作为丰富视图和模型之间的薄粘合层的预期角色。

标签: ios objective-c cocoa-touch


【解决方案1】:

这里的问题不是属性的覆盖,而是使用类类型的前向声明。

所以这个...

@class PlaybackView;

@interface PlaybackViewController : UIViewController

@property (nonatomic, strong) PlaybackView* view;

@end

会给你上面提到的警告,因为编译器无法知道PlaybackView 的继承层次结构。 UIViewController 具有从其 view 属性提供 UIView 的合同

它告诉你它认为PlaybackView 不是UIView

这里的简单解决方案是使用#import 代替,让编译器充分了解PlaybackView...

#import "PlaybackView.h"

@interface PlaybackViewController : UIViewController

@property (nonatomic, strong) PlaybackView* view;

@end

或者(但由于 PCH 是一种优化功能并且不应该管理依赖项,因此形式非常糟糕)是将#import "PlaybackView.h" 添加到您的项目 PCH 中

【讨论】:

  • 当然!如此明显(回想起来)。 .一旦时间到了,也会奖励 +100 赏金。
【解决方案2】:

正如另一个答案中所建议的那样,使用#import 而不是@class 将清除警告,但建议在标题中尽可能少地导入,所以我建议保持view 不变并有一个额外的@ 987654324@:

  • viewplaybackView 都指向同一个视图非常好。
  • 需要了解您的专业view 的类必须导入您的控制器标头,因此他们可以首先使用playbackView
  • 更重要的是,如果您希望将来将专用视图嵌入为子视图(这通常会像添加 UIScrollView 超级视图一样发生),您不必重构其他代码和类!
  • 它很干净。

【讨论】:

  • 在我看来这绝对不是更干净。有另一个链接到一个非常熟悉的结构会使代码的读者感到困惑。如果您要添加另一个 IBOutlet,您将不得不冒险到 IB 看看它实际上与视图相同。如果你在 ViewController 中清楚地覆盖它,你可以看到它实际上是 .view。那个基础 .view 就是它的本质,一个 PlaybackView。我经常使用 Warren Burton 描述的技术,因为我想使用该基本视图来执行不属于该视图中特定项目的视图管理职责。
【解决方案3】:

我不认为覆盖 UIViewControllers 视图属性是个好方法。

我认为这样做更好:

@interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
    NSMutableArray* _sections;
    LightingMode _lightingMode;
}

//@property (nonatomic, strong) PlaybackView* view; //you do not need this property

#pragma mark - injected
@property (nonatomic, strong) id<OscClient> oscClient;
@property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;

@end

在 .m 文件中。

- (void)loadView
{
    PlaybackView *mainView = [[PlaybackView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
    // set the mainView
    self.view = mainView;
}

你可以像这样使用你的 PlaybackView。

((PlaybackView *)(self.view)).oscClient

((PlaybackView *)(xxxViewControler.view)).oscClient

【讨论】:

  • @JasperBlues 在我看来,它更灵活,比如 navigationBar.leftItem ,我们不覆盖 leftItem ,我们只是给 leftItem 一个 buttonItem 。如果您的视图有时需要为 PlaybackView1 类型,有时需要为 PlaybackView2 类型,只需在 loadView 中添加一个 bool 即可处理。而如果你override的话,我觉得做起来也不容易。
【解决方案4】:

从某种意义上说,也许您可​​以声明另一种为您提供演员表的方法。

@implementation PlaybackViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //  use view_ property instead of view
    self.view_.foo = 1;
}

- (void)loadView {
    CGRect frame = [UIScreen mainScreen].applicationFrame;
    self.view = [[PlaybackView alloc] initWithFrame:frame];
}

- (PlaybackView *)view_ {
    return (PlaybackView *)self.view;
}

不完全是最干净的方法,但它确实避免了对self.view 的强制转换(尽管不使用self.view

【讨论】:

  • 这就像提供一个不同的财产,他说他不感兴趣。
  • 好吧,为了迂腐,OP 说的是 "property" 而不是 "method"。我承认现代 ObjC 语法模糊了区别。
  • @NSBum 我想我说的“访问器”确实暗示了一个属性(合成或自定义方法),但它也暗示了“方法”。 . .无论如何感谢 Leo Natan 的建议。
  • 很公平。我想不出任何其他方法来做到这一点;虽然这是一种慢性刺激。
【解决方案5】:

[更新]
最后我可能找到了适合该问题的解决方案: 这是一个快速而肮脏的方法,只是为了抑制警告,尝试在这些行之间包装你的代码

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu" 
//YOUR CODE
#pragma clang diagnostic pop

或 -Wall 了解更多关于编译器警告抑制Clang Manual

[旧答案]
我想给我的 2 美分。 如果我理解得很好,您正在尝试创建一种抽象工厂,它为您提供基于视图控制器功能的专用视图版本。在我看来,故事板在这种设计中效果不佳,但我想告诉你我对它的看法。

首先,我将为您的视图控制器创建一个抽象类,在该接口中您声明所有 VC 子类中所需的所有属性,例如:

  • 操作系统客户端
  • AbstractStageLayoutView
  • PlaybackView
  • 播放属性

PlaybackView 类是一个类簇,例如 NSNumber,您在其上调用工厂方法,该类将返回一个可能因情况而异的对象。如果你检查一个 NSNumber,如果你创建一个浮点数或一个整数,它会返回一个不同的对象,但它们都是 NSNumber 的子类,并且 NSNumber 声明了它的子类的所有属性,但它没有实现它们。
现在你可以在抽象类的 -viewDidLoad 方法中做的是调用这样的方法

PlaybackView *plbackView = [PlaybackView playbackViewFroProperty:self.playbackProperty];
[self.view addSubview:playbackView];
self.playbackView = plbackView;

playbackProperty 可以在 viewcontroller 故事板编辑器的 User defined runtime attibute 中赋值。

【讨论】:

  • 不,很抱歉我的问题措辞很糟糕。我要做的就是在我的 UIViewController 子类中指定我的 UIView 的类型,例如 PlaybackView* (抽象部分是红鲱鱼)。 . .如果你这样做,clang 实际上会发出警告,迫使你到处施法。对于简洁的代码,我想避免强制转换。
  • 现在我明白了,但我没有答案:-(
  • 是的,抱歉我的问题不清楚。 . .我在问题中添加了更多说明,以帮助解释我的意思。 .你怎么看?似乎必须有办法,但我一直没能找到。
  • 您可以禁止编译器警告将您的方法包装在两个预处理器宏周围,但警告有其自身的原因。我不知道你是否可以接受,但为什么不创建一种 myView 方法,简单地调用 view 方法进行转换
  • @Andrea,我说过没有什么比有机会举起 swizzle-cannon 更让我高兴的了 :) 看看 www.typhoonframework.org - 我们在那里用 ObjC 运行时做了一些时髦的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多