无法更新 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
。您还必须观察其center
、bounds
和transform
。
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?的主要内容,如果未能解决你的问题,请参考以下文章