如何覆盖 UIViewController 中的“视图”属性?

Posted

技术标签:

【中文标题】如何覆盖 UIViewController 中的“视图”属性?【英文标题】:How do I override the "view" property in UIViewController? 【发布时间】:2010-10-13 09:21:05 【问题描述】:

我有一个自定义 UIViewController 和自定义 UIView。我想覆盖 viewcontroller.view 属性以返回 MyCustomUIView。

现在我有:

@interface MyViewController : UIViewController     
    IBOutlet MyView* view;


@property (nonatomic, retain) IBOutlet MyView* view;

这可以编译,但我收到警告:属性“视图”类型与超类“UIViewController”属性类型不匹配。

如何缓解此警告?

【问题讨论】:

我认为你应该使用@dynamic。请阅读this question,那里的答案对我很有帮助:) 有一篇很好的文章叫做Overriding UIViewController's View Property, Done Right。 【参考方案1】:

@lpaul7 已经发布了 Travis Jeffery 博客的链接作为评论,但它比所有其他答案更正确,它确实需要代码作为答案:

ViewController.h:

@interface ViewController : UIViewController

@property (strong, nonatomic) UIScrollView *view;

@end

ViewController.m:

@implementation ViewController

@dynamic view;

- (void)loadView 
  self.view = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];


@end

如果您使用 xib,请更改视图控制器的根视图类型并将 IBOutlet 添加到属性中,而不是覆盖 loadView

更多详情请见Overriding UIViewController's View Property, Done Right。

【讨论】:

使用该方法我得到以下编译器警告:“属性类型 'MyScrollView *' 与继承自 'UIViewController' 的类型 'UIView *' 不兼容” @Koen 你的 MyScrollView 是 UIView 的子类吗?如果您使用 UIScrollView 而不是 MyScrollView,您会收到编译器警告吗? MyScrollView 是UIScrollView 的子类。我最终使 scrollView 只是 MyCustomView 的容器,我在其中进行所有绘图。而不是在 MyScrollView 上进行所有绘制。 我用我的小眼睛和 UIScrollView 进行监视,保留计数为 1 没有被释放:X - 如果我错了,请纠正我。 @Lifely 如果您没有使用 ARC,那么您会发现 :-) 如果您使用的是 ARC,则代码是正确的(如果您确实输入了,则会出现编译器错误自动释放)。替代答案:确保你没有收到任何编译器或分析警告,你应该很好!【参考方案2】:

简短的回答是你没有。原因是属性实际上只是方法,如果你试图改变返回类型,你会得到:

(UIView *)view; (MyView *)view;

Objective-C 不允许返回类型协方差。

您可以做的是添加一个新属性“myView”,并使其简单地对“view”属性进行类型转换。这将减轻整个代码中的类型转换。只需将您的视图子类分配给视图属性,一切都会正常工作。

【讨论】:

+1 用于准确给出我回答中缺少的部分:)【参考方案3】:

UIViewController 的视图属性/方法将自动返回您的视图。我认为您只需将结果转换为 MyView (或仅使用 id 类型):

MyView *myView = (MyView*)controller.view;
id myView = controller.view;

我认为您在上面发布的代码会给您带来麻烦。您可能不想自己创建视图成员(因为控制器将存储 2 个视图,并且可能不会在内部使用正确的视图)或覆盖视图属性(因为 UIViewController 对其进行了特殊处理。)(参见@ 987654321@ 了解有关后者的更多信息。)

【讨论】:

【参考方案4】:

抱歉,您的简短回答是错误的。

正确的实现是将@property 添加到带有返回类型的头文件中。 然后手动添加getter和setter而不是@synthesize,并从[super view]返回一个强制类型

例如我的类是 PlayerViewController

PlayerViewController.h

@property (strong, nonatomic) IBOutlet PlayerView *view;

PlayerViewController.m

- (PlayerView *)view
    return (PlayerView*)[super view];

- (void)setView:(PlayerView *)view
    [super setView:view];

重要的是把正确的类放到视图中。

将 UIView 放在 PlayerView 所在的位置可能会在 Interface Builder 中工作,但在代码中无法正常工作。

我目前正在使用这个实现。

【讨论】:

我知道这个问题很老了。但我发现了这一点,然后想出了我自己的答案。希望这对未来的用户有所帮助。 你觉得属性应该强吗?我的意思是,UIView 已经拥有强大的属性 view 具有相同的值。 readonly修饰符不是更合适吗? +1:我认为这是可能的。感谢您确认。 @lpaul7 - 不,readonly 没有意义,因为那样你就无法设置 view 属性 - 因此 readonly 的含义。您需要将此属性设置为 strong 以覆盖超类 view 属性(其属性声明为 strong)。 如果你愿意,你可以使用weak,但如果你确保在dealloc上将它设置为nil,那真的没关系。【参考方案5】:

如果有人使用 Swift 2.0+,您可以使用协议扩展来实现可重用:

// Classes conforming to this protocol...
protocol CustomViewProvider 

    typealias ViewType


// ... Will get customView accessor for free
extension CustomViewProvider where Self: UIViewController, Self.ViewType: UIView 

    var customView: Self.ViewType 
        return view as! Self.ViewType
    



// Example:
extension HomeViewController: CustomViewProvider 
    typealias ViewType = HomeView


// Now, we can do something like
let customView: HomeView = HomeViewController().customView

【讨论】:

以上是关于如何覆盖 UIViewController 中的“视图”属性?的主要内容,如果未能解决你的问题,请参考以下文章

UIViewController 扩展不允许覆盖 Swift 中的视图相关功能?

Monotouch - UIViewController 中的 tableview:不会调用覆盖函数

覆盖单独的SecondaryViewControllerForSplitViewController:用于单点触控中的特定UIViewController

如何添加子视图以覆盖 UINavigation barin UIViewController?

在准备 segue 时,如何将数据传递给子 UIViewController?

从 UIViewController 画一条线