为什么viewDidLayout仅在iOS 7上调用一次,但在iOS 8上调试了几次

时间:2015-04-02 10:59:12

标签: ios

我有一个使用autolayout的项目,

我注意到viewWillAppear之后,{8}会在iOS 8上多次调用viewWillLayoutSubViewsviewDidLayoutSubViews对,对于我的情况,它通常是2-3次。

第一个viewDidLayoutSubViews将获得不正确的帧大小,因此我必须首先避免使用viewDidLayoutSubViews,然后启动我的观看次数。

然而,当我在iOS 7上测试它时,我发现只有一个viewWillLayoutSubViewsviewDidLayoutSubViews对被调用,所以我的代码再次崩溃。

我的问题是,iOS 8对此行为有什么改变?

编辑: 我在这里粘贴了我的演示代码:

在代码中,_pieChart将被添加到self.ChartViewCanvas,而self.ChartViewCanvas将使用autolayout。 _pieChart来自旧的项目代码,它是在没有自动布局的情况下绘制的。

我需要在viewDidAppear之前绘制饼图,因为与storyboard中的其他视图相比,viewDidAppear中的绘图将延迟1秒。我不允许这样做。

有没有办法知道最后的viewDidLayoutSubViews是什么时候?多次调用[self.ChartViewCanvas addSubview:_pieChart];会降低性能,有时_pieChart的drawInRect不会每次调用,因此图表不会更新。

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    _pieChart.delegate = self;
        if (!_pieChart) {
            _pieChart = [[PieChartView alloc] initWithFrame:CGRectMake(0, 0, pieRadius * 2, pieRadius * 2)];
        }else {
            [_pieChart setFrame:CGRectMake(0, 0, pieRadius * 2, pieRadius * 2)];
        }

    //_pieChart.translatesAutoresizingMaskIntoConstraints = NO;
    if ([_pieChart superview]) {
        [_pieChart removeFromSuperview];
    }
    [self.ChartViewCanvas addSubview:_pieChart];
}

2 个答案:

答案 0 :(得分:0)

可能只有Apple知道,但如果一切正常,我就不会处理太多问题。在iOS8中,Apple改变了很多视图控制器(再次),它们是从容器VC和旋转以及UITraitCollections呈现的。
例如,UIAlertView现在是一个视图控制器,当你显示一个时,你会触发与呈现VC相关的所有机制 如果这个事实产生了一个问题,那么必须说你不应该依赖这些方法被调用多少次,因为它们总是不可预测的,有太多的变量需要考虑。
 如果您希望它只被调用一次,那么快速而肮脏的解决方案可以将您的代码包装在dispatch_once中。
如果您使用自动布局正确添加视图,您将看不到任何类型的错误。
[编辑]
这里有一个关于它如何看起来你的viewDidLoad的小片段:

- (void)viewDidLoad
{
    [super viewDidLoad];
    //.. your stuff

    //We don't need any frame autolayout wil take care of calculating it on its pass
    _pieChart = [[PieChartView alloc]initWithFrame:CGRectZero];
    _pieChart.delegate = self;
    _pieChart.translatesAutoresizingMaskIntoConstraints = NO;
    [self.ChartViewCanvas addSubview:_pieChart];
    NSDictionary *bindings = NSDictionaryOfVariableBindings(_pieChart);
    // We create constraints to tell the view that it needs to sctretch its bounds to the superview
    NSString *formatTemplate = @"%@:|[_pieChart]|";
    for (NSString * axis in @[@"H",@"V"]) {
        NSString * format = [NSString stringWithFormat:formatTemplate,axis];
        NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:bindings];
        [_pieChart.superview addConstraints:constraints];
    }

    // Do any additional setup after loading the view.
}

当然这将调用drawRect:,当一个视图在显示传递中被标记为脏时调用rect,但在显示之前通常称为autolayout引擎来计算需要布局的视图帧。

答案 1 :(得分:0)

我在我的应用程序上尝试了这一点,发现和你一样:1在iOS7上调用,在iOS8调用3。从堆栈跟踪看,这似乎是在viewWillAppear之后进行双重布局以及在iOS7上看不到viewDidAppear之后的额外布局。

我的建议是在viewDidLoad(或viewWillAppear)中添加任何视图,然后只在布局子视图中进行布局调整。根据您更新的帖子,例如:

- (void)viewDidLoad{
  [super viewDidLoad];

  _pieChart = [[PieChartView alloc] initWithFrame:CGRectMake(0, 0, pieRadius * 2, pieRadius * 2)];
  [self.ChartViewCanvas addSubview:_pieChart];
  _pieChart.delegate = self;
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [_pieChart setFrame:CGRectMake(0, 0, pieRadius * 2, pieRadius * 2)];
}

感兴趣的是iOS7和8呼叫序列之间的区别是:

<强> iOS7

i)viewWillAppear被召唤。

ii)调用子视图的布局。从堆栈中看,这似乎与导航栏和动画有关。

ii)viewDidAppear被召唤。

<强> iOS8上

i)viewWillAppear被召唤。

ii)调用子视图的布局。从堆栈中看,这似乎与导航栏和动画有关。

iii)再次调用具有完全相同堆栈的完全相同的布局。因此,堆栈中的某些内容必须从某个位置请求重新运行。

iv)viewDidAppear被调用。

v)调用额外的子视图布局。这似乎是从推入运行循环的事务驱动的。