You can save yourself a lot of typing, as well as making code much more readable, by using "modern" constraint syntax.
For example, if we've created a label and stack view, and added them to the cell's content view:
// let's use the default margins
UILayoutGuide *g = self.contentView.layoutMarginsGuide;
[NSLayoutConstraint activateConstraints:@[
// constrain label Top/Leading/Trailing to content margins guide
[label.topAnchor constraintEqualToAnchor:g.topAnchor constant:0.0],
[label.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[label.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
// no Height, because we'll use the label's intrinsic size
// constrain stack view Top to label bottom plus 8-points
[stack.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8.0],
// Leading/Trailing/Bottom to content margins guide
[stack.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[stack.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
[stack.bottomAnchor constraintEqualToAnchor:g.bottomAnchor constant:0.0],
// no Height, because we'll use the arranged subviews heights
]];
So, we can write a cell class like this:
StackTableViewCell.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface StackTableViewCell : UITableViewCell
@end
NS_ASSUME_NONNULL_END
StackTableViewCell.m
#import "StackTableViewCell.h"
@interface StackTableViewCell()
{
UIStackView *stack;
UILabel *label;
}
@end
@implementation StackTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)init
{
self = [super init];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
// create a label
label = [UILabel new];
[label setBackgroundColor:UIColor.greenColor];
[label setNumberOfLines:0];
// create a vertical stack view
stack = [UIStackView new];
[stack setAxis:UILayoutConstraintAxisVertical];
[stack setSpacing:5.0];
// add label and stack view to content view
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
[stack setTranslatesAutoresizingMaskIntoConstraints:NO];
[[self contentView] addSubview:label];
[[self contentView] addSubview:stack];
// let's use the default margins
UILayoutGuide *g = self.contentView.layoutMarginsGuide;
[NSLayoutConstraint activateConstraints:@[
// constrain label Top/Leading/Trailing to content margins guide
[label.topAnchor constraintEqualToAnchor:g.topAnchor constant:0.0],
[label.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[label.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
// no Height, because we'll use the label's intrinsic size
// constrain stack view Top to label bottom plus 8-points
[stack.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8.0],
// Leading/Trailing/Bottom to content margins guide
[stack.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:0.0],
[stack.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:0.0],
[stack.bottomAnchor constraintEqualToAnchor:g.bottomAnchor constant:0.0],
// no Height, because we'll use the arranged subviews heights
]];
// let's give the stack view a border so we can see its frame
stack.layer.borderColor = UIColor.redColor.CGColor;
stack.layer.borderWidth = 1.0;
}
@end
along with a basic controller...
StackTableViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface StackTableViewController : UITableViewController
@end
NS_ASSUME_NONNULL_END
StackTableViewController.m
#import "StackTableViewController.h"
#import "StackTableViewCell.h"
@interface StackTableViewController ()
@end
@implementation StackTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:StackTableViewCell.class forCellReuseIdentifier:@"c"];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 30;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
StackTableViewCell *cell = (StackTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"c" forIndexPath:indexPath];
// Configure the cell...
return cell;
}
@end
At this point, because we're not giving the cells any data - and labels and stack view have no intrinsic height when empty - it will look like this:

So, let's add a fillData
method:
StackTableViewCell.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface StackTableViewCell : UITableViewCell
// add this line
- (void)fillData:(NSInteger)n;
@end
NS_ASSUME_NONNULL_END
StackTableViewCell.m
// add this method
- (void)fillData:(NSInteger)n {
label.text = [NSString stringWithFormat:@"Cell %ld", (long)n];
// cells are reused, so first clear any existing labels in the stack view
// this is inefficient, but we're just demonstrating the cell sizing
for (UIView *v in stack.arrangedSubviews) {
[v removeFromSuperview];
}
// now add n number of labels
for (int i = 0; i < n; i++) {
UILabel *v = [UILabel new];
v.text = [NSString stringWithFormat:@"Stack Label %ld", (long)i];
v.backgroundColor = UIColor.yellowColor;
[stack addArrangedSubview:v];
}
}
and modify the controller to set some cell data:
StackTableViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
StackTableViewCell *cell = (StackTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"c" forIndexPath:indexPath];
// Configure the cell...
[cell fillData:(indexPath.row % 4) + 1];
return cell;
}
and now the output looks like this:
