【问题标题】:How do I override the "view" property in UIViewController?如何覆盖 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”属性类型不匹配。

如何缓解此警告?

【问题讨论】:

标签: iphone cocoa-touch uiview uiviewcontroller


【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多