zic10
zic10

Reputation: 2330

iOS 7.1 UITableView layoutSubviews issue

I'm working on a project that must support both iOS 8 and iOS 7.1. Right now I'm running into a problem that only appears on iOS 7.1 but works properly on iOS 8. I have a ViewController that contains a tableview and a custom view in the tableHeaderView. I'll post the code as follows. All constraints are added programatically.

//View Controller.

-(void)viewDidLoad
{
  [super viewDidLoad];

  self.commentsArray = [NSMutableArray new];

  [self.commentsArray addObject:@"TEST"];
  [self.commentsArray addObject:@"TEST"];
  [self.commentsArray addObject:@"TEST"];
  [self.commentsArray addObject:@"TEST"];
  [self.commentsArray addObject:@"TEST"];
  [self.commentsArray addObject:@"TEST"];

  [self.view addSubview:self.masterTableView];

  self.masterTableView.tableHeaderView = self.detailsView;

  [self.view setNeedsUpdateConstraints];


}

- (void)viewWillAppear:(BOOL)animated
{
  [super viewWillAppear:animated];
  [self.view layoutIfNeeded];
}


//Table view getter
- (UITableView *)masterTableView
{
  if(!_masterTableView)
  {
    _masterTableView = [UITableView new];
    _masterTableView.translatesAutoresizingMaskIntoConstraints = NO;

    _masterTableView.backgroundColor                = [UIColor greyColor];

    _masterTableView.delegate                       = self;
    _masterTableView.dataSource                     = self;

    _masterTableView.separatorStyle                 = UITableViewCellSeparatorStyleNone;
    _masterTableView.showsVerticalScrollIndicator   = NO;
    _masterTableView.separatorInset                 = UIEdgeInsetsZero;

  }

  return _masterTableView;
}

-(void)updateViewConstraints
{
 NSDictionary *views = @{
      @"table"        : self.masterTableView,
      @"details"      : self.detailsView,
 };


  NSDictionary *metrics = @{
                            @"width"  : @([UIScreen mainScreen].applicationFrame.size.width)
                            };
    //Table view constraints
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[table]-0-|" options:0 metrics:0 views:views]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[table]-0-|" options:0 metrics:0 views:views]];

  //Details View
  [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[details(width)]-0-|" options:0 metrics:metrics views:views]];
  [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[details]-0-|" options:0 metrics:metrics views:views]];

}


//Details View Getter
- (DetailsView *)detailsView
{
  if(!_detailsView)
  {

   _detailsView = [[DetailsView alloc]initWithFrame:CGRectMake(0, 0, 0, 100)];
    _detailsView.backgroundColor = [UIColor orangeColor];

  return _detailsView;
}

Now the details view contains some basic subviews which all derive from a UIView and the details view itself derives from a more general super class. I'll post the code as follows.

//Parent View 
@interface ParentView (): UIView

- (instancetype)init
{
  self = [super init];

  if (self)
  {
    [self setupViews];
  }

  return self;
}


- (void)setupViews
{
  [self addSubview:self.publishedTimeView];
}

- (void)updateConstraints
{
  NSDictionary *views = @{
                          @"time"         : self.publishedTimeView,
                          };


  // Header with published video time
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[time]-10-|" options:0 metrics:0 views:views]];
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[time(11)]" options:0 metrics:0 views:views]];

  [super updateConstraints];
}

//Getter for the timePublished view added to the detail view. Will not post all its related code for //the sake of brevity.
- (TimePublishedDetailView *)publishedTimeView
{
  if(!_publishedTimeView)
  {
    _publishedTimeView = [TimePublishedDetailView new];
    _publishedTimeView.translatesAutoresizingMaskIntoConstraints = NO;
  }

  return _publishedTimeView;
}

//Child View (or the detailsView) of the view controller.

@interface DetailsView : ParentView


@implementation RecordingDetailsView

- (void)updateConstraints
{
  NSDictionary *views = @{
                          @"time"         : self.publishedTimeView,
                          };

   //Vertical alignment of all views

  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-20-[time]" options:0 metrics:nil views:views]];

  [super updateConstraints];

}


- (void)setModel:(DetailViewModel *)model
{
  self.publishedTimeView.date = model.dateTime;

  [self setNeedsUpdateConstraints];
  [self layoutSubviews];
}

Now on iOS 8 this looks like this: enter image description here

However on iOS 7.1 this will crash with this error message:

"Exception: Auto layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super"

I've googled this and played around with the code in my layout calls to see if I can remedy the problem but so far have been unsuccessful. If anyone could post some tips or advice on how to fix this I would really appreciate it.

Upvotes: 1

Views: 352

Answers (1)

gfpacheco
gfpacheco

Reputation: 3215

I know this question is old, but I ran into a similar problem recently and after a lot of Googling, trying and failing I made it work with the help of this answer and a few changes.

Just add this category to your project and call [UITableView fixLayoutSubviewsMethod]; only once (I recommend inside AppDelegate).

#import <objc/runtime.h>
#import <objc/message.h>

@implementation UITableView (FixUITableViewAutolayoutIHope)

+ (void)fixLayoutSubviewsMethod
{
    Method existing = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method new = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existing, new);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

Upvotes: 2

Related Questions