【问题标题】:iOS: [self.view setNeedsDisplay];iOS:[self.view setNeedsDisplay];
【发布时间】:2014-01-31 05:51:10
【问题描述】:

我似乎忘记了这方面的基础知识,或者我太累了。

我有一个视图控制器和一个名为 MyViewClass 的 UIView 子类的视图。

在我的 viewController 的 viewDidLoad 中,我的 allocinit self.myView 像这样:

self.myView = [[MyViewClass alloc] initWithFrame:(CGRect){{0, 0}, 320, 480}];
self.view = self.myView;

有时在我的 viewController 中,我会调用 [self.view setNeedsDisplay];。这里没问题。

我在 MyViewClass.h 中公开了一个属性:

@property(nonatomic) i;

属性i 将用于myView 中的drawRect; 所以,从我的 viewController 我会这样设置:

self.myView.i = 100;

所以我的问题是:

为什么我可以在我的视图控制器中调用 [self.view setNeedsDisplay]; 而无需调用 [self.myView setNeedsDisplay];drawRect 将在 myView 中被调用,但我不能只在我的视图控制器中将 self.view.i 调用为 @987654336 @ 已分配给 self.view 已经在我的视图控制器中的 viewDidLoad 中?

【问题讨论】:

    标签: ios objective-c setneedsdisplay uiview-hierarchy


    【解决方案1】:

    您可以调用[self.view setNeedsDisplay],但不能调用self.view.i,因为UIViewControllerview 属性属于UIView 类型。

    self.myView 已在 viewDidLoad 中分配给 self.view

    请记住,实际的赋值发生在运行时,而编译器错误是在编译时产生的。编译器不知道视图控制器方法的调用顺序,因此它无法猜测属性值的实际类型超出属性的原始声明。就 Xcode 而言,self.view 只是一个UIView

    正如上面 danh 建议的那样,您可以覆盖属性声明以通知编译器您的视图类型为 MyView。不过,无论如何,您使用 self.myView 属性的方法可能是首选:它允许您在将来更改视图层次结构而不更改您的界面,并且不会使用继承的方法。 UITableViewController 做同样的事情:viewtableView 属性都返回相同的视图实例,但属性的类型不同。

    请务必记住,属性的类型不一定与其的类型相同。 self.view 可能已分配给 MyView 的实例,但 属性 仍然是 UIView 类型。无论分配给它的对象类型如何,属性的访问器仍然是返回类型为UIView 的方法。

    所以,就编译器而言,self.view 只不过是一个普通的旧 UIView,即使你知道它不是。

    我认为这将有助于详细说明什么是属性,并区分属性和实例。当您创建这样的属性时:

    @interface MyViewController : UIViewController
    
    @property(strong, nonatomic) MyView *myView;
    
    @end
    

    编译器将它翻译成一个变量、一个访问器方法(“getter”)和一个修改器方法(“setter”)。该属性只是声明方法的简写,如下所示:

    @interface MyViewController : UIViewController
    {
        MyView *_myView;
    }
    
    - (MyView *)myView; // accessor / getter
    - (void)setMyView:(MyView *)myView; // mutator / setter
    
    @end
    
    @implementation MyViewController
    
    - (MyView *)myView
    {
        return _myView;
    }
    
    - (void)setMyView:(MyView *)myView
    {
        _myView = myView;
    }
    
    @end
    

    当你调用self.myViewself.view时,你实际上是在调用访问器方法;它相当于[self myView][self view]。它返回的是一个指向内存中对象的指针。因为您分配了self.view = self.myView,所以两个属性都设置为同一个对象;因此,self.viewself.myView 都返回指向同一个对象的指针。

    总结一下:

    • 分配self.view = self.myView 不会产生编译器错误,因为MyViewUIView 的子类。请注意,分配self.myView = self.view 生成警告,因为UIView 不是MyView 的子类
    • 调用[self.view setNeedsDisplay] 会导致myView 绘制自己,因为相同的实例 被分配给这两个属性。如果您记录两个属性的描述 (NSLog(@"view=%@, myView=%@", self.view, self.myView)),您可以观察到它们具有相同的内存地址
    • 您不能调用self.view.i,因为view 属性已声明为UIView 类型,而UIView 没有名为i 的方法。

    【讨论】:

    • 抱歉,我在完成之前尝试在评论中输入换行符。我在回答中添加了更多说明。
    • 我认为重要的是要意识到self.myView 不是在绘图,因为self.myView 只是一种方法。 self.myViewMyView 的一个实例)返回的对象正在绘制。尽管 self.view 和 self.myView 是两个不同的方法,但它们返回的是同一个对象。
    • 从技术上讲,您可以通过转换((MyView *)self.view).i 或调用[self.view performSelector:@selector(i)] 或其他更模糊的方式来调用self.view.i。只是苹果的编译器不接受,因为编译器试图强制类型安全。
    • 注意:如果您不从 nib 加载视图控制器的视图,或以编程方式分配它,UIViewController 将在其实现 loadView 中创建一个视图。所以你的控制器总是有一个视图。
    • 谢谢 - 很高兴我能帮上忙!
    【解决方案2】:

    在我的 viewController 的 viewDidLoad 中,我像这样分配和初始化 self.myView:

    self.myView = [[MyViewClass alloc] initWithFrame:(CGRect){{0, 0}, 320, 480}];
    self.view = self.myView;
    

    永远不要在viewDidLoad 中这样做。 viewDidLoad 表示您已经有了视图。你永远不应该改变你的视图控制器的视图。

    设置您的视图loadView 很好(也是必需的)。这就是它的用途。但在viewDidLoad,绝对不是。

    【讨论】:

    • 这偏离了我打算在我的问题中提出的问题,但是:如果我在我的视图控制器的情节提要中没有视图(比如说我删除了)并且我没有设置视图怎么办以编程方式在我的视图控制器中,系统会自动填充我的控制器的默认视图吗?
    • 除了我上面的评论:“....你永远不应该改变你的视图控制器的视图..” 原因是什么?这样做会导致什么样的坏事?谢谢。
    【解决方案3】:

    您正在尝试用 UIView 子类替换 VCs 视图,它是否以所有常用方式工作,但也让 vc 的代码访问子类上的属性?我认为您可以通过覆盖视图获取器来实现:

    - (MyView *)view {
        return (MyView *)[super view];
    }
    

    另外,您可以在 IB 中将自定义视图设置为 VC 的视图,因此无需在 loadView 中进行任何操作。

    【讨论】:

    • 我认为编译器会抱怨 self.view.i 因为 self view 是抽象类型的。这就是为什么我建议更换吸气剂并让它为你做演员。
    • 嗯。很抱歉听到。你的意思是你按照我的建议覆盖设置器,然后说 self.view.i = 100;然后 NSLog(@"%d", self.view.i);它没有记录 100?
    • 请发布错误/警告,以及它出现的行
    猜你喜欢
    • 2015-08-03
    • 1970-01-01
    • 2011-10-14
    • 2010-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多