使用特定类型覆盖UIViewController.view

时间:2013-02-05 01:56:42

标签: ios objective-c cocoa-touch

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

我们将有一种特定类型的视图控制器发送方法到特定类型的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

Ovverriding对于定义另一个访问器是有意义的,我只需将消息发送到特定类型的UIView而无需强制转换。

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

属性类型'PlaybackView *'与从'UIViewController'继承的类型'UIView *'

不兼容

。 。我喜欢构建没有任何警告的代码。这样,有效的警告不会被埋没在其他警告之中。

问题:

  • 有没有办法抑制此特定警告?
  • 为什么这是默认设置的一部分,当大多数现代OO语言很乐意允许覆盖子类中的属性或方法,以便它返回超类中声明的类型的更具体的子类?

5 个答案:

答案 0 :(得分:7)

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

所以这......

@class PlaybackView;

@interface PlaybackViewController : UIViewController

@property (nonatomic, strong) PlaybackView* view;

@end

将为您提供上述警告,因为编译器无法知道PlaybackView的继承层次结构。 UIViewController签订合同,从UIView媒体资源

提供view

它告诉你它认为PlaybackView不是UIView

这里的简单解决方案是使用#import代替编译器完全了解PlaybackView ...

#import "PlaybackView.h"

@interface PlaybackViewController : UIViewController

@property (nonatomic, strong) PlaybackView* view;

@end

另外(但由于PCH是优化功能并且不应管理依赖性,因此非常糟糕)是将#import "PlaybackView.h"添加到您的项目PCH

答案 1 :(得分:1)

根据使用#import而不是@class的其他答案中的建议会清除警告,但建议尽可能少地导入标题中,因此我建议保留view没有变化并且有一个额外的PlaybackView * playbackView

  • viewplaybackView指向同一视图完全没问题。
  • 需要了解您的专业view的类必须导入您的控制器标头,因此他们可以首先使用playbackView
  • 更重要的是,如果您希望将您的专业视图作为未来的子视图嵌入(通常会添加UIScrollView超级视图),您将不必重构其他代码和类!
  • 简单明了。

答案 2 :(得分:0)

我认为覆盖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

答案 3 :(得分:0)

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

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

或 - 墙 要了解有关编译器警告抑制的更多信息Clang Manual

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

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

  • OSClient
  • AbstractStageLayoutView
  • PlaybackView 为弱
  • playbackProperty

PlaybackView类是一个类集群,例如NSNumber,你在其上调用一个工厂方法,它将返回一个可能因case而异的对象。如果你检查一个NSnumber,它会返回一个不同的对象,如果你创建一个浮点数或一个整数,但它们都是NSNumber的子类,NSNumber声明它的子类的所有属性,但它没有实现它们。
现在,您可以在抽象类的-viewDidLoad方法中执行的操作是调用类似的方法

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

可以在viewcontroller storyboard编辑器的User defined runtime attibute内对playbackProperty进行估值。

答案 4 :(得分:0)

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

@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