I can't get my first UITableView to update with a setting made in a second UITableView.
A user clicks a row in firstTableView causing secondTableView to be displayed. When the user selects a row, secondTableView disappears and firstTableView is reappears. However, the data isn't updated.
I tried using the following in firstTableView:
- (void) viewWillAppear:(BOOL)animated {
// (verified it's defenitely section 2, row 0 by logging it before and after...)
// (also verified that the source data has been updated before viewWillAppear is called...)
NSIndexPath *durPath = [NSIndexPath indexPathForRow:0 inSection:2];
NSArray *paths = [NSArray arrayWithObject:durPath];
[self.firstTableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationNone];
// If I use some row animation, I can clearly see that the correct row is being animated, it's just not being updated.
}
But the label does not update. Obviously I'm missing something.
Both views are modal view controllers.
Here's my cell construction:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [firstTableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(150.0, 15.0, 120.0, 17.0)];
myLabel.backgroundColor = [UIColor clearColor];
myLabel.font = [UIFont systemFontOfSize:14];
myLabel.textAlignment = UITextAlignmentLeft;
static NSString* kConstants[] = {kOption0,kOption1,kOption2,kOption3,kOption4,kOption5,kOption6,kOption7,kOption8,kOption9,kOption10,nil};
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
if (indexPath.section == 2) {
[cell addSubview:myLabel];
}
}
switch (indexPath.section) {
case 0:
// … deal with a bunch of UISwitches
break;
case 1:
// … deal with section 1 stuff
break;
case 2:
{
NSLog(#"Verify that intType has in fact been changed here: %i, %#",intType, kConstants[intType]);
// Even though intType and the constant string reflects the correct (updated) values when returning from secondTableView, myLabel.text does not change, ie: it's correct one line above, but not correct one line below. The myLabel.text is just not updating to the new value.
myLabel.text = kConstants[intType];
cell.textLabel.text = #"Choose Some Value:";
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
break;
case 3:
// … deal with section 3 stuff
break;
}
[myLabel release];
return cell;
}
I have finally found the problem: when you reload the cell, (cell == nil) will be false, since the cell is already present.
Also, even if (cell == nil) is true, you are adding a new subview, and not modifying the existing one -- which is not only a memory management problem, it also makes the text unreadable by placing the labels on top of each other.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
static NSString* kConstants[] = {kOption0,kOption1,kOption2,kOption3,kOption4,kOption5,kOption6,kOption7,kOption8,kOption9,kOption10,nil};
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(150.0, 15.0, 120.0, 17.0)];
myLabel.backgroundColor = [UIColor clearColor];
myLabel.font = [UIFont systemFontOfSize:14];
myLabel.textAlignment = UITextAlignmentLeft;
myLabel.tag = 1;
myLabel.text = kConstants[intType];
[cell addSubview:myLabel];
[myLabel release];
cell.textLabel.text = #"Label";
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
} else {
UILabel *myLabel = (UILabel *)[cell viewWithTag:1];
myLabel.text = kConstants[intType];
}
return cell;
}
For anyone who stumbles upon this in the future, I'm posting a complete solution to my question here, thanks especially to Antal for showing me the major error in my table construction.
Using ViewWillAppear to reload the table is a bad idea in general, because it causes the table, or portions of the table to load twice. The proper way to do this is using the Delegate method of the second view controller, which I've done here.
I'm posting the relevant portions of the two view controllers. Both are setup as UViewControllers, not as UITableViewControllers. Both are modal views.
I hope someone finds this useful.
//
// FirstViewController.h
//
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#interface FirstViewController : UIViewController
<SecondViewControllerDelegate, UITableViewDataSource, UITableViewDelegate>
{
UITableView *firstTableView;
NSArray *myArray;
}
#property (nonatomic, retain) IBOutlet UITableView *firstTableView;
#property (nonatomic, assign) NSArray *myArray;
- (void) didSelectOptions:(NSInteger *)intOptionType;
- (void) didCancelOptions;
#end
//
// FirstViewController.m
//
#import "FirstViewController.h"
#import "Constants.h"
#implementation FirstViewController
#synthesize firstTableView;
#synthesize myArray;
- (void) viewDidLoad {
// Load the array that contains the option names, in this case, constants stored in Constants.h
myArray = [[NSArray alloc] initWithObjects:kStoredRowName0, kStoredRowName1, kStoredRowName2, nil];
}
// do everything else to deal with the first view . . .
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Use the indexPath.section as the identifier since objects in each section share unique construction, ie: switches, etc.
NSString *identifier = [NSString stringWithFormat: #"%d", [indexPath indexAtPosition: 0]];
// In this example I'm storing the important integer value in NSUserDefaults as kStoredConstant
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
UITableViewCell *cell = [firstTableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault
reuseIdentifier:identifier]
autorelease];
switch (indexPath.section) {
case 0: // OnOff Controls using UISwitch
NSLog(#"Section 0");
// set up switches …
break;
case 1: // Segmented Controls using UISegmentedControl
NSLog(#"Section 1");
// set up segmented controls …
break;
case 2: // Label that will be selected from SecondViewContoller
NSLog(#"Section 2");
// set up label
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(150.0, 15.0, 120.0, 17.0)];
myLabel.backgroundColor = [UIColor clearColor];
myLabel.font = [UIFont systemFontOfSize:14];
myLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
myLabel.textAlignment = UITextAlignmentLeft;
myLabel.tag = indexPath.section;
myLabel.text = [myArray objectAtIndex:[userDefaults integerForKey:kStoredConstant]];
[cell addSubview:myLabel];
[myLabel release];
cell.textLabel.text = #"Choose A Value:";
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
break;
}
} else {
switch (indexPath.section) {
case 0: // OnOff Controls using UISwitch
NSLog(#"Section 0");
break;
case 1: // Segmented Controls using UISegmentedControl
NSLog(#"Section 1");
break;
case 2: // Label that will be selected from SecondViewContoller
{
NSLog(#"Section 2");
UILabel *myLabel = (UILabel *)[cell viewWithTag:indexPath.section];
myLabel.text = [myArray objectAtIndex:[userDefaults integerForKey:kStoredConstant]];
}
break;
}
}
// Format the cell label properties for all cells
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.font = [UIFont systemFontOfSize:14];
cell.textLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
cell.textLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
return cell;
}
- (void) tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
// Un-highlight the selected cell
[firstTableView deselectRowAtIndexPath:indexPath animated:YES];
switch (indexPath.section) {
case 0: // Deal with changes in UISwitch Controls
NSLog(#"Section 0");
break;
case 1: // Deal with changes in Segmented Controls
NSLog(#"Section 1");
break;
case 2: // Launch the SecondViewContoller to select a value
{
SecondViewController *secondViewController = [[SecondViewController alloc] init];
secondViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
secondViewController.secondViewControllerDelegate = self;
[self presentModalViewController:secondViewController animated:YES];
[secondViewController release];
}
break;
}
}
#pragma mark -
#pragma mark SecondViewControllerDelegate
- (void) didSelectOptions:(NSInteger *)intOptionType {
// User selected a row in secondTableView on SecondViewController, store it in NSUserDefaults
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:(int)intOptionType forKey:kStoredConstant];
[userDefaults synchronize];
// Reload only the row in firstTableView that has been changed, in this case, row 0 in section 2
[self.firstTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:2]]withRowAnimation:UITableViewRowAnimationNone];
[self dismissModalViewControllerAnimated:YES];
}
- (void) didCancelOptions {
// User didn't select a row, instead clicked a done or cancel button on SecondViewController
[self dismissModalViewControllerAnimated:YES];
}
// Make sure and release Array and Table
//
// SecondViewController.h
//
#import <UIKit/UIKit.h>
#protocol SecondViewControllerDelegate <NSObject>
- (void) didCancelOptions;
- (void) didSelectOptions:(NSInteger *)optionType;
#end
#interface SecondViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate>
{
NSArray *myArray;
UITableView *secondTableView;
id secondViewControllerDelegate;
}
#property (nonatomic, retain) NSArray *myArray;
#property (nonatomic, retain) IBOutlet UITableView *secondTableView;
#property (nonatomic, assign) id<SecondViewControllerDelegate> secondViewControllerDelegate;
- (IBAction) doneViewingOptions:(id)sender; // This is wired to a Cancel or a Done Button
#end
//
// SecondViewController.m
//
#import "SecondViewController.h"
#import "Constants.h"
#implementation SecondViewController
#synthesize secondViewControllerDelegate;
#synthesize myArray;
#synthesize secondTableView;
- (void) viewDidLoad {
// Load the array that contains the option names, in this case, constants stored in Constants.h
myArray = [[NSArray alloc] initWithObjects:kStoredRowName0, kStoredRowName1, kStoredRowName2, nil];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Build a default table. This one is simple so the following is the only important part:
cell.textLabel.text = [myArray objectAtIndex:indexPath.row];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Return the changed row value to the FirstViewController using secondViewControllerDelegate
[self.secondViewControllerDelegate didSelectOptions:(NSInteger *)indexPath.row];
}
- (IBAction) doneViewingOptions:(id)sender {
// User didn't select a row, just clicked Done or Cancel button
[self.secondViewControllerDelegate didCancelOptions];
}
// Make sure and release Array and Table
You can use reloadRowsAtIndexPaths to reload a specific row (or rows) and avoid having to reload the entire table.
As for the label that displays the string value, could you post the code so we can see?
Related
I have a table view. Now I want to collapse and expand rows by tapping on the section header. In other words, when I tap the header the rows display for that section. How can I do this?
I draft up some code to give you the idea. The concept is we keep track of collapsed section in NSMutableSet and add/remove it according to the user touch on the section. The collapse/expand animation is actually the animation of adding/removing cells.
#import "ViewController.h"
#interface ViewController () < UITableViewDataSource, UITableViewDelegate> {
NSMutableSet* _collapsedSections;
}
#property (nonatomic, weak) IBOutlet UITableView* tableView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_collapsedSections = [NSMutableSet new];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_collapsedSections containsObject:#(section)] ? 0 : 10;
}
-(NSArray*) indexPathsForSection:(int)section withNumberOfRows:(int)numberOfRows {
NSMutableArray* indexPaths = [NSMutableArray new];
for (int i = 0; i < numberOfRows; i++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:i inSection:section];
[indexPaths addObject:indexPath];
}
return indexPaths;
}
-(void)sectionButtonTouchUpInside:(UIButton*)sender {
sender.backgroundColor = [UIColor greenColor];
[self.tableView beginUpdates];
int section = sender.tag;
bool shouldCollapse = ![_collapsedSections containsObject:#(section)];
if (shouldCollapse) {
int numOfRows = [self.tableView numberOfRowsInSection:section];
NSArray* indexPaths = [self indexPathsForSection:section withNumberOfRows:numOfRows];
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
[_collapsedSections addObject:#(section)];
}
else {
int numOfRows = 10;
NSArray* indexPaths = [self indexPathsForSection:section withNumberOfRows:numOfRows];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
[_collapsedSections removeObject:#(section)];
}
[self.tableView endUpdates];
//[_tableView reloadData];
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIButton* result = [UIButton buttonWithType:UIButtonTypeCustom];
[result addTarget:self action:#selector(sectionButtonTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
result.backgroundColor = [UIColor blueColor];
[result setTitle:[NSString stringWithFormat:#"Section %d", section] forState:UIControlStateNormal];
result.tag = section;
return result;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
result.textLabel.text = [NSString stringWithFormat:#"Cell %d", indexPath.row];
return result;
}
#end
As already this is a old question now.Me too also searched and in the last got 1 sample code from Github.So I thought of sharing the link,if in near future any one comes up with the same issues.
https://github.com/iSofTom/STCollapseTableView
TLIndexPathTools does this with only a few lines of code on your part. Try running the Collapse sample project. It subclasses TLCollapsibleTableViewController, which has a couple of nice options. It supports expanding a single section at a time or multiple sections. It also optimizes the scroll position when you expand a section to show as many rows of the section as possible. So if you tap on a section near the bottom of the screen, it will scroll up automatically.
The full view controller code of the sample project is as follows:
#import "TLCollapsibleTableViewController.h"
#interface CollapseTableViewController : TLCollapsibleTableViewController
- (IBAction)toggleSingleSectionExpanded:(UISwitch *)sender;
#end
#import "CollapseTableViewController.h"
#import "TLIndexPathSectionInfo.h"
#import "TLCollapsibleDataModel.h"
#define SECTION1_NAME #"Section 1"
#define SECTION2_NAME #"Section 2"
#interface CollapseTableViewController ()
#property (strong, nonatomic) TLIndexPathDataModel *backingDataModel;
#end
#implementation CollapseTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//define items for two sections
NSArray *section1Items = #[
#"Fredricksburg",
#"George Washington",
#"Grand Canyon"];
NSArray *section2Items = #[
#"Jelly Bean",
#"Bibliography",
#"Keyboard Shortcut",
#"Metadata",
#"Fundamental",
#"Cellar Door"];
//We're using plain string items, so we don't have a sectionNameKeyPath property
//to use, so instead we explicitly create section info objects
TLIndexPathSectionInfo *section1 = [[TLIndexPathSectionInfo alloc] initWithItems:section1Items andName:SECTION1_NAME];
TLIndexPathSectionInfo *section2 = [[TLIndexPathSectionInfo alloc] initWithItems:section2Items andName:SECTION2_NAME];
//create the backing model, which contains all sections and items
self.backingDataModel = [[TLIndexPathDataModel alloc] initWithSectionInfos:#[section1, section2]
andIdentifierKeyPath:nil andCellIdentifierKeyPath:nil];
[self collapseAll];
}
- (void)tableView:(UITableView *)tableView configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSString *item = [self.dataModel itemAtIndexPath:indexPath];
cell.textLabel.text = item;
}
- (IBAction)toggleSingleSectionExpanded:(UISwitch *)sender {
self.singleExpandedSection = sender.isOn;
[self collapseAll];
}
- (void)collapseAll
{
self.dataModel = [[TLCollapsibleDataModel alloc] initWithBackingDataModel:self.backingDataModel
collapsedSectionNames:[NSSet setWithArray:self.backingDataModel.sectionNames]];
}
#end
configure viewForHeaderInSection:
like this
(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
UILabel *lblHeader = [[UILabel alloc]init];
lblHeader.text = #"Section 0";
lblHeader.backgroundColor = [UIColor blueColor];
lblHeader.font = [UIFont fontWithName:#"Avenir" size:18];
lblHeader.textAlignment=NSTextAlignmentLeft;
lblHeader.userInteractionEnabled=YES;
UIGestureRecognizer *gr;
if(section==0){
lblHeader.text = #"Section 0";
gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
}else if(section == 1){
lblHeader.text = #"Section 1";
gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture1:)];
}
[lblHeader addGestureRecognizer:gr];
return lblHeader;
}
then write seperate action calls
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer {
}
- (void)handleGesture1:(UIGestureRecognizer *)gestureRecognizer {
}
I am using a custom framework and I need to set the subclass of my 'TableView' and 'TableViewCell' to the custom framework. I would normally do this easily with the identity inspector but since I created everything programmatically, I do not know how to do it. I am also not using storyboards. Any tips?
---edit----
TableViewController.h
#import <UIKit/UIKit.h>
#import "Tasks.h"
#import "Properties2ViewController.h"
#import "PKRevealController.h"
#import "FMMoveTableView.h"
#import "FMMoveTableViewCell.h"
#import "DetailViewController.h"
#interface ToDoTableViewController : UITableViewController <Properties2ViewControllerDelegate, UITableViewDelegate, FMMoveTableViewDataSource>
#property (strong, nonatomic) NSMutableArray *taskArray;
-(IBAction)addCell:(id)sender;
#end
TableViewController.m
#import "ToDoTableViewController.h"
#implementation ToDoTableViewController
#synthesize taskArray;
- (id)init {
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
UINavigationItem *i = [self navigationItem];
[i setTitle:#"Task List"];
[[i title] uppercaseString];
UIBarButtonItem *bbi = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addCell:)];
[[self navigationItem] setRightBarButtonItem:bbi];
[self.tableView setSeparatorColor:[UIColor colorWithRed:26.0/255 green:188.0/255 blue:156.0/255 alpha:1.0f]];
[self.tableView setBackgroundView:nil];
[self.tableView setBackgroundColor:[UIColor colorWithRed:26.0/255 green:188.0/255 blue:156.0/255 alpha:1.0f]];
}
return self;
}
- (id) initWithStyle:(UITableViewStyle)style{
return [self init];
}
-(void) viewDidLoad{
FMMoveTableView *mtc = [[FMMoveTableView alloc]init];
[mtc setDataSource:self];
[self.tableView setDelegate:self];
taskArray = [[NSMutableArray alloc] init];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (!cell)
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"UITableViewCell"];
NSString *detailText = [NSString stringWithFormat:#"%.0f", [[taskArray objectAtIndex:[indexPath row]] timeInterval]];
[[cell textLabel] setText:[[taskArray objectAtIndex:[indexPath row]] taskName]];
cell.textLabel.text = [[[taskArray objectAtIndex:[indexPath row]] taskName] uppercaseString];
[[cell textLabel] setFont:[UIFont fontWithName:#"AvenirNext-DemiBold" size:15]];
[cell setBackgroundColor:[UIColor colorWithRed:236.0/255 green:240.0/255 blue:241.0/255 alpha:1.0f]];
cell.textLabel.textColor = [UIColor colorWithRed:26.0/255 green:188.0/255 blue:156.0/255 alpha:1.0f];
[[cell detailTextLabel] setText:detailText];
[[cell detailTextLabel] setFont:[UIFont fontWithName:#"Avenir-Black" size:16]];
return cell;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [taskArray count];
}
-(IBAction)addCell:(id)sender{
Properties2ViewController *pvc = [[Properties2ViewController alloc]init];
[pvc setDelegate:self];
[self presentViewController:pvc animated:YES completion:NULL];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[self tableView] reloadData];
}
-(void)properties2ViewControllerDidEnterPropertiesSuccesfully:(Tasks *)t{
if (![[t taskName] isEqual: #""]) {
[taskArray addObject:t];
}
[self.tableView reloadData];
}
-(void)moveTableView:(FMMoveTableView *)tableView moveRowFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath{
[tableView moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath];
[tableView reloadData];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Tasks *task = [taskArray objectAtIndex:[indexPath row]];
DetailViewController *dvc = [[DetailViewController alloc]init];
[dvc setTestTask:task];
[[self navigationController] pushViewController:dvc animated:YES];
// PKRevealController *pkrc = [[PKRevealController alloc]initWithFrontViewController:self rightViewController:dvc options:nil];
//[pkrc showViewController:dvc animated:YES completion:NULL];
}
-(void)loadView{
[super loadView];
}
#end
If you are instantiating the UITableView yourself, just instantiate the library class instead. For example, if you said this:
UITableView *tableView = [[UITableView alloc] initWithFrame:frame
style: UITableViewStylePlain];
then do this instead:
LibraryTableView *tableView = [[LibraryTableView alloc] initWithFrame:frame
style:UITableViewStylePlain];
If LibraryTableView provides some other, more specialized initWith… method, you may need to use that instead.
If you're storing a reference to the table view in a property or instance variable, you may also want to change the type of that property or instance variable from UITableView * to LibraryTableView *.
UPDATE
I believe if you're using a UITableViewController, you can create your own table view and assign it to the controller's tableView property:
UITableViewController *tvc = ...;
tvc.tableView = [[LibraryTableView alloc] initWithFrame:frame
style:UITableViewStylePlain];
UPDATE 2
In your viewDidLoad, you're creating an FMMoveTableView, but you're not storing it in the view controller's tableView property. You must store it in the view controller's tableView property, and when you do, the view controller will automatically set the table view's dataSource and delegate to self.
-(void) viewDidLoad{
self.tableView = [[FMMoveTableView alloc] init];
taskArray = [[NSMutableArray alloc] init];
}
In your tableView:cellForRowAtIndexPath:, you need to instantiate an FMMoveTableViewCell, not a UITableViewCell. You send the alloc message to the class you want to instantiate:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (!cell) {
cell = [[FMMoveTableViewCell alloc]
initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:#"Cell"];
}
// etc.
If you have your own custom subclass of FMMoveTableViewCell, instantiate that instead.
I have created custom TableViewCells before, you can create a custom class just like you would for viewControllers (File > New > File). When choosing which object to subclass, choose UITableViewCell.
Unfortunately, Xcode will not give you the option to include a .xib file, so you'll have to create one of those too: File > New > File, but this time on the left side of the window that pops up, select 'User Interface' instead of 'Cocoa Touch'. Choose the 'Empty' icon at this screen.
You will be asked whether the device family is iPhone or iPad. Since you are making a table view cell not a viewController, it doesn't matter which you pick.
When you create this object you will be presented with a blank canvas. Drag a 'Table View Cell' from the object library to the canvas and you're set to start creating!
It is of note that where you'd normally create outlets, you should create properties for TableViewCells. This is because other objects are going to be interacting with the TableViewCell, not just the TableViewCell's implementation file.
Here is what a header file for a custom TableViewCell might look like:
#interface MyCustomCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIImageView *icon;
#property (weak, nonatomic) IBOutlet UILabel *label;
#property (weak, nonatomic) IBOutlet UILabel *number;
#end
My implementation file was empty.
However, that's not all. There is also some work to be done in the TableViewController that will utilize this cell. Be sure to import your TableViewCell's header file.
First, the cell has to be registered in the viewDidLoad method
// Resiter the Nib
UINib *nib = [UINib nibWithNibName:#"MyCustomCell" bundle:nil];
[[self tableView] registerNib:nib forCellReuseIdentifier:#"MyCustomCell"];
And then when the TableViewController determines how to draw the cells:
- (UITableViewCell *) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCustomCell"]
// Then here you can set the properties of each cell
[[cell label] setText:#"Some text here"];
[[cell number] setText:[NSString stringWithFormat:#"%d", 5]];
[[cell icon] setImage:[UIImage imageNamed:#"myImage]];
}
I hope this is helpful!
Right Now I am making a twitter app and I am Using IFTweetLabel. I made a TableViewCell Subclass but When I Run it the label dosent show up.
Here is the .h:
#import <UIKit/UIKit.h>
#import "IFTweetLabel.h"
#interface twitterTextCell : UITableViewCell
#property (strong, nonatomic) IFTweetLabel *customtextLabel;
#end
And here is the implementation:
#import "twitterTextCell.h"
#implementation twitterTextCell
#synthesize customtextLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
customtextLabel.backgroundColor = [UIColor orangeColor];
self.customtextLabel.frame = CGRectMake(240, 60, 345, 109);
[self addSubview:customtextLabel];
NSLog(#"Custom Label Text: %#", customtextLabel.text);
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(UILabel *)textLabel
{
return nil;
}
Here is the code that is in the cellforrow in the tableviewdelegate:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
twitterTextCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[twitterTextCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSDictionary *tweet = [self.timeLineData objectAtIndex:indexPath.row];
if ([tweet objectForKey:#"text"] != nil) {
NSString *allText = [[tweet objectForKey:#"text"] stringByDecodingHTMLEntities];
cell.customtextLabel.numberOfLines = 4;
cell.textLabel.numberOfLines = 4;
cell.customtextLabel.text = allText;
cell.textLabel.text = allText;
cell.customtextLabel.linksEnabled = YES;
NSDictionary *whoPostedTweet = [tweet objectForKey:#"user"];
cell.detailTextLabel.text = [whoPostedTweet objectForKey:#"name"];
NSURL *imageURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#", [whoPostedTweet objectForKey:#"profile_image_url"]]];
[cell.imageView setImageWithURL:imageURL placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.backgroundColor = [UIColor clearColor];
[cell.imageView.layer setCornerRadius:7.0f];
[cell.imageView.layer setMasksToBounds:YES];
return cell;
} else {
[self getTwitterData];
NSLog(#"nil called(else) ");
return cell;
}
return cell;
}
After all of this only the image and the detailtextview show up. I removing the default textview because If I don't the text shows up normally but it is not the IFTweetLabel. Another thing I have a question about is how do I reposition the detailtextabel so that it is Under the IFTweetLabel. Anything is greatly appreciated, I need help on both of these issues.
i think your problem is you are not allocating customTextLabel.
Just allocate the same before setting the background color in initWithStyle.
ie, self.customtextLabel = [[[IFTweetLabel alloc] init] autorelease];
How to make UITableView like above image?
I know this is grouped table type. but how we can add the image+label+button to header of section.
I have tried
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
but it starts with CGRectMake(0,0,320,height).
I want just above section and exact width of section just like in image.
Thanks in advance.
Instead of trying to change the section header view, you might want to create a custom cell with a brown background, a label and a button and use it for the first row. So, in -cellForRowAtIndexPath, you could do something like
if (0 == indexPath.row) {
return brownCell;
} else {
return normalCell;
}
There are several ways to create custom cells, I always start from the Table View Programming Guide for iOS.
CustomCell.h
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell{
id delegate;
NSIndexPath *indexpath;
}
#property(nonatomic,assign) id delegate;
#property(nonatomic,retain)NSIndexPath *indexpath;
#property(nonatomic,retain) IBOutlet UIToolbar *Toolbar;
-(IBAction)SelectorLeft:(id)sender;
-(IBAction)SelectorRight:(id)sender;
#end
customcell.m
#import "CustomCell.h"
#import <QuartzCore/QuartzCore.h>
#implementation CustomCell
#synthesize Toolbar;
#synthesize delegate,indexpath;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
-(IBAction)SelectorLeft:(id)sender{
[delegate perfromselector:#selector(left:) withObject:indexpath];
}
-(IBAction)SelectorRight:(id)sender{
[delegate perfromselector:#selector(left:) withObject:indexpath];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
UItbaleView part
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"identifier";
NSUInteger row = [indexPath row];
if (row == 0) {
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
self.Cell = nil;
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = self.Cell;
}
cell.Toolbar.clipsToBounds=YES;
CALayer *l=cell.Toolbar.layer;
// set corner radious
[l setCornerRadius:10];
// to apply border on corners
[l setBorderColor:[[UIColor clearColor] CGColor]];
// to apply set border width.
[l setBorderWidth:5.0];
return cell;
}else{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.textLabel.text = [NSString stringWithFormat: #"cell %i",row];
cell.delegate = self;
cell.indexpath = indexpath;
return cell;
}
return nil;
}
Also do n't forget to create Customcell.xib and add tool bar through interface builder
also create an outlet of CustomCell in tableview class and handle it as above
its easy all what you have to
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSUInteger row = [indexPath row];
if (row % 2 == 0) {
static NSString *identifier = #"RowOne";
UITableViewCell *cell = (RowTypeOne*)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault]autorelease];
}
cell.Title.text = [datasource objectatindex:row];
cell.Title.font = [UIFont fontWithName:#"Tahoma" size:16];
cell.contentView.backgroundColor = [UIColor redColor];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.textAlignment = UITextAlignmentRight;
return cell;
}else if (row % 2 == 1) {
static NSString *identifier = #"RowTwo";
UITableViewCell *cell = (RowTypeOne*)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault]autorelease];
}
cell.contentView.backgroundColor = [UIColor redColor];
cell.Title.text = [datasource objectatindex:row];
cell.Title.font = [UIFont fontWithName:#"Tahoma" size:16];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.textAlignment = UITextAlignmentRight;
return cell;
}
return nil;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
//create btn left each on handeled by selector
UIBarButtonItem *btnleft = [[UIBarButtonItem alloc] initWithTitle:#"List of sms" style:UIBarButtonItemStylePlain target:self action:#selector(ListClicked:)];
//create btn right each on handeled by selector
UIBarButtonItem *btnright = [[UIBarButtonItem alloc] initWithTitle:#"New" style:UIBarButtonItemStyleBordered target:self action:#selector(NewClicked:)];
//create uitoolbar then add btns to it as list of array
UIToolbar *tool = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
//change style of toolbar to be black
[tool setBarStyle:UIBarStyleBlack];
tool.items = [NSArray arrayWithObjects:btnleft,[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],btnright, nil];
//this is the parent view that we will add on it the uitoolbar objects and the buttons
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
[view autorelease];
[view addSubview:tool];
return view;
}
-(void)ListClicked:(id)sender{
//handle here btn left
}
-(void)NewClicked:(id)sender{
//handle here btn right
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 44;
}
if you want the cell not to be in the header you can check for first row in cellforrow at index path...
also if you want to do it in another style you can make custom cell and add it also to cellforrowatindexpath
I am working on an application having UItableView. In the rows of the table i am able to put checkmarks.Now how to save the state of checkmarks so that even if user close the application
the state should be shaved and in the next launch of application checkmark should be shown.
i have followed the tutorials on NSUSerDefaults but Pulling my hairs where to put the codes of saving and retrieving.I have tried but every time errors are stuffing me and not able to fix.
My Code:
MY.h file
**#protocol LocationSelectionViewControllerDelegate <NSObject>
#required
- (void)rowSelected:(NSString *)selectedValue selectedIndex:(NSInteger)index;
#end**
#interface LocationSelection : UIViewController <UITableViewDelegate,UITableViewDataSource>{
UITableView *table;
***NSInteger selectedIndex;***
NSMutableArray *menuList;
***id <LocationSelectionViewControllerDelegate> delegate;***
}
#property (nonatomic,retain) NSMutableArray *menuList;
#property (nonatomic,retain) IBOutlet UITableView *table;
***#property (nonatomic, assign) id <LocationSelectionViewControllerDelegate> delegate;**
**#property NSInteger selectedIndex;***
#end
my .m file:
#implementation LocationSelection
***#synthesize menuList, table,selectedIndex,delegate;***
- (void)viewDidLoad
{
menuList = [[NSMutableArray alloc] initWithObjects:
[NSArray arrayWithObjects:#"LOCATION1", nil],
[NSArray arrayWithObjects:#"LOCATION2", nil],
[NSArray arrayWithObjects:#"LOCATION3", nil],
nil];
self.title = #"Location Selection";
[table reloadData];
[super viewDidLoad];
}
//MY CELLFORROWATINDEXPATH
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [table dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero] autorelease];
}
cell.highlighted = NO;
***NSArray * rowArray = [menuList objectAtIndex:indexPath.row];***
UILabel * nameLabel = [[[UILabel alloc] initWithFrame:CGRectMake(15, 8, 200, 20)] autorelease];
nameLabel.text = [NSString stringWithFormat:#"%#", [rowArray objectAtIndex:0]];
[cell.contentView addSubview:nameLabel];
***cell.accessoryType = (rowArray == selectedIndex && selectedIndex > -1 && selectedIndex < [menuList count]) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
***
}
//MY DIDSELECTROWATINDEXH
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int newRow = [indexPath row];
if (newRow != selectedIndex)
{
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
if (selectedIndex > - 1 && selectedIndex < [menuList count])
{
NSUInteger newIndex[] = {0, selectedIndex};
NSIndexPath *lastIndexPath = [[NSIndexPath alloc] initWithIndexes:newIndex length:2];
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath: lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
}
selectedIndex = newRow;
NSString *selectedValue=[menuList objectAtIndex:selectedIndex];
[self.delegate rowSelected:selectedValue selectedIndex:selectedIndex];
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
}
Use this method to save your data to NSUserDefaults
And set the data in viewDidLoad of LocationSelection
Hi I think it will suit you if you have an object(s) to indicate the checkmark. You can save this to the user defaults through synchronize. In the cellForRowATIndexPath: you can check if the value is present in the user defaults and if yes make the cell accessory as checkmarked and if its not present make it none.
Put a state in your menuList which reflects selection of the cell so even multiselection can be done easily. You can use a dictionary instead of the arrays. Arrays are fine but not as readable, so you have to remember which field contains what and map it to an index.
When the view loads load the menuList array from your userDefaults, when the app closes save the defaults.
In didSelectRowAtIndexPath you save selection in menuList.
In cellForRowAtIndexPath you read menuList and the new array index/dictionary key and set the checkmark.