18

Since I added the following code, every time my app opens this UITableViewController it crashes:

 self.noArticlesView = [[UIView alloc] init];
 self.noArticlesView.translatesAutoresizingMaskIntoConstraints = NO;
 self.noArticlesView.backgroundColor = [UIColor colorWithRed:0.961 green:0.961 blue:0.961 alpha:1];

 [self.view addSubview:self.noArticlesView];

 [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.noArticlesView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
 [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.noArticlesView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]];
 [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.noArticlesView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0]];
 [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.noArticlesView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0]];    

And it gives me this error:

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'

What on earth am I doing wrong? I call that code in tableView:numberOfRowsInSection: when there's 0 rows.

Doug Smith
  • 29,668
  • 57
  • 204
  • 388
  • 1
    Not an expert, but I had this issue too, and I think it's because UITableView, implements it's own version of layoutSubviews (or another methods like it which is needed for auto layout). I up your question cause I would like to have the answer too :) – Tancrede Chazallet Nov 07 '13 at 13:34
  • Yeah, I might just have to go back to using frames for this (what I did previously) but Auto Layout would have been nice to use. :/ – Doug Smith Nov 07 '13 at 13:34
  • 1
    I went back to frame for this, but it's really not convenient once everything is with autolayout... – Tancrede Chazallet Nov 07 '13 at 13:36
  • is self.view a table view and self.noArticlesView is a view that should overlay that showing that there are no table cells? – Max MacLeod Nov 07 '13 at 14:01
  • Well, this is all operating in a `UITableViewController` subclass, so I imagine `self.view` is a tableview. You're correct about `self.noArticlesView`. – Doug Smith Nov 07 '13 at 14:08

9 Answers9

14

I was subclassing UIScrollView and received the same error message on iOS 7 (but not 8).

I was overriding layoutSubviews in a manner similar to the following:

- (void)layoutSubviews {
    [super layoutSubviews];
    // code to scroll the view
}

I resolved the issue by moving the call to super's layoutSubviews to be the last thing in the method:

- (void)layoutSubviews {
    // code to scroll the view
    [super layoutSubviews];
}
Senseful
  • 86,719
  • 67
  • 308
  • 465
  • I encounter the same problem. My `-(void)layoutSubViews` of sub-class will modify some constraint constant. Call `[super layoutSubviews]` before modifying constraint will crash in iOS7 (but not 8). – AechoLiu May 18 '15 at 07:12
2

Had the same problem. Added view(s) to self.tableView and used constraints. Do not add the views to the table view via addSubview: but add them as header(s), footer(s) or cells.

dergab
  • 965
  • 7
  • 16
2

[self.view layoutIfNeeded]

Hope this helps

Zay
  • 115
  • 1
  • 9
1

You also need to disable mask translation for the table view.

Cocoanetics
  • 8,171
  • 2
  • 30
  • 57
1

for me is was this

self.tableView.backgroundView = self.emptyView;

I changed to this

    NSComparisonResult order = [[UIDevice currentDevice].systemVersion compare: @"8.0" options: NSNumericSearch];
if (order == NSOrderedSame || order == NSOrderedDescending) {
    // OS version >= 8.0
    self.tableView.backgroundView = self.emptyView;
}else{
    [self.tableView.backgroundView addSubview:self.emptyView];
}
MUH Mobile Inc.
  • 1,462
  • 1
  • 16
  • 25
0

Checkout "Auto Layout still required after executing -layoutSubviews" with UITableViewCell subclass as the question appears to be the same.

I was able to implement the category mentioned in one of the answers which solved the problem for me. However, I had to create the category on the UITableView class instead of the UITableViewCell class as is discussed in that particular answer.

Community
  • 1
  • 1
lidsinker
  • 1,190
  • 1
  • 11
  • 20
0

You can add your 'no articles view' as a custom header in the table to make sure it's positioned correctly.

ilya n.
  • 18,398
  • 15
  • 71
  • 89
0

A possible solution is not to add the noArticlesView directly in the table, but is to put the UITableView inside a container UIView (eventually setting table constraints to fit with the container frame) and then constraint your noArticlesView to the container, using the same constraints you set and in the same place in your code, that is inside the -tableView:numberOfRowsInSection: UITableViewDataSource method. I tested it with a simple example, and it worked.
The changes you need to apply to your code are to replace the UITableViewController with a UIViewController, add a container view (unless you want your table to fit exactly with the view controller's view, in such case this view is the container) and then constraint your noArticleView to the container instead of the table. My example code is at the bottom of this answer.
I will try to make a possible explanation of the reason of the issue and why this solution works, but consider that part of my explanation is based on guesses so it couldn't be completely exact.
First of all a brief explanation of how the view hierarchy rendering process works in iOS. The first step for the layout engine is to determine the size and position of all views and subviews in the view hierarchy. Usually this is done with an iterative approach, where auto layout constraints are evaluated, in order to determine size and position of views and subviews, and then each view's -layoutSubviews method is called for fine tuning: this means that you can change your layout after constraints are evaluated. What the layout engine requires is that if the -layoutSubviews method changes constraints again, then -[super layoutSubviews] must be called again to allow the iterative process: if this doesn't happen the layout engine raises an exception. In the case of the UITableView my guess is that the tableView:numberOfRowsInSection: method is called somewhere within the internal UITableView layoutSubviews method and this method doesn't call [super layoutSubviews]. So if your table data source method updates internal table constraints, at the end of the method the exception is triggered. The solution I propose works because the only constraint applied to the table is the external constraint towards the container, so evaluated at the first stage of the layout process, while the constraints added in the table data source method are not relevant to the table as they are applied to views external to the table (container and noArticlesView) so they don't affect the internal table view layout process.



//
//  RootController.h

#import 

@interface RootController : UIViewController

@property (nonatomic,strong) IBOutlet UITableView *table;
@property (nonatomic,strong) IBOutlet UIView *container;

@end

//
//  RootController.m


#import "RootController.h"

@implementation RootController

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.

    UIView *_v = [[UIView alloc] init];
    _v.backgroundColor=[UIColor redColor];
    _v.translatesAutoresizingMaskIntoConstraints=NO;

    [self.container addSubview:_v];
    NSLayoutConstraint *_c1 = [NSLayoutConstraint constraintWithItem:_v
                                                            attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.container attribute:NSLayoutAttributeTop multiplier:1.0 constant:20.0];
    NSLayoutConstraint *_c2 = [NSLayoutConstraint constraintWithItem:_v
                                                           attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.container attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-20.0];
    NSLayoutConstraint *_c3 = [NSLayoutConstraint constraintWithItem:_v
                                                           attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.container attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20.0];
    NSLayoutConstraint *_c4 = [NSLayoutConstraint constraintWithItem:_v
                                                           attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.container attribute:NSLayoutAttributeRight multiplier:1.0 constant:-20.0];
    [self.container addConstraints:@[_c1,_c2,_c3,_c4]];

    return 0;
}
viggio24
  • 12,316
  • 5
  • 41
  • 34
-8

I unchecked auto layout on the view controller the crash is not occurring now