UITableViewHeaderFooterView and Interface Builder: how to adjust width for different devices? - ios

I am implementing a custom Header view for my UITableView. I created a HeaderView class that extends UITableViewHeaderFooterView and a Nib file for this view.
In the view controller, I registered it:
[self.tableView registerNib:[UINib nibWithNibName:#"HeaderView" bundle:[NSBundle mainBundle]] forHeaderFooterViewReuseIdentifier:#"HeaderView"];
And then set as a header:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
HeaderView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:#"HeaderView"];
return view;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 26.0f;
}
The problem is that the width of the view is always the same as in the nib file. Since now there are multiple screen widths, I cannot simply set the view width to 320 (or the width of the table view) in the nib file.
How exactly am I supposed to set up the header view so it automatically fits the table width?
Update: here's a comparison when I change the view simulated metrics from Inferred to a fixed one (iPhone 3.5" in this case):
img http://cl.ly/image/451n2N303J3S/Image%202015-04-19%20at%206.39.58%20PM.png

Had the same problem ... the only solution I found was by setting all the frames of the subviews in my custom UITableViewHeaderFooterView to the according state.
Dirty example:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
NSString *headerIdentifier = #"SectionHeader";
TableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerIdentifier];
if (headerView == nil) {
headerView = [[TableViewHeaderFooterView alloc] initWithReuseIdentifier:headerIdentifier];
}
.....
[headerView setWidthForSubviews:tableView.frame.size.width]; // set the right width of the right subviews
....
return headerView;
}
in TableViewHeaderFooterView
- (void)setWidthForSubviews:(CGFloat)width {
for (UIView *view in #[[self viewWithTag:1],[self viewWithTag:2],[self viewWithTag:3], ...]) {
view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, width, view.frame.size.height);
}
}
If someone finds another solution I'll be grateful but it seems that UITableViewHeaderFooterView may have problems with autolayout, especially if you load it from a nib.

I would look at a couple of things.
If you using springs and struts, then trying set the autoResizingMask
[maskingBackground setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
If using autolayout then pin to the tableWidth and use a constant height constraint.
If that does not meet your needs I would look at using this in your header view class
-(void)willMoveToSuperview:(UIView*)superview
And setting your frame in there using the superview.frame.size to guide you.

Related

UIView's autolayout size reported from wrong size class the first time

I have a UITableViewController with some custom cells and an UIView within each cell. I have implemented the controller's cellForRowAtIndexPath to configure the UIView in each cell. For this purpose I need to know the width of the UIView on screen. Since I am using autolayout and size classes to automatically change the size of the UIView based on device orientation, I have implemented an additional method of getting the width runtime.
The problem is that when the table view is presented the first time, my code reports width for UIVIew from a compact width size class even when I am using the device in the landscape orientation. The system renders all the views as should, but my code to get the width is not working. Scrolling new cells visible or an orientation change will remedy the situation immediately.
My code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ...
CustomCell* cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = [nib firstObject];
} else {
// Clear custom content
NSArray *viewsToRemove = [cell.histogramView subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
}
[cell setNeedsLayout];
[cell layoutIfNeeded];
int width = ((CustomView*)cell.customView).getWidth;
NSLog(#"width = %d", width);
// ...
}
And then:
#implementation CustomView
- (int)getWidth {
[self setNeedsLayout];
[self layoutIfNeeded];
int width = self.frame.size.width;
return width;
}
#end
Edited to add:
The problem seems to be that at when cellForRowAtIndexPath is called the first time tableview appears, autolayout has not occurred for the cell. Forcing it with [cell setNeedsLayout] and [cell layoutIfNeeded] right after creating the cell does not do the trick either.
It seems my problem root cause is a potential duplicate of How to know the width of an UITableViewCell when using auto layout? So the problem has to do with fact that when my CustomCell is loaded from a nib, it will have the default frame. Special tricks should be done to force autolayout. However, the accepted answer does not work for cells that are initially out of visible area. Any takers on this?
I would suggest trying to use constraints and ratios instead of actual width of the frame, since, as stated by Marcus Adams, the frame will have the value you are actually looking for only after viewDidAppear.
For example if your cells contain a UILabel that is 1/3 the width of the cell, and a UIImageView that fills the space left, you can set constraints between them and the parent view.
Can your logic be changed like this?
Another suggestion would be to call reloadData on your table view after viewDidAppear, but that is definitely not a nice option UX-wise.
I found out the correct solution to this:
Create a custom cell like in the original question.
Set cell.contentMode = UIViewContentModeRedraw in tableview: cellForRowAtIndexPath:
Implement layoutSubviews for the custom cell like so:
- (void)layoutSubviews {
[super layoutSubviews];
NSLog(#"cell width == %f", self.bounds.size.width);
}
This way you will have access to the size of the cell as it will appear on screen.

Remove bottom line in Custom Header's tableview

I have a UITableView with Custom Headers but I can't remove the bottom white line inside. The Separator property is set to none, in fact, the cells in the section don't have the line.
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *CellIdentifier = #"DashboardSectionHeader";
UITableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
...
return headerView;
}
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 60;
}
Have you checked DashboardSectionHeader cell?If there is a line at the bottom.Try changing the backgroundcolor of this cell and see.If the line is appearing in the section view ,then there should be say a one pixel space at the bottom of this cell through which the background will be visible.
I had the same issue in the same circumstances as you, and for me the problem have been solved by adding:
cell.contentView.clipsToBounds = NO;
in tableView: viewForHeaderInSection: method. Or you can just uncheck Clip Subview property in Attribute Inspector for your cells Content View (in Storyboard or xib file). FYI, I kept this property checked for my TableViewCell.

A better way to implement margins in a UITableView?

Apple has deprecated margins in table views, since iOS7, but designers still seem to want margins. What’s the best way t implement margins in iOS 7 and above? The old “group” table view style that included margins in iOS 6 no longer indents the left and right edges.
I have aUITableView subclass, in which i’m using the following method to programmatically add margins to the edge of all subviews, except thebackgroundView (such as cells, section header etc):
Code:
- (void)layoutSubviews {
[super layoutSubviews];
for (UIView *subview in self.subviews) {
if (subview != self.backgroundView && subview.frame.origin.x == 0.0 && subview.frame.size.width == self.frame.size.width) {
subview.frame = CGRectInset(subview.frame, 10.0, 0);
}
}
}
During runtime, it all seems to work as desired. The cells resize according to the margin offset and the subviews of those cells adjust correctly.
However, in the storyboard, usingIBDesignable, the cells adjust correctly but the cells’ subviews do not seem to resize within the cell’s calculated size
Is there a better way to implement margins in aUITableView?
Thanks c:
UIEdgeInsets is used to implement margins around the tableview that looks like grouped tableview. You may try below solutions and check it.
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if ([tableView respondsToSelector:#selector(setSeparatorInset:)]) {
[tableView setSeparatorInset:UIEdgeInsetsZero];//iOS7
}
if ([tableView respondsToSelector:#selector(setLayoutMargins:)]) {
[tableView setLayoutMargins:UIEdgeInsetsZero];
}
if ([cell respondsToSelector:#selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
}
//OR
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// Force your tableview margins (this may be a bad idea)
if ([self.tableView respondsToSelector:#selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsZero];
}
if ([self.tableView respondsToSelector:#selector(setLayoutMargins:)]) {
[self.tableView setLayoutMargins:UIEdgeInsetsZero];
}
}
For iOS 8 use
- (UIEdgeInsets)layoutMargins {
return UIEdgeInsetsZero;
}
Hope it helps you..
Apple switched to layout margins in iOS 8. The advantage of adding constraints to margins instead of edges, is that your margin is now adaptive, and will adjust itself, based on the screen's traits.
After I transitioned to constraining to margins (instead of edges), I really appreciated how it simplified layout, and eliminating having to adjust constraints myself. It does the work for me, and it looks pleasing.

UITableView header height will not change in storyboard

I have a Storyboard that is using a UITableViewController. I have added a UIView as the UITableView header and set the height to 200. But, for some reason, when I preview it, the header is huge! looks to be about 540 high (header is white):
Here are my settings:
It looks correct in the storyboard preview. What could be causing it to be so huge and prevent my height setting from working?
Apple figured it out. Here is what they said:
Because the frame of a view can not be customized per-size class, you
need to make changes to your header view's frame while editing the
[wAny hAny] size class.
I was in the [wCompact hRegular] mode, which apparently you cannot set frame sizes in.
Just solved this question, try this:
UIView *v = self.tableView.tableHeaderView;
CGRect fr = v.frame;
fr.size.height = [UIScreen mainScreen].bounds.size.height -100;
v.frame = fr;
[self.tableView updateConstraintsIfNeeded];
Can't set constraints of UItableView's tableHeaderView height by autolayout in Storyboard
For me in order to make it work, I needed to create an IBOutlet reference to the header view. Then I added in the viewDidLoad:
self.header.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 40);
self.tableView.sectionHeaderHeight = 0;
You have to take a seperate NibFile and resize the nib file as per your required size (Resize the height manually do not use attribute inspector.). Change Simulated metrics to freeform in attribute inspector.
Then You can customize the HeaderView .
static NSString *SectionHeaderViewIdentifier = #"SectionHeaderViewIdentifier";
#define HEADER_HEIGHT 200
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.sectionHeaderHeight = HEADER_HEIGHT;
// Set up default values.
self.tableView.sectionHeaderHeight = HEADER_HEIGHT;
UINib *sectionHeaderNib = [UINib nibWithNibName:#"SectionHeaderView" bundle:nil];
[self.tableView registerNib:sectionHeaderNib forHeaderFooterViewReuseIdentifier:SectionHeaderViewIdentifier];
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *sectionHeaderView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:SectionHeaderViewIdentifier];
//You can customize your header view
return sectionHeaderView;
}

Reusing UITableViewCell moves subview frame

I'm subclassing UITableViewCell. In the xib for my custom cell, I have an UIImageView that has a frame of (0, 0, 57, 57). It has autolayout constraints set to top, left, width, and height.
When the table view is first rendered, all the cells look fine. But when I scroll around, (and thus reusing the celles) the frame for the UIImageView shifts to (15, 0, 57, 57). None of the other subviews seem to be affected.
The UITableViewDataSource:
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Content* content = [[[ContentManager sharedContentManager] allContent] objectAtIndex:indexPath.row];
ContnetCell* cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier forIndexPath:indexPath];
[cell loadWithContent:content];
return cell;
}
The Cell:
{
[super prepareForReuse];
self.imageView.image = nil;
}
-(void)loadWithContent:(Content*)content
{
self.imageView.image = content.contentImage;
}
It's a custom cell right? If you use the same property names for your custom images or labels as those found in a default cell (i.e, imageView, textLabel or detailTextLabel), strange things can happen. Try renaming imageView to something else and see if that fixes it.

Resources