Using autoResizingMask with CGRectZero - ios

I am building a footer for a tableview's section. The height of the footer will be specified in heightForFooterInSection, so in viewForFooterInSection I would like to just add the subview and specify that the footer view should fill whatever footer height is specified (this footer size will be dynamic). So, I am using CGRectZero as the initial frame and telling the footer view to expand to fill its parent view.
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
UIView *footerView = [[UIView alloc] initWithFrame:CGRectZero];
footerView = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
footerView = [UIColor greenColor];
return footerView;
}
This works as expected - the footer of the table view is filled completely with the green view.
But now I want to add a UITextView to the footer. The text view should fill the same space, but leave a 5-point border:
{
UIView *footerView = [[UIView alloc] initWithFrame:CGRectZero];
footerView = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
footerView = [UIColor greenColor];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectInset(footerView.frame, 5, 5)];
textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
textView.backgroundColor = [UIColor redColor];
[footerView addSubview:textView];
return footerView;
}
Instead of filling the footer view (with a 5 point margin), the text view does not appear at all. It likely has a frame of CGRectZero (or maybe even -5 x -5?). If I set the inset to 0, 0, however, it expands as expected.
What is the explanation for this? And If I can't use an inset of CGRectZero for the initial frame, what am I expected to use when the frame of the footerView can not be known?

CGRectInset will create a rectangle based on the existing rectangle. It doesn't refer to the footer view ever again: only when it is calculating it this one time. In this case, since you are trying to inset a rectangle with a zero size, this applies from the docs:
Discussion.
The rectangle is standardized and then the inset parameters are
applied. If the resulting rectangle would have a negative height or
width, a null rectangle is returned.
Therefore, you are creating your label with a null rectangle.
I would create the footer with a "typical" size, then an appropriately sized label with the autoResizingMask that you want, and then set your footerView to zero if that is what you want it set to.

I would guess that the TextView creates the content in the background, so at the moment of initialization it's empty. I usualy end up using [string sizeWithFont:constrainedToSize:lineBreakMode:]
CGSize size = [aString sizeWithFont:[UIFont systemFontOfSize:12.0] constrainedToSize:CGSizeMake(320,500) lineBreakMode:NSLineBreakByWordWrapping];
CGRect frame = CGRectMake(0, 0, size.width, size.height);

Related

UIScrollView not working like most scroll views

I am trying to make my scroll view get to the bottom of my text without having to 'force scroll'. So when I scroll down, the scroll bar at the right stops before I reach the bottom portion of the remaining text. I have to then scroll again with force (so that the scroll bar at the right shrinks, revealing the remaining text). Hopefully someone has encountered this. I don't want to force scroll. I want it all displayed in a clean way.
UIScrollView *theScroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 568.0f)];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(17.0f, 150.0f, 286.0f, 568.0f)];
label.text = #" a very large portion of text ";
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont fontWithName:#"Chalkduster" size:16.0];
label.lineBreakMode = NSLineBreakByWordWrapping;
label.numberOfLines = 0;
[label sizeToFit];
[theScroll setShowsVerticalScrollIndicator:NO];
theScroll.contentSize = CGSizeMake(theScroll.contentSize.width, label.frame.size.height);
[theScroll addSubview:label];
[self.view addSubview:theScroll];
This is all in the viewDidLoad method.
Two things don't quite seem right.
You set the label's origin.y to 150. Did you mean to have the label start so far from the top of the scroll view's content area?
You set the scroll view's contentSize.height to be the same as the label's height. This conflicts with #1. The contentSize.height should be tall enough for all of the content.
Assuming #1 is correct, #2 should be set as:
theScroll.contentSize = CGSizeMake(theScroll.contentSize.width, CGRectGetMaxY(label.frame));
Remember, the bottom of the label will be its origin + its height, not just its height.

How to get the header view of a grouped table view?

I want to get all the views of a grouped table view to change the label color and to set the background color.
I found the answer, it's not possible to get the header view of a table view section. But you can implement the delegate tableView:viewForHeaderInSection: to recreate the header view and the label. The following code will give you the same header view and the exact label.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
if (sectionTitle == nil) {
return nil;
}
// Create label with section title
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 5.5f, 300.0f, 30.0f)];
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont boldSystemFontOfSize:16.5];
label.shadowColor = [UIColor whiteColor];
label.shadowOffset = CGSizeMake(0.0, 1.0);
label.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
label.text = sectionTitle;
// Create header view and add label as a subview
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 44.0f)];
view.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
[view addSubview:label];
return view;
}
That's great you figured out your solution.
A couple of suggestions:
Don't hardcode the CGRect for the width of your frame, but rather use self.view.size.width for the width (e.g. in case you're in landscape orientation or if Apple ever introduces an iPhone with a different screen size);
You probably want to use autoresizingMask for both the label and the view that holds the label, so that they'll resize as the screen orientation changes, or make sure you invoke [self.tableview reloadData] on orientation changes; and
This is obviously a single line label ... if that works for you great, otherwise you'd want to use sizeWithFont:constrainedToSize:lineBreakMode to determine the height, both for creating the label/view as well as responding to tableView:heightForHeaderInSection:.
You also need to add the textColor:
label.textColor = [UIColor colorWithRed:0.265 green:0.294 blue:0.367 alpha:1.000];

iOS UITableView unexpectedly adding margins

I have a UITableView, and currently it has a single cell in it. I have written a custom TableViewCell class which inherits from UITableViewCell in order to do some custom drawing. I have set the width of the table to the desired size, and am trying to set the width of the cell to the same size, so it will fill up the entire width of the table. The problem seems to be that I'm getting some margins on the left and right sides of the cell, and I don't know why.
Here's an example of the problem.
I made the TableView background black to be more clear. The TableView is the correct size. The background image is added to the cell, not the table, and it should be taking up the full width of the table.
I have tried making the TableView wider (as wide as the screen) to try to accommodate the size of the background image, but that doesn't quite do it. I would rather just figure out where these margins are coming from, and how I can get rid of them.
The TableView itself is initialized in Interface Builder. The style is set to Grouped, scrolling is disabled, and the view mode is set to Scale To Fill.
Here's the cell class' initWithStyle method
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// Initialization code
_primaryLabel = [[UILabel alloc] init];
_primaryLabel.textAlignment = UITextAlignmentLeft;
_primaryLabel.font = [UIFont systemFontOfSize:18];
_primaryLabel.backgroundColor = [UIColor clearColor];
_detailLabel = [[UILabel alloc] init];
_detailLabel.textAlignment = UITextAlignmentLeft;
_detailLabel.font = [UIFont systemFontOfSize:12];
_detailLabel.backgroundColor = [UIColor clearColor];
_icon = [[UIImageView alloc] init];
[self.contentView addSubview:_primaryLabel];
[self.contentView addSubview:_detailLabel];
[self.contentView addSubview:_icon];
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView* whiteDisclosureView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 15, 13)];
[whiteDisclosureView setImage:[UIImage imageNamed:#"white_disclosure.png"]];
self.accessoryView = whiteDisclosureView;
UIImageView * background = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 305, 61)];
[background setImage:[UIImage imageNamed:#"button_silver.png"]];
[self setBackgroundView:background];
self.backgroundColor = [UIColor clearColor];
self.frame = self.contentView.frame = CGRectMake(0, 0, 305, 61);
}
return self;
}
Is your tableView using "grouped" style? With grouped style, iOS normally adds left and right margin for the table cells.
It may be possible to remedy this by adjusting the frame of the tableView to slightly outside its superview. See here for example in previous question
You shouldn't explicitly set your cell's frame (size), but declare its style. (If you don't do that already) The cells are designed to automatically take up the whole space. (Horizontally)
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
If not when allocating your cell, how do you set the cell's frame?
EDIT: Instead of using hardcoded frame sizes, use self.frame. Additionally, remove the last statement where you set the frame.
Another alternative solution I used.
#jonkroll's solution does work but it does not fulfil my need. I have a header section in the table view which I want to keep the margin left and right as is, but want to remove them on the normal cell.
The solution I did is to implement a layoutSubViews method in a custom table view cell. Within this method, set the contentView's width equal to table cell's width.
-(void)layoutSubviews {
self.contentView.frame.size.width = self.frame.size.width;
}
This may be very late, but I think some people will run into the same problem as well. Hope this solution works for you guys : )

How to center UITableView Section Footer with Support for Orientation?

EDIT:
After messing with this for days the real questions I have are the following:
1. Does UITableView take up the entire view?
2. If so, how does it set the bounds of the cells to that it looks like it only takes up part of the view.
3. How do I get the bounds of the cells - or more accurately how do I know the bounds of the visible area that the cells are taking up. self.tableView.bounds.size.width does not help because it returns the width of the view.
Thanks.
Leaving the previous info below in case it helps make my question clearer.
Can this be possible?
I have read the apple docs and trolled the forums here and elsewhere and can't find and answer to this.
Does the footer in a UITableVIew actually take up the entire view no matter what you do? Does it not have a concept of the table width?
Example:
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
[footerView setBackgroundColor:[UIColor redColor]];
return footerView;
}
This code will create a red line from one edge to the other. No matter what boundaries you give it the line will take up the entire view. The problem with this is that if you want to center a label in that footer you don't have any way to know where center is if you are supporting orientation changes.
For instance in an iPad app I am trying to do the following:
if ([footerText length] > 0) {
UIView *customView = [[[UIView alloc]initWithFrame:CGRectMake(0.0, 0.0, 0, 0.0)] autorelease];
[customView setBackgroundColor:[UIColor grayColor]];
UILabel *footerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
footerLabel.numberOfLines = 2;
footerLabel.adjustsFontSizeToFitWidth = NO;
[footerLabel setTextAlignment:UITextAlignmentCenter];
[footerLabel setBackgroundColor:[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.5]];
[footerLabel setOpaque:NO];
[footerLabel setTextColor:[UIColor whiteColor]];
[footerLabel setShadowColor:[UIColor lightGrayColor]];
[footerLabel setFont:[UIFont boldSystemFontOfSize:14]];
[footerLabel setFrame:CGRectMake(customView.center.x/0.3, 0.0, 600, 40.0)];
[footerLabel setText:footerText];
[customView addSubview:footerLabel];
[footerLabel release];
NSLog(#"customView width = %f", customView.frame.size.width);
NSLog(#"tableview width = %f", self.tableView.frame.size.width);
NSLog(#"tableview center = %f", self.tableView.center.x);
return customView;
} else {
return nil;
}
The table's center in portrait should be 384 (it's in the detail view/right side) and 351.5 in landscape. But when I use setCenter or try to adjust the left edge based on that center it does not center up.
Final question: How does one center a custom view in a footer with support for orientation when the footer seems to have no concept of the table bounds? I must be missing something in the docs because this has to be a problem solved by someone else but I can't find it.
Thanks for your time.
To center something within the tableview, you need to wrap it in a container, and set the appropriate autoresize mask for both the embedded view and the container.
The container should be flexible width, and the embedded view should have both flexible side margins.
eg:
- (UIView *) tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
static UIView *footerView;
if (footerView != nil)
return footerView;
NSString *footerText = NSLocalizedString(#"Some Display Text Key", nil);
// set the container width to a known value so that we can center a label in it
// it will get resized by the tableview since we set autoresizeflags
float footerWidth = 150.0f;
float padding = 10.0f; // an arbitrary amount to center the label in the container
footerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, footerWidth, 44.0f)];
footerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
// create the label centered in the container, then set the appropriate autoresize mask
UILabel *footerLabel = [[[UILabel alloc] initWithFrame:CGRectMake(padding, 0, footerWidth - 2.0f * padding, 44.0f)] autorelease];
footerLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
footerLabel.textAlignment = UITextAlignmentCenter;
footerLabel.text = footerText;
[footerView addSubview:footerLabel];
return footerView;
}

Vertical space between UITableViewCells after setting the backgroundView on a cell

I am implementing UITableViewCells that have a custom background and expands when tapped.
I am setting a custom view as my UITableViewCells backgroundView: (in RowForIndexPath)
if (cell == nil) {
CGRect f = CGRectMake(0.0f, 0.0f, 300.0f, 50.0f);
cell = [[UITableViewCell alloc] initWithFrame:f reuseIdentifier:cellIdentifier];
UIView *back = [[UIView alloc] initWithFrame:CGRectMake(10.0f, 10.0f, 300.0f, 50.0f)];
back.layer.cornerRadius = 8.0f;
[back setBackgroundColor:[UIColor blackColor]];
This works fine, by setting the backgroundView instead of the contentView, my backgroundView scales to accommodate the new size of the cell after it expands (changing the height in heightForRowAtIndexPath after a tap).
My problem is now that I would like a few pixels vertical space between my cells. Using the above approach will make the rounded black cells be displayed "back to back".
Is there a way to add the vertical space between the cells or is there a completely different approach I can take to obtain the desired look of my cells?
Thanks in advance.
In order to display a space between cells and avoid the "back to back" issue that you are having you can add a UIView with the the following frame CGRectMake(0, 0, 320, 1) and the background set to a light gray for the standard cell separator or if you just want some padding you can make the background clear.
Personally I like to use interface builder but you can of course do this programmatically.
UIView *cellSeparator = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320 ,1)];
[cellSeparator setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleWidth];
[cellSeparator setContentMode:UIViewContentModeTopLeft];
[cellSeparator setBackgroundColor:[UIColor lightGrayColor]];
[cell addSubview:cellSeparator];
[cellSeparator release];
The reason I set the cellSeparator at the top of the cell is because the effect is really for the cells that fall in between the first and last rows. Also it is important to set the autoresizingMask and the contentMode to make sure the cell separator adjusts properly when you make size changes to the UITableViewCell. If you have any elements in the cell that start at x=0 y=0 of you will need to move them down the height of the cell separator plus perhaps some additional pixels for padding so that the separator doesn't run through any elements with in the cell.

Resources