Reputation: 4441
I'm loading a custom UIView's XIB file as a header view of a uitableview inside a view controller.
The file owner for the xib file is the viewcontroller. I have both the viewcontroller's & the uiview's interface declared inside the uiviewcontroller.
ViewController.h
@class ZeroStateView;
@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, weak) IBOutlet CustomUITableView *tableView;
@property (nonatomic,strong) NSMutableArray *dataArray;
@property (weak, nonatomic) IBOutlet ZeroStateView *zeroStateView;
@end
@interface ZeroStateView : UIView
@property (weak, nonatomic) IBOutlet AutoLayoutLabel *titleLabel;
@property (weak, nonatomic) IBOutlet UIImageView *titleIcon;
- (void)updateView;
@end
ViewController.m
- (void)prepareHeaderViewForZeroState{
ZeroStateView *sizingView = [ZeroStateView new];
[[[NSBundle mainBundle] loadNibNamed:@"ZeroStateView" owner:self options:nil] objectAtIndex:0];
sizingView = self.zeroStateView;
[sizingView updateView];
self.tableView.tableHeaderView = sizingView;
UIView *headerView = self.tableView.tableHeaderView;
CGFloat height = [headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = headerView.frame;
headerFrame.size.height = height;
headerView.frame = headerFrame;
self.tableView.tableHeaderView = headerView;
}
@end
@implementation ZeroStateView
-(void)updateView{
self.titleIcon.alpha = 0.5;
UIFontDescriptor *titleFontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline];
self.titleLabel.text = @"This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. ";
}
The AutolayoutLabel class the following method overridden:
- (void)setBounds:(CGRect)bounds {
[super setBounds:bounds];
// For multiline label, preferredMaxLayoutWidth always matches the frame width
if (self.numberOfLines == 0 && bounds.size.width != self.preferredMaxLayoutWidth) {
self.preferredMaxLayoutWidth = self.bounds.size.width;
[self setNeedsUpdateConstraints];
}
}
The height calculated by the systemLayoutSizeFittingSize:UILayoutFittingCompressedSize returns 0. As a result I get the following view as the table header view:
When I added the actual height as below, the uilabel overflows. I'm expecting the uiview to grow as the label height grows.
headerFrame.size.height = self.sizingView.frame.size.height;
Here is the screen capture of that UIViews constraints:
What do I miss here? Can someone point me out?
Update I created a sample project for you guys to check on whats exactly issue is.
Upvotes: 5
Views: 937
Reputation: 3733
As you call prepareHeaderViewForZeroState
method from viewwillappear
. At that point you layout is not calculate. so force layout to calculate before calling systemLayoutSizeFittingSize
method to calculate height of cell. Here are the code that you need to write before calling systemLayoutSizeFittingSize
.
UIView *header = self.tableView.tableHeaderView;
[header setNeedsLayout];
[header layoutIfNeeded];
Edit :
You just left 1 constraints in ZeroStateView.xib
. that is Bottom Space to : Superview
. kindly refer screenshot.
Output :
Here you have Updated code
Hope this help you.
Upvotes: 1
Reputation: 12334
I reimplemented what you had so far in a different way. To start, I removed the UIView
in ZeroStateView.xib
that the UIImageView
and UILabel
were embedded in. The base of the xib is already a UIView
, so it is unnecessary to add another UIView
to it.
Next I changed the constraints around. I don't remember exactly what constraints I changed, so I will just list them out here:
UIImageView
UILabel
= 31UILabel
UIImageView
= 31Onto the code. In ViewController.h
, the IBOutlet
wasn't doing anything as far as I could tell, so I changed that property to read @property (strong, nonatomic) ZeroStateView *zeroStateView;
Now the important changes: ViewController.m
. There are two UITableViewDelegate
methods that will replace prepareHeaderViewForZeroState
. In viewDidLoad
, initialize the zeroStateView
and set the table view's delegate to self
.
- (void)viewDidLoad
{
//...
// Load the view
self.zeroStateView = [[[NSBundle mainBundle] loadNibNamed:@"ZeroStateView" owner:self options:nil] firstObject];
[self.zeroStateView updateView];
self.zeroStateView.backgroundColor = [UIColor darkGrayColor];
// Set self for table view delegate for -heightForHeaderInSection: and viewForHeaderInSection:
self.dataTable.delegate = self;
}
Now that we are the table view's delegate, we get two method calls that will allow us to customize the header view and set its height appropriately.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
// This will set the header view to the zero state view we made in viewDidLoad
return self.zeroStateView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
// sizeToFit describes the size that the label can fit itself into.
// So we are saying that the label can use the width of the view and infinite height.
CGSize sizeToFit = CGSizeMake(self.view.frame.size.width, MAXFLOAT);
// Then we ask the label to tell us how it can fit in that size.
// The label will respond with the width of the view and however much height it needs
// to display its text. This is the magic of how it grows vertically.
CGSize size = [self.zeroStateView.titleLabel sizeThatFits:sizeToFit];
// Add the height the label needs to the overall zero state view. This should be changed
// to the height of the UIImage + the height we just got + the whitespace above and below
// each of these views. You can handle that part.
return self.zeroStateView.frame.size.height + size.height;
}
I uploaded my changes to Dropbox here.
Upvotes: 2
Reputation: 5640
There are a few things here that are confusing to me that might be complicating things.
Once you resolve those issues, let me know if you have better luck.
Upvotes: 0
Reputation: 729
I'm not sure but you could check if you have a leading,trailing,top and bottom constraints for both the UIImage and the label with reference to the superview.
Edit: Add the width constraint before getting the systemLayoutSize
NSLayoutConstraint *tempWidthConstraint =
[NSLayoutConstraint constraintWithItem:self.contentView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:CGRectGetWidth(window.frame)];
widthConstraint.constant = tempWidthConstraint.constant;
[self.contentView addConstraint:tempWidthConstraint];
CGSize fittingSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
CGFloat height = fittingSize.height +1;
[self.contentView removeConstraint:tempWidthConstraint];
Upvotes: 0
Reputation: 4163
Just changing the size of the frame of the tableHeaderView, doesn't change it's size. You have to set it again to the tableView to force a reload.
You have to call this again self.tableView.tableHeaderView = headerView;
after setting the new frame size.
Upvotes: 1