I have a UITableView with 3 sections. Each of them have a footer that I've added using viewForFooterInSection. The problem I'm having is that when I scroll the tableview down, the footer sticks to the bottom of the screen, and doesn't scroll like the rest of the cells. Does anyone know how to make it so the footer almost acts like a cell, and scrolls along with the rest of the table? Thanks!
I actually figured it out. Probably isn't the smartest way to do it, but I changed my UITableView style to grouped, and that fixed it. I had to tweak the TableView a bit so the cells would look the same as they did non-grouped (clear background, no separators), but it worked fine. The footer no longer sticks to the bottom of the TableView.
Footers are supposed to stick. The trick is to add an extra cell to each section and render the footer there. If you need more help, add a comment, but it should be pretty straightforward.
EDITS:
Q: Alright. I'm using Core Data and NSFetchedResultsController, to populate the TableView. Would that make it more tricky to accomplish this?
A: Not at all. Override
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
To add an extra cell in each section, and in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
test if the indexPath.row > than your fetchedResultsController's rows for that section. If true, add in your cell that shows the footer information.
One way around this is if you set the footer as one of the cells as the last cell in the scroll view (could be done but setting it as the last item in the array that you set the uitable from)
Adding an extra cell, making it invisible, and rendering your view there is not an advisable way of adding a footer. Doing it properly is pretty straight-forward:
- (UIView*)tableView:(UITableView*)tableView viewForFooterInSection:(NSInteger)section {
NSString* sectionFooter = [self tableView:tableView titleForFooterInSection:section];
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, yourWidth, yourHeight)]; //create a view- the width should usually be the width of the screen
UILabel* label = [[UILabel alloc] initWithFrame:someFrame];
label.backgroundColor = [UIColor blueColor];
label.textColor = [UIColor whiteColor];
label.text = sectionFooter;
[view addSubview:label];
return view;
}
You will also have to implement tableView: titleForFooterInSection: if you want to add text like I have here.
I did had similar problem when I found out that my code did not work with Landscape mode, and only worked in Portrait mode. In my old code, when in Landscape the tableview can not scroll lower than visible view and it bounced back to top row when scroll (not letting me see the lower rows). All I have to change is to make sure that the height set to 44 as default cell height. So my footer is basically another cell with clearColor. Note my app uses 'AutoLayout'
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
CGRect screenBounds = [UIScreen mainScreen].bounds;
CGFloat width = screenBounds.size.width;
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, 44)];
view.backgroundColor = [UIColor clearColor];
UILabel *label = [[UILabel alloc] initWithFrame:view.frame];
label.text = #"Your Text";
[view addSubview:label];
return view;
}
Related
I would like to get the cell frame at tableView:cellForRowAtIndexPath: in order to position and size the views that I want to add to UITableViewCell's contentView. But there self.frame is always (0, 0, 320, 44).
I know you can get the right frame in layoutSubviews (thanks to this answer), but if I add the subviews there it would be done every time the cell is reused, not only once like in the "official" example at Programmatically Adding Subviews to a Cell’s Content View.
In that example they add some views using hardcoded frames like:
mainLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 220.0, 15.0)];
I guess that example is outdated, since they should use constraints, or at least calculate the size of subviews using the actual cell frame (which may be impossible to get at that point indeed).
Note: this reminds me of the view holder design pattern used in Android.
This should return the bounding frame.
CGRect cellFrame = cell.bounds;
Then you can use it like
cellFrame.size.width;
cellFrame.size.height;
cellFrame.origin.x;
cellFrame.origin.y;
etc... Though origin.x and origin.y should be 0 each.
Maybe you should calculate cell height in a UITableViewCell subclass and use
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
in your delegate to find the correct height.
I use two solutions:
1) When I set the frames explicitly
I fake the frame:
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
// Use whatever height you like for your cell
// Should be the same value you return in tableView:heightForRowAtIndexPath:
CGFloat cellHeight = XXX;
CGRect frame = CGRectMake(0, 0, SCREEN_WIDTH, cellHeight);
2) When I use autolayout
I just add my views and constraints to self.contentView.
[someView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.contentView addSubview:someView];
[self.contentView addConstraints:someConstraints];
But check this answer just in case.
Within my app, I bring up a UITableView when prompting the user to select an item. Based on whether the item is in stock, out of stock, on order, etc., I want to display an image on the right-hand side of the UITableViewCell.
I have tried using the accessory view button, but it isn't showing up on the UITableViewCell. I have tried changing the UITableViewCellStyle to UITableViewCellStyleValue2 (the style with the detail closure button), and have also tried setting the accessory type of the cell directly.
static NSString *CellIdentifier = #"MyCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
//cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier];
}
[cell.textLabel setText:#"Testing"];
[cell.detailTextLabel setText:#""];
//cell.accessoryType = UITableViewCellAccessoryNone;
//cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
UIImageView *imageView = [[UIImageView alloc] initWithImage:[myImageFilePath stretchableImageWithLeftCapWidth:0 topCapHeight:1.0]];
[imageView setContentMode: UIViewContentModeScaleAspectFit];
[imageView setFrame:CGRectMake(0, 5, cell.frame.size.height - 10, cell.frame.size.height - 5)];
cell.accessoryView = imageView;
return cell;
When the app runs, the UITableViewCells appear with the "Testing" text, but there is no image on the right-hand side of the cells.
Is there another method that needs to be overridden in order to implement a custom UIImage for the detail diclosure button?
The "button" is more of a status indicator, because I don't want the tap of the button to do anything different than tapping anywhere else on the UITableViewCell does (just select the row).
Some additional info:
If I remove
[imageView setFrame:CGRectMake(0, 5, cell.frame.size.height - 10, cell.frame.size.height - 5)];
then the image shows up in the UITableViewCell, but it is too big and does not fit on a single row, so it overlaps with other rows' accessory views.
I was using the setFrame so that it would resize the image to fit with the UIViewContentModeScaleAspectFit so that it would fit on the row, but it seems to prevent the image from appearing at all.
Note that in this example cell.frame.size.height = 44.
If I change the frame to:
[imageView setFrame:CGRectMake(0.0, 0.0, 120, 44)];
then the image appears on the row.
However, since it is 120 pixels wide, it is cutting off the text with the trailing ... earlier that needed. The image could fit about 3 times before the trailing ..., with the width being 120 and height being 44.
How can I get the trailing ... to get closer to the image?
If I reduce the 120 to a lower value, the image moves to the right and eventually runs off the cell.
The width of the UITableView was 804. Updating it to 768 allowed me to use the original code that was posted.
Your question is very similar to this one here:
Using a custom image for a UITableViewCell's accessoryView and having it respond to UITableViewDelegate
You can ignore the "have it respond" bit unless you need to. But, as you can see there, the rect is square for the accessoryView property, so an image that is 120x44 is probably not going to fit correctly, ever.
I would rethink your approach to this problem, and create a custom UITableViewCell subclass that has the necessary items you require (sounds like a UILabel and a UIImageView). This way, you can easily control how large the label is versus how large the image is, how the label will wrap lines (if it wraps at all or maybe truncates) and so on.
Another way is just subclass the UITableviewCell and set frame's in "layoutSubviews" and also make sure to pass correct row height for each cell.
By subclassing you will get full flexibility.
hope this helps
I guess there's an issue with your CGRect X and Y, in this case here 0 and 5. Maybe trying to set them dinamically can fix this. Let me know
I have a UITableView which I am able to add a header view to fairly easily. Many apps (like Facebook, for viewing events) have a headerView that when you pull down, the header view stays put but the rest of the table (the UITableViewCell's) are bouncing. When scrolling up the header disappears. How can I achieve this functionality?
Right now when I pull down the UITableView, even the headerView bounces as well
You can achieve this effect quite easily by adding a subview to the header view and adjusting its frame or transform when the table view is scrolled beyond the top, i.e. the y component of its contentOffset becomes negative.
Example (in a UITableViewController subclass):
- (void)viewDidLoad
{
[super viewDidLoad];
CGFloat headerHeight = 64.0f;
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, headerHeight)];
UIView *headerContentView = [[UIView alloc] initWithFrame:headerView.bounds];
headerContentView.backgroundColor = [UIColor greenColor];
headerContentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[headerView addSubview:headerContentView];
self.tableView.tableHeaderView = headerView;
}
//Note: UITableView is a subclass of UIScrollView, so we
// can use UIScrollViewDelegate methods.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y;
UIView *headerContentView = self.tableView.tableHeaderView.subviews[0];
headerContentView.transform = CGAffineTransformMakeTranslation(0, MIN(offsetY, 0));
}
(to keep it simple, I've just used the first subview of the actual header view in scrollViewDidScroll:, you may want to use a property for that instead.)
Your UITableView is most likely working properly. Section headers are sticky by default in Plain style tables. Meaning as you scroll down the header stays at the top of the UITableView's frame until the next section header pushes it out of the way. The opposite occurs when you scroll up. Conversely you get the sticky behavior on section footers at the bottom of the UITableView's frame.
EDIT Misunderstood the original question:
I would suggest using a section header rather than the table view header to get the sticky behavior you're looking for.
Include a section in your data with no rows and put your table header's view in that section header view.
you can use this line in view did load: (swift 5.6)
tableView.bounces = false
There is 2 ways you can set the table header:
Using the .tableHeaderView property directly (this header scrolls with the table)
Overriding the - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section function (this header stays static with the section)
By the sounds of it you should use the 2nd method instead of using the .tableHeaderView property
I have a UITableView with a transparent backgroundColor, and the cells inside are initialized with the following code
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, CGRectGetWidth(self.contentView.frame), 180)];
self.label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.label.text = #"test";
[self.contentView addSubview:self.label];
}
return self;
}
Each row has a height of 200, and the label doesn't fill up the entire cell. There will be transparent portions in between each cell. When I try to scroll the table by touching those transparent portions, the touches are completely ignored. I am aware that as of iOS5, touches on views will be ignored. What can I do to solve this?
Things I've tried that don't work:
Setting a transparent or hidden or alpha = 0 UIView to act as a fake background of the tableView
Same deal, touches are ignored.
Creating a subclassed UIView on top of the tableView, and the subclassed UIView uses tableView as the nextResponder
Apparently UITableView doesn't use touchesBegan/cancelled/ended/moved, so this doesn't work. I don't think it's wise to implement my methods to scroll the UITableView either.
Setting the backgroundColor of the tableView to [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1]
I don't want to do this, it's still visible.
In short, what can I do to scroll the table even when I start the scrolling from the transparent section?
First you create a label with no text(or a view) such that it completely fills the cell. Then create a new label with your text and add this as a subview of the first view.
I have a custom view for displaying application status messages that I slide over my table view as needed. This worked fine until I started customizing my table view cells.
When I manually assign a row height to my table view cells in initWithSTyle...
self.TableView.rowHeight = 64.0;
...the custom view doesn't show up anymore.
I replaced the code above with a delegate method...
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 64.0;
}
...and now the custom view works as before.
Set rowHeight in the view controller.
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.rowHeight = 64.0f;
}
I think initWithStyle is too early to set the height. Eitherway, since your height is constant, it's still more efficient to set the height once (in viewDidLoad) than set it every time for each cell created (in initWithStyle or heightForRowAtIndexPath).
Did you create a custom cell class inheriting UITableVIewCell , if yes , then the height of customcell is different than the height of default cell.So you need to change the height of row to match your customcell;s in order for it to desplay correctly,Else cellswill overlap.
To Dynamically set height accoring to your customcell:-
YourCustomCell *cell = (YourCustomCell *)[tableView cellForRowAtIndexPath:indexPath];
return [cell.frame.size.height];
maintableView = [[UITableView alloc] init];
maintableView.frame = CGRectMake(0, 0, 320, 480);
maintableView.rowHeight = 100.0;
maintableView.delegate=self;
maintableView.dataSource=self;
It is working in my Xcode. Try like it. I think it will work.