I'm currently trying to implement a editable details view using a grouped UITableView. I'd like it to look like the Contacts application:
in viewing-state it should display the header as plain label (in Contacts it's the name with TRANSPARENT background).
in editing-state it should display the header as editable UITableViewCell (in Contact's the tableHeader? changes from just the plain text with transparent background to a standard UITableViewCell with white background).
I'm not really sure what the best way is the achieve this. First I've tried to add the header as UILabel tableHeaderView (which works great), but then I cannot switch this to a UITableViewCell. A possibility would be to remove the header and add a new section when entering editing mode.
Currently I'm trying to always use a UITableViewCell and make it transparent in viewing mode and switch it to default in editing mode. However, I haven't been able to make the UILabel of the UITableViewCell (which is in UITableViewCellStyleDefault) transparent (although I did manage to make the UITableViewCell transparent, but not the textLabel inside it).
What is the best way to implement this behavior?
I've done this too (although a moot point with the changes to the Contacts app in iOS4!) My solution uses two different header views and switches between them based on isEditing:
- (UIView *)infoHeaderAnimated:(BOOL)animated {
UIView *header = [[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 90.0)] autorelease];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(98.0, 41.0, 221.0, 21.0)];
label.font = [UIFont boldSystemFontOfSize:17.0];
label.backgroundColor = [UIColor clearColor];
label.text = baseEntity.labelText;
[header addSubview:label];
[label release];
return header;
}
- (UIView *)editingHeaderAnimated:(BOOL)animated {
UIView *header = [[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 90.0)] autorelease];
UITableView *tv = [[UITableView alloc] initWithFrame:CGRectMake(78.0, 10.0, 240.0, 90.0) style:UITableViewStyleGrouped];
tv.backgroundColor = [UIColor clearColor];
tv.dataSource = self;
tv.delegate = self;
tv.rowHeight = 62.0; //### height of cell and frame depend on elements
tv.tag = kEditingHeaderTag;
editingHeaderTableView = [tv retain];
[header addSubview:tv];
[tv release];
return header;
}
What you are trying to do is very standard, consider implementing these protocols in the UITableViewDatasource, especially the titleForHeaderInSection & commitEditingStyle:
Configuring a Table View
– tableView:cellForRowAtIndexPath: required method
– numberOfSectionsInTableView:
– tableView:numberOfRowsInSection: required method
– sectionIndexTitlesForTableView:
– tableView:sectionForSectionIndexTitle:atIndex:
– tableView:titleForHeaderInSection:
– tableView:titleForFooterInSection:
Inserting or Deleting Table Rows
– tableView:commitEditingStyle:forRowAtIndexPath:
– tableView:canEditRowAtIndexPath:
Remember to choose the type of your TableView as Group instead of Plain in the Interface Builder.
Related
I have a customTableViewCell in a tableView. I am highlighting the cell when selected using,
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
But, the tableView appears like the following image.
It hides the labels and text in the customCell. I just want to highlight the cell simply without hiding the background image and labels. I do not know where I am making the mistake, whether in code or IB. .
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.Your_imageView.highlightedImage = [UIImage imageNamed:#"Your_image_Name.png"];
this will work fine
Hey i think you need to add labels and texts in the customCell to cell.contentview. For reference go to Apple developer document.
http://developer.apple.com/library/ios/#DOCUMENTATION/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7
[_tableView deselectRowAtIndexPath:selectedIndexPath animated:YES];
put this in to view Will Appear and its works for me ,
NSIndexPath *selectedIndexPath;
also put this to .h file too
Try this
UIView *viewSelected = [[[UIView alloc] init] autorelease];
viewSelected.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:#"cell_highlighted.PNG"]];
cell.selectedBackgroundView = viewSelected;
I just implemented a Load More button into the footer of my tableView, but the footer is always scrolling with the table. The style of my tableView is UITableViewStylePlain.
Please could you tell me where I am going wrong.
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
UIView *footerView = nil;
if(footerView == nil) {
footerView = [[[UIView alloc] init] autorelease];
footerView.backgroundColor = [UIColor clearColor];
UIButton *button = [[UIButton alloc] init];
button.layer.cornerRadius = 7;
[button setFrame:CGRectMake(10, 3, 300, 44)];
[button setTitle:#"Load 20 more" forState:UIControlStateNormal];
[button.titleLabel setFont:[UIFont boldSystemFontOfSize:20]];
[button setBackgroundColor:[UIColor whiteColor]];
[button.titleLabel setTextColor:[UIColor blackColor]];
[button addTarget:self action:#selector(load20More) forControlEvents:UIControlEventTouchUpInside];
[footerView addSubview:button];
[button release];
}
if ([songInfo count] > 20 && count < 100) {
return footerView;
}
else
return nil;
}
First of all, tableView:viewForFooterInSection does not define a footer for the table, it defines a footer for the section in the table. You can have multiple section footers in one table. In the case where your table is only one section, using this method will be functionally equivalent to adding a table footer, but it is NOT meant to be the table's footer. It is much better practice to use the actual table footer if that's what you want, which can be accomplished by simply assigning a view to the UITableView's tableFooterView property.
However, table footers (section footers AND table footers) are both built to scroll with your table. If you are looking to implement a "footer" that sticks to the bottom of the screen and does not scroll with the table, your best bet is to resize your UITableView smaller to make room for your "footer", and add a new view to sit in the spot you just cleared for it. That way, it will stick in place, and the scrollable region in your table will not overlap with the "footer".
Here if you use the table view in-built delegate method,
tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView?,
it will appear as fixed at the bottom of the view and it doesn't care about the height of the table view. Even though if you set tableView.tableFooterView?.isUserInteractionEnabled = true it won't work as you want. So as the solution for this, you have to set the table footer view on viewDidLoad() (as you are setting delegate and datasource in this method).
let cell = tableView.dequeueReusableCell(withIdentifier: "MyFinanceTableViewFooterCell") as! MyFinanceTableViewFooterCell
tableView.tableFooterView = cell
And this will work as you wish.
This is a work around. For plain UITableView, if you want the footer to be not sticky at the bottom, try using the footer in a section with no header. Example: we have one section with headerView and footerView. try using two sections, first section -> with headerView and nil(footer), second section -> nil(header) and footerView.
i would like to know how to build a screen that looks exactly like the About screen on iPhone.
I would like to display information in such format.. its so clean..
any ideas?
Erm all it is is a grouped type UITableView which is pretty easy to create. Create it in Interface Builder and choose "Grouped" as the Type & connect it up with your class or create it programatically:
Example:
UITableView * myTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 465) style:UITableViewStyleGrouped];
[myTable setDelegate:self];
[myTable setDataSource:self];
[controllerView addSubview:myTable];
[myTable reloadData];
[myTable release];
Then, you'll need to define the .accessoryView property in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. Let's for example create a switch as the cells accessoryView:
cell.textLabel.text = #"Donation Reminder";
UISwitch*donationSwitch = [[[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 27)] autorelease];
cell.accessoryView = donationSwitch;
and voila, the switch will be its subview. If you just want text, like in your question, just create a UILabel as I created the Switch.
UPDATE 4.0
Seems like iOS 4.0 changed something here. Same code producing incorrect backgrounds for section header in the described scenario is working with 4.0 according to my first quick check!
Original
I have a UITableView grouped style with custom header and footer view. Inside the footer I put a UILabel and a UIButton.
Clicking on the button hides or show some rows, updates the UILabel in the footer view and finally resizes footer view.
Basically everything is working fine. BUT the text ion the label is not updated on the screen. It is updated in the UILabel text property, but only if I scroll the section footer out of the visible area and scroll it back, it is updated. So it's a typical redraw problem here of the UITableView.
I tried every method to force update like needsLayout etc. Nothing helped.
I have seen some related questions but with some different behaviour and no solution. Any help/ideas?
Thanks, Gerd
UPDATE:
My problems occurs with section footer, so here is my viewForFooterInSection.
Basically I want to collapse/expand a section, but not completely (that was an easy thing) instead only the empty cell (ItemSize empty). The footerView is large if it is collapsed and will shrink if it is expanded. Furthermore the label text will change.
- (UIView *)tableView: (UITableView *)tableView viewForFooterInSection: (NSInteger)section{
NSLog(#"viewForFooterInSection section:%i", section);
UIButton *myView;
UILabel *label;
if ([[[self.sectionStatus objectAtIndex:section] valueForKey:#"collapseStatus"] isEqual:#"collapse"]){
myView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 320, 52)];
[myView setBackgroundImage:[UIImage imageNamed:#"ItemViewFooter.png"] forState:UIControlStateNormal];
label = [[UILabel alloc] initWithFrame:CGRectMake(20, 32, 300, 20)];
label.text = NSLocalizedString(#"list_expand",#"");
} else { //is expanded
myView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 320, 21)];
[myView setBackgroundImage:[UIImage imageNamed:#"ListCollapseExpand.png"] forState:UIControlStateNormal];
label = [[UILabel alloc] initWithFrame:CGRectMake(20, 1, 300, 20)];
label.text = NSLocalizedString(#"list_collapse",#"");
}
myView.tag=section;
[myView addTarget:self action:#selector(collapseExpandAction:) forControlEvents:UIControlEventTouchUpInside];
myView.backgroundColor = [UIColor clearColor];
myView.adjustsImageWhenHighlighted = NO;
myView.showsTouchWhenHighlighted = YES;
label.textColor = FONTCOLOR;
label.font = [UIFont systemFontOfSize:14];
label.numberOfLines = 1;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[myView addSubview:label];
return myView;
};
In the button action method I store status of section collapse/expand and the number of displayed rows. Than I delete or insert rows. (It has to be with insert/delete because I need the animation).
- (void) collapseExpandSection: (NSInteger) section{
NSMutableArray *paths = [NSMutableArray arrayWithCapacity:10];
NSInteger row;
NSInteger numberOfDisplayedItems=[[[self.sectionStatus objectAtIndex:section] valueForKey:#"numberOfDisplayedRows"] intValue];
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
NSInteger numberOfAllItems=[sectionInfo numberOfObjects];
Item *tmpItem=nil;
NSSet *itemsWithSizes=nil;
//filter not used cells
for ( row = 0; row < numberOfAllItems; row++ ) {
tmpItem=[fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:row inSection:section]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"itemSize != nil"];
NSSet *itemsWithSizes = [tmpItem.itemSizes filteredSetUsingPredicate:predicate];
if ([itemsWithSizes count]==0){
[paths addObject:[NSIndexPath indexPathForRow:row inSection:section]]; //all unused cells
};
}
if (numberOfDisplayedItems == numberOfAllItems){ //currently all shown => Collapse
[self.tableView beginUpdates];
[[self.sectionStatus objectAtIndex:section] setValue:[NSNumber numberWithInt:(numberOfDisplayedItems-[paths count])] forKey:#"numberOfDisplayedRows"];
[[self.sectionStatus objectAtIndex:section] setValue:#"collapse" forKey:#"collapseStatus"];
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
} else { //Not all shown so expand with the unused cells
[[self.sectionStatus objectAtIndex:section] setValue:[NSNumber numberWithInt:(numberOfDisplayedItems+[paths count])] forKey:#"numberOfDisplayedRows"];
[[self.sectionStatus objectAtIndex:section] setValue:#"expand" forKey:#"collapseStatus"];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
return;
};
Doing all this works fine in general. After the blocks begin/endupdate the viewForFooter is called for every section and the label text is set correct in the property. However the display doesn't update correctly. As soon as a redisplay is forced (srolling out- scrolling in) the display is OK.
There 2 problems.
First problem is that section footer not updated.
Try call [tableView reloadData] or [tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationFade] after your update (may be with dalay).
Second problem is memory leaks in myView and label.
Also why do you use label when you can use button's internal label?
P.S. Don't allocate UIButton object directly because it is factory. Call [UIButton buttonWithType:UIButtonTypeCustom] instead.
Upd: Another way to update is to update footer directly by accessing footer views.
- (void) collapseExpandSection: (NSInteger) section{
Check that section is actualy your button
- (void) collapseExpandSection: (UIButton*) sender{
// Do update of sender here
// Do other stuff
}
Also you can try next trick: create UIView object in delegate, add your button and label on it and return instaed of buttom view itself.
My problem was that I was expanding the Section header to show a search bar, but it wouldn't redraw the view until I scrolled the UITableView.
I had my own SectionHeader class that subclassed UIView and controlled the searching stuff.
After my animation, I just used this to force an update. It's not pretty but it works.
CGPoint point = CGPointMake(((UIScrollView *)self.superview).contentOffset.x,
((UIScrollView *)self.superview).contentOffset.y+1);
[((UIScrollView *)self.superview) setContentOffset:point animated:NO];
point = CGPointMake(((UIScrollView *)self.superview).contentOffset.x,
((UIScrollView *)self.superview).contentOffset.y-1);
[((UIScrollView *)self.superview) setContentOffset:point animated:NO];
Basically force the UITableView to scroll down 1 pixel and up 1 pixel.
I had an issue just like this where I wanted to update the section header after inserting a new row. I found that calling tableView reloadSections() method with an animation setting of .None after I call the insertRows method worked for me (both calls in the same tableView update block). I got the insert animation I wanted and also the section header was updated.
I have a cell in a table view that has a UILabel as a subview, when I set the text of the label, it is never shown on the cell. I'm somewhat a noob in iPhone development and am not sure what I'm doing wrong...
I am creating and returning the following cell in my table view's cellForRowAtIndexPath delegate:
cell = [[[ActivityCell alloc] initWithFrame:
CGRectZero reuseIdentifier:ColumnedCell] autorelease];
CGRect frame = CGRectMake(180.0, 11.0, 130.0, 22);
UILabel *valueField = [[UILabel alloc] initWithFrame:frame];
[valueField setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
valueField.tag = 111;
valueField.textAlignment = UITextAlignmentRight;
valueField.textColor = [UIColor colorFromHex:#"326799"];
valueField.highlightedTextColor = [UIColor whiteColor];
valueField.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
valueField.adjustsFontSizeToFitWidth = YES;
[cell.contentView addSubview:valueField];
[valueField release];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
I then want to set the UILabel (subview) later by doing this (I currently have this code in viewDidAppear):
ActivityCell *cell = (ActivityCell*)[formDetailsTableView cellForRowAtIndexPath:
[NSIndexPath indexPathForRow:0 inSection:1]];
UITextField *valueField = (UITextField *)[cell viewWithTag:111];
valueField.text = #"foo";
But the word "foo" never shows up in the UILabel. I believe it shows up when I compile with <= 2.2 SDK, but I want to get this working with 3.0+
Any ideas of why the text isn't showing up?
FOLLOW UP:
Please take a look at this question as a follow up to this: UITableViewCell's textLabel is overlapping a subview label, how can I fix it? (iPhone Dev)
Well first off, you're creating a UILabel, tagging it as 111, and then adding it as a subview to the cell's contentView. Later on, you are trying to retrieve that UILabel but you're casting it to a UITextField. Fundamentally something you're doing here is wrong.
Here's what I recommend doing:
Take a look at Apple's guide for using Interface Builder to create cells. It's going to lead to a lot less code and you'll be able to easily see what's going on, and won't have to add the Label/TextField programatically.
Instead of trying to access cells directly in viewDidAppear and changing their subviews, you should be doing all of that display logic inside of cellForRowAtIndexPath. In another method (for example, one called when the user touches a button), you'll modify some data structure and then send the tableView the reloadData message. At that time, cellForRowAtIndexPath is called again and the values set in your data structure determines how the cell is created.
It looks like you have a small bug:
change
UITextField *valueField = (UITextField *)[cell viewWithTag:111];
to
UITextField *valueField = (UITextField *)[cell.contentView viewWithTag:111];
My guess is that the cell coming back is nil and thus not updating your text. I would start buy making sure you are getting a cell back from this line:
ActivityCell *cell = (ActivityCell*)[formDetailsTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
cell = [[[ActivityCell alloc] initWithFrame:
CGRectZero reuseIdentifier:ColumnedCell] autorelease];
your cell has a frame of CGRectZero