无法更新 viewDidLoad 中的 view.layer.frame?

Posted

技术标签:

【中文标题】无法更新 viewDidLoad 中的 view.layer.frame?【英文标题】:cannot update view.layer.frame in viewDidLoad? 【发布时间】:2012-02-28 04:07:44 【问题描述】:

我正在尝试在我的单视图控制器项目的 viewDidLoad 方法中执行以下代码:

self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20); 

但它没有提供所需的插图。但是,我以相同方法进行的其他 UI 更改确实有效,例如

self.view.layer.backgroundColor = [UIColor orangeColor].CGColor;

上面的代码行,背景变为橙色,但框架没有。

仅当我将代码行放在 viewDidAppear 中时,插图才有效。如果有人可以解释,我想了解这种行为的关键原因。提前谢谢你。

【问题讨论】:

【参考方案1】:

我认为你在这行遇到的问题:

self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);

可以这样解释:

在 viewDidLoad 中设置了属性,但尚未定义视图的框架。 在 viewWillAppear 被触发时,它们将被设置。这就解释了为什么那行代码在那里工作。 但从 ios 5 开始,在 viewDidLoad 之后和 viewWillAppear 之前调用了另一个方法,其中设置了视图框架:viewDidLayoutSubviews

你可以在本季关于 iOS 编程的斯坦福 CS193P 课程中找到完整的解释(顺便说一句,很酷)。

因此,如果您希望它只工作一次,请使用:

- (void)viewDidLayoutSubviews

    self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);

PS:我也在 Ray 的论坛上发布了这个答案。 问候, 弗雷德

【讨论】:

【参考方案2】:

viewDidLoad 方法设置视图的框架还为时过早,因为稍后会有其他东西改变框架。

例如,如果这是您的根视图控制器,那么UIWindow 对象将在将视图作为子视图添加到自身时设置视图的框架。只要loadView 返回,viewDidLoad 方法就会在任何外部对象(如UIWindow)获得视图之前运行。

您可以通过使用自定义视图(如果您还没有)并在自定义视图类中覆盖 setFrame: 来测试这一点:

- (void)setFrame:(CGRect)frame 
    [super setFrame:frame];

在该方法中放置一个断点,您会看到视图的框架在viewDidLoad 返回后的某个时间被设置。

【讨论】:

注意,你也可以使用Key Value Observing观察值的变化,虽然它不会给出与子类完全相同的结果(使用断点时不同) @lindonfox 您无法使用 KVO 可靠地观察视图的 frame。您还必须观察其centerboundstransform viewWillAppear 怎么样?在我看来,在viewDidAppear 中这样做为时已晚,并且会导致视觉故障。 系统在布局阶段之前发送viewWillAppear,所以你还没有更新的框架。您需要在布局阶段更新框架,无论是在自定义视图类的layoutSubviews 方法中,还是在UIViewController 子类的viewDidLayoutSubviews 方法中。【参考方案3】:

Rob Mayoff 的回答是正确且出色的,但换一种方式略有不同:viewDidLoad 仅表示视图已加载,即视图控制器已获取其视图。这并不意味着视图已被放置在界面中。这确实是viewDidAppear: 的意思之一——这就是当你在那里运行代码时它起作用的原因。

在这种情况下,您想要初始化有关视图的某些内容的诀窍是做的足够晚,但只做一次。 viewDidAppear: 稍后可以很容易地再次调用,但您不想再次初始化视图(除非它已被卸载)。在 iOS 5 中,isMovingToParentViewController 允许您区分您正在寻找的特定环境。在此之前,可能需要设置一个 BOOL 标志,以便您只执行一次最终初始化。

一个相关的陷阱是当应用程序以横向模式启动时会发生什么。在这里,viewDidLoad 也太早了,因为界面还没有转成横向。

但是,这个问题根本不应该出现。插入视图控制器的视图不应该是您的事。视图要么是根视图控制器,在这种情况下它的大小会自动正​​确处理,要么是父视图控制器的子视图,在这种情况下,它是父视图控制器的工作来调整视图的大小(如 UINavigationController,例如,已经这样做了),或者视图将以模态方式呈现,在这种情况下,它将自动设置其大小以匹配它替换的视图。所以我建议你的问题表明你做错了什么。

【讨论】:

【参考方案4】:

使用旧版本的 Xcode 创建您的项目(例如,我为此使用 Xcode 4.3.3)。然后你可以在任何版本的 Xcode 中使用 setFrame: 方法和 viewDidLoad 。

【讨论】:

【参考方案5】:

我在 Ray Wenderlich 的教程“CALayers 简介”中也遇到了这个问题。 http://www.raywenderlich.com/2502/introduction-to-calayers-tutorial

似乎缩小整个视图控制器的视图并不是最好的做法。而是创建一个子视图并在其上调用 CGRectInset,例如在您的视图控制器 viewDidLoad

UIView *viewToManipulate = [[UIView alloc] initWithFrame:CGRectMake(0.0,0.0, self.view.bounds.size.width, self.view.bounds.size.height)];

[self.view addSubview:viewToManipulate];

viewToManipulate.backgroundColor = [UIColor redColor];
viewToManipulate.layer.cornerRadius = 20.0;
viewToManipulate.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);

【讨论】:

你能详细解释一下为什么缩小整个视图控制器的视图不好? 我在 Ray Wenderlich 的教程“CALayers 简介”中遇到了同样的问题。修复(使用 XCode 4.6 时)是将代码转移到“viewDidAppear”函数中。不是 viewWillAppear 或 viewDidLoad,而是 viewDidAppear。然后它会工作!我想知道 XCode 4.6 可怕的新“使用自动布局”或自动调整大小功能是否会阻止原始代码工作..?

以上是关于无法更新 viewDidLoad 中的 view.layer.frame?的主要内容,如果未能解决你的问题,请参考以下文章

viewDidLoad 中的动画

UIViewController.View.Window 在 ViewDidLoad 方法中为空

在 viewDidLoad 中获取 IB 中的视图框架?

viewDidLoad 中的边界和帧大小

屏幕旋转时 ReloadData()

如何多次调用 viewDidLoad