Hi hope someone can help.
I currently have a tableview which has a set of sections, in my titleForHeaderInSection i am returning a string that includes a sum of values contained in the section cells to display in the section header. This is fine but when i update a cell value i want the titleForHeaderInSection to update and refresh my sum of values. At the moment the user needs to scroll the header out of sight then back in for it to refresh. I have been googling to see if i could find a solution seen a few examples that suggest including a label in the view for header but i need the sections to be dynamic so cant create labels for each section, i have also tried using the reloadsection but this doesent work properly either and the tableview reloaddata is to much of a performance hit to do each time a value changes in a tableview cell.
my current code for my titlerForHeaderInSection is
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
int averageScoreTotal, _total;
averageScoreTotal = 0;
_total = 0;
for (BlkCon_BlockToConstructionType *sPC in sectionInfo.objects)
{
_total = [sPC.compositionPc integerValue];
averageScoreTotal += _total;
}
return [NSString stringWithFormat: #"(Total Composition for Group %d)", averageScoreTotal];
}
Thanks in advance for any help
You can use UITableView's -reloadSections:... method with the correct section. That will reload the section header, too.
If you don't want to use that method, because your table view stops scrolling for a moment or one of the table view cells is first responder, you have to use a custom header view for the section containing a label.
1) Implement -tableView:heightForHeaderInSection: and -tableView:viewForHeaderInSection:
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return tableView.sectionHeaderHeight;
}
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
CGFloat height = [self tableView:tableView heightForHeaderInSection:section];
NSString *title = [self tableView:tableView titleForHeaderInSection:section];
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, height)];
containerView.backgroundColor = tableView.backgroundColor;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(19, 7, containerView.bounds.size.width - 38, 21)];
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont boldSystemFontOfSize:17];
label.shadowOffset = CGSizeMake(0, 1);
label.shadowColor = [UIColor whiteColor];
label.text = title;
label.textColor = [UIColor colorWithRed:0.265 green:0.294 blue:0.367 alpha:1];
[containerView addSubview:label];
return containerView;
}
2) Update the label directly by changing its text property. You'll have to create an iVar for the labels or better use an array to store them, so you can access them when you want to update the section header's text.
3) If you want to make the header flexible in height, set the numberOfLines property of the label to 0 so that it has indefinite lines and make sure the -tableView:heightForHeaderInSection: returns the correct height.
In order to update the section header's height use
[self.tableView beginUpdates];
[self.tableView endUpdates];
Good luck,
Fabian
Edit:
The code above assumes you're using ARC.
Related
I got a table view with two sections, no crazy code, just my delegate methods.
It works pretty fine, like i want it to work. It should just look like on this screenshot:
Now the problem is: Sometimes while scrolling or flicking the scoll view to the bounds, this happens (if you can't see it: There is 1 or 1/2 pixel in gray on the top of the second section header, what is not intended to be so):
So, is this a iOS 7.1 or 7.x bug? I'm not using a custom view for the header. Does anyone know how to fix this?
Feedback really is appreciated.
I had this same problem that I battled for a few weeks, and the way I solved it was to set the tableView's separatorStyle to UITableViewCellSeparatorStyleNone, and add a custom subview that is a line to the cell's contentView.
Then in your cellForRowAtIndexPath method, hide the line subview of the last cell in the section:
- (UIView *)lineView
{
// Your frame will vary.
UIView *colorLineView = [[UIView alloc]initWithFrame:CGRectMake(82, 67.5, 238, 0.5)];
colorLineView.backgroundColor = [UIColor blackColor];
return colorLineView;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
static NSString *identifier = #"cellIdentifier";
UIView *lineView = [self lineView];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
[cell.contentView addSubview:lineView];
}
if (indexPath.section == 0)
{
if (indexPath.row == keys.count -1)
{
lineView.hidden = YES;
}
}
return cell;
}
It may be recycling one of the cell views with the separator from the scroll. This is a long shot, but what if you were to try tweaking the footer view for the section by returning an empty view?
-(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return [[UIView alloc] init];
}
It's also a good trick for removing empty cells from the table when you have only a couple rows.
I tried it with multiple different things and the cleanest approach i found is this.
I created a custom view for the header, but wanted it to look the same as the original not modified header:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 34)];
[headerView setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, tableView.frame.size.width, 34)];
[label setFont:[UIFont boldSystemFontOfSize:14]];
if (section == 0) {
NSMutableArray *difficultyArray = [dictionary objectForKey:#"Difficulty"];
NSString *difficulty = [difficultyArray objectAtIndex:0];
[label setText:[NSString stringWithFormat:#"Time Challenge (%#)", difficulty]];
} else {
[label setText:#"Freeplay (5x5 board)"];
}
[headerView addSubview:label];
return headerView;
}
Now we got the sections as they would appear without custom header views, but the bug still exists. I made it simple and clean:
UIView *lineFix = [[UIView alloc] initWithFrame:CGRectMake(0, 77.5, self.tableView.frame.size.width, 0.5)];
lineFix.backgroundColor = [UIColor groupTableViewBackgroundColor];
[self.tableView addSubview:lineFix];
Now we set a view over the buggy seperator with a height of 0.5 pixel, the seperator isn't visible anymore. Between the two section headers now is a 0.5 height view what shouldn't be there, but since i set it the same color as the section background color it isn't noticeable. The view moves, because it is a subview of the tableview, the same direction like the tableview.
If you have questions, just add a comment.
I'm trying to customize the headers for sections in a grouped table. The view I set for the first section in table looks fine, but subsequent section headers look like cropped at top and at bottom (in this screenshot it is only shown at top):
I've been trying different X and Y values for frame.size.origin, but it remains looking the same. This is my code:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (section == 1) {
UIView *wrapper = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 70)];
[wrapper setBackgroundColor:[UIColor clearColor]];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, self.tableView.frame.size.width, 20)];
label.text = NSLocalizedString(#"Section 2 Header", #"");
[label setFont:[UIFont boldSystemFontOfSize:15]];
[label setBackgroundColor:[UIColor clearColor]];
[wrapper addSubview:label];
return wrapper;
}
else
return nil;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (section == 1)
return 70;
else
return 0;
}
I do the same for all the section headers, and only the first one is correctly displayed, what could I'm doing wrong? Regarding this issue, is it possible to dynamically know the height of an UILabel will take once its text and font size are known, or should you always set its frame size "at a guess"? The same for the height of view for the header that is set in heightForHeaderInSection: method.
Thanks!
you have not set height for header for the sections other than section 1 in this code,you should remove if(section==1) condition and provide common height for each section and then check
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (section == 1)
return 70;
else
return 0;
}
thanks,
Mittal.
Greets,
I'm reading that the default behaviour of UITableView is to pin section header rows to the top of the table as you scroll through the sections until the next section pushes the previos section row out of view.
I have a UITableView inside a UIViewController and this does not seem to be the case.
Is that just the defualt behaviour for UITableViewController?
Here's some simplified code based on what I have.
I'll show the UIController interface and each table view method I've implemented to create the table view.
I have a helper data source class that helps me index my objects for use with the table.
#interface MyUIViewController ()<UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, readonly) UITableView *myTableView;
#property (nonatomic, readonly) MyCustomHelperDataSource *helperDataSource;
#end
//when section data is set, get details for each section and reload table on success
- (void)setSectionData:(NSArray *)sections {
super.sectionData = sections; //this array drives the sections
//get additional data for section details
[[RestKitService sharedClient] getSectionDetailsForSection:someId
success:^(RKObjectRequestOperation *operation, RKMappingResult *details) {
NSLog(#"Got section details data");
_helperDataSource = [[MyCustomHelperDataSource alloc] initWithSections:sections andDetails:details.array];
[myTableView reloadData];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed getting section details");
}];
}
#pragma mark <UITableViewDataSource, UITableViewDelegate>
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (!_helperDataSource) return 0;
return [_helperDataSource countSectionsWithDetails]; //number of section that have details rows, ignore any empty sections
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//get the section object for the current section int
SectionObject *section = [_helperDataSource sectionObjectForSection:section];
//return the number of details rows for the section object at this section
return [_helperDataSource countOfSectionDetails:section.sectionId];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell;
NSString *CellIdentifier = #"SectionDetailCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.textLabel.font = [UIFont systemFontOfSize:12.0f];
}
//get the detail object for this section
SectionObject *section = [_helperDataSource sectionObjectForSection:indexPath.section];
NSArray* detailsForSection = [_helperDataSource detailsForSection:section.sectionId] ;
SectionDetail *sd = (SectionDetail*)[detailsForSection objectAtIndex:indexPath.row];
cell.textLabel.text = sd.displayText;
cell.detailTextLabel.text = sd.subText;
cell.detailTextLabel.textColor = [UIColor blueTextColor];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 50.0f;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 30.0f;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger) section {
//get the section object for the current section
SectionObject *section = [_helperDataSource sectionObjectForSection:section];
NSString *title = #"%# (%d)";
return [NSString stringWithFormat:title, section.name, [_helperDataSource countOfSectionDetails:section.sectionId]];
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 260, 0)];
header.autoresizingMask = UIViewAutoresizingFlexibleWidth;
header.backgroundColor = [UIColor darkBackgroundColor];
SSLabel *label = [[SSLabel alloc] initWithFrame:CGRectMake(3, 3, 260, 24)];
label.font = [UIFont boldSystemFontOfSize:10.0f];
label.verticalTextAlignment = SSLabelVerticalTextAlignmentMiddle;
label.backgroundColor = [UIColor clearColor];
label.text = [self tableView:tableView titleForHeaderInSection:section];
label.textColor = [UIColor whiteColor];
label.shadowColor = [UIColor darkGrayColor];
label.shadowOffset = CGSizeMake(1.0, 1.0);
[header addSubview:label];
return header;
}
The headers only remain fixed (floating) when the UITableViewStyle property of the table is set to UITableViewStylePlain.
If you have it set to UITableViewStyleGrouped, the headers will scroll up with the cells (will not float).
Change your TableView Style:
self.tableview = [[UITableView alloc] initwithFrame:frame
style:UITableViewStyleGrouped];
As per apple documentation for UITableView:
UITableViewStylePlain- A plain table view. Any section headers or
footers are displayed as inline separators and float when the table
view is scrolled.
UITableViewStyleGrouped- A table view whose sections present distinct
groups of rows. The section headers and footers do not float.
Hope this small change will help you ..
Swift 3.0
Create a ViewController with the UITableViewDelegate and UITableViewDataSource protocols. Then create a tableView inside it, declaring its style to be UITableViewStyle.grouped. This will fix the headers.
lazy var tableView: UITableView = {
let view = UITableView(frame: UIScreen.main.bounds, style: UITableViewStyle.grouped)
view.delegate = self
view.dataSource = self
view.separatorStyle = .none
return view
}()
You can also set the tableview's bounces property to NO. This will keep the section headers non-floating/static, but then you also lose the bounce property of the tableview.
to make UITableView sections header not sticky or sticky:
change the table view's style - make it grouped for not sticky & make it plain for sticky section headers - do not forget: you can do it from storyboard without writing code. (click on your table view and change it is style from the right Side/ component menu)
if you have extra components such as custom views or etc. please check the table view's margins to create appropriate design. (such as height of header for sections & height of cell at index path, sections)
Basically, I have a UITableView which will hold, say, alarms.
There is a '+' button at the bottom of the screen to add alarms but, of course, before any alarms are added the table is blank.
If there are no alarms, and the table view is empty, I'd like there to be some sort of placeholder text like "press the + to add an alarm".
I've tried this and also found a suggestion about making a placeholder UITableView with a cell that shows the above text. Then you show/hide the placeholder UITableView or the alarm UITableView depending on whether or not there are any alarms. I couldn't get that to work and think it's a bit much for wanting a simple string to show up.
I also tried creating a placeholder UITableViewCell if there are no alarms but that messes with numberOfRowsInSection which, in turn, breaks the ability to delete cells, as well it should (because numberOfRowsInSection can never be zero).
UPDATE: I also tried adding a UILabel to the tableView's tableHeaderView (and tableFooterView). No luck.
Any ideas?
After some researching on this site, I solved it by implementing the following delegate callbacks:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
if (self.alarmList.count == 0) {
UILabel *label = [[UILabel alloc] init];
label.text = #"press the + to add an alarm";
label.textAlignment = UITextAlignmentCenter;
label.numberOfLines = 2;
label.font = [UIFont boldSystemFontOfSize:16];
label.backgroundColor = [UIColor darkTextColor];
label.textColor = [UIColor whiteColor];
return label;
} else {
return nil;
}
}
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (self.alarmList.count == 0) {
return 68;
} else {
return 0;
}
}
I am creating a Grid (spreadsheet) like layout for iPad app using UITableView. I got the grid part working but since I am dynamically adding UILabels to the cells the reusable portion is not working fine. Here is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"FundCell"];
Fund *fund = [funds objectAtIndex:[indexPath row]];
float labelWidth = 1024 / ([columnNames count] -1 );
for(NSString *columnName in columnNames)
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x, 0, labelWidth, 44)];
label.backgroundColor = [UIColor clearColor];
label.text = [fund valueForKey:columnName];
x += label.bounds.size.width;
[cell addSubview:label];
}
x = 0;
return cell;
}
Result:
You're adding new labels in every reuse. You should only add the labels once, store references to them (usually as properties of a custom cell subclass) and just set the text value thereafter.
You may find it easier to define a custom cell in a xib and position your labels there, creating outlets. You can register this for reuse with the table, it will create or dequeue a cell as needed.