在 viewDidAppear 之前无法正确设置框架

43

我在想,为什么当你在viewDidLoadviewWillAppear中设置子视图的框架时,更改不会在屏幕上生效,但如果你在viewDidAppear中设置,则会生效?

就我的情况而言,我正在加载一个带有两个表视图的自定义xib,然后尝试在viewDidLoad中将它们向下移动,以留出空间放置另一个视图。由于不必始终显示该视图,因此该视图在viewDidLoad中添加。

问题是,当我在viewDidLoadviewWillAppear中设置框架时,它被设置到对象上,我可以通过打印来看到,但它不会反映在屏幕上。将我的设置框架调用移动到viewDidAppear中将使一切按预期工作。

viewDidLoad中设置框架是错误的吗?

- (id)init {
    if ( self = [super initWithNibName:@"MyView" bundle:nil] ) {}

    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.descriptionWebView = [[[UIWebView alloc] initWithFrame:CGRectMake( 0, 0, self.view.frame.size.width, 200 )] autorelease];

    [self.view addSubview:self.descriptionWebView];

    self.tableView1.autoresizingMask = UIViewAutoresizingNone;
    self.tableView2.autoresizingMask = UIViewAutoresizingNone;

    [self.descriptionWebView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"...." withExtension:@"html"]]];

    table1LocationWithHeader = CGRectMake( self.tableView1.frame.origin.x, 200, self.tableView1.frame.size.width, self.tableView1.frame.size.height - 200 );
    table2LocationWithHeader = CGRectMake( self.tableView2.frame.origin.x, 200, self.tableView2.frame.size.width, self.tableView2.frame.size.height - 200 );

    //this will NOT work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    //this will NOT work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}


- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    //this WILL work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}


//I added these after comments for stack overflow users, it seems like viewDidLayoutSubviews is the best place to set the frame

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    //this will NOT work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    //this WILL work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}

你可以展示一下你的代码吗? - Nekto
2
可能是UIViewController返回无效框架?的重复问题。 - rob mayoff
注意!你的viewWillAppear调用[super viewDidAppear];看起来像是复制粘贴错误。 - Maxim Kholyavkin
3个回答

100

viewDidLoad方法在类被加载后被调用,但是此时尚未初始化UI元素,因此任何试图引用它们的尝试都将在初始化过程中被覆盖或不可用(这个过程发生在viewDidLoadviewDidAppear之间)。一旦所有UI元素都被初始化并绘制完成,就会调用viewDidAppear方法。

viewDidLoad - 当控制器的视图被加载到内存中后调用

此时视图还没有被添加到视图层次结构中。

viewWillAppear - 通知视图控制器其视图即将添加到视图层次结构中。

同样地,视图尚未添加到视图层次结构中。

viewDidAppear - 通知视图控制器其视图已添加到视图层次结构中。

只有这时候视图才会被添加到视图层次结构中。

更新

viewDidLayoutSubviews是修改UI的最合适的位置,因为它可以在UI实际上在屏幕上显示之前进行修改。

viewDidLayoutSubviews - 通知视图控制器其视图刚刚完成对其子视图的布局。


8
我刚试过了,viewDidLayoutSubviews 似乎是设置 frame 最好的位置。谢谢您帮我澄清这一点。 - nacross
3
我认为当您在界面生成器中设置的约束与您所设置的框架冲突时,就会出现此问题。这必须发生在viewWillLayoutSubviews和viewDidLayoutSubviews之间。 - nacross
2
viewDidLayoutSubviews 真是太棒了!我已经开发 iOS 有两年了,这是我第一次听说它。非常好的答案!+1 - ctlockey
5
我经常看到一个答案,其中一个重要问题被忽略了。viewDidLayoutSubviews会被调用多次(比如在添加子视图时,如@DienBell所解释的)。如果你在那里进行许多框架计算,它将多次执行这些计算。远非优雅。可以设置一个标志,但第一次调用可能会有一个不完整的框架。所以你真的想要最后一次调用。但我想不到实现这一点的方法。检查self.view.frame不是CGRectZero可能会起作用,但同样也很丑陋。 - bauerMusic
1
@Noitidart 不是的。我一直在“期望”苹果有一天会解决这个问题。我猜想,由于某些框架依赖于其他框架,所以viewDidLayoutSubviews是正确的位置,但我大多数情况下使用UIScreen.main.bounds来获取屏幕大小(它总是设置正确且不会改变)。请查看我的帖子:https://dev59.com/SpHea4cB1Zd3GeqPp307 - bauerMusic
显示剩余5条评论

2

看一下这篇帖子 什么时候会调用layoutSubviews方法?
当使用自动布局时,框架不会自动调用layoutSubviews方法。这很重要。参考:

  • init不会导致调用layoutSubviews方法(显而易见)
  • addSubview: 会在被添加到的视图(目标视图)上调用layoutSubviews方法,以及目标视图的所有子视图上也会调用。...


如果你在viewDidLoad中添加了子视图,那么layoutSubviews方法会在viewDidAppear之前被调用,并且您可以获得子视图的正确大小。但是,如果您什么都没有做,layoutSubviews方法将在viewDidAppear之后被调用。这取决于您的代码。


0

在viewWillAppear()方法中

只需为要获取其frame的视图调用一次layoutIfNeeded()方法。

例如,如果是tableView,则调用tableView.layoutIfNeeded()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接