I'm currently working on my second project which is focusing on CoreData and Custom Cells.
I have a custom cell with an image & label & switch, i'm trying to save the value of the switch to userdefaults when the value of the switch has changed. But i'm stuck with how to access each switch individually (there are 2 in the same section of my table view), so that when the switch is pressed the integer stored in userdefaults is instantly updated.
This is the code in the .m file concerning the custom cell (switchCell) apologises for any messy code etc, i'm pretty much 100% self taught without any advice/feedback on any mistakes i'm making.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil;
// Code for the first section, controlling which cells use the custom cell SwitchCell
if (indexPath.section == 0)
{
if(indexPath.row == 1 || indexPath.row == 2)
{
SwitchCell *switchCell = [tableView dequeueReusableCellWithIdentifier:#"SwitchCellIdentifier"];
cell = switchCell;
}
else
{
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"CellIdentifier"];
UISwitch *testSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
cell.accessoryView = testSwitch;
}
}
}
// Each other section currently uses the standard cell type
else
{
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"CellIdentifier"];
}
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dictionary = [settingsTableData objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"Settings"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
SwitchCell *switchCell = (SwitchCell *)cell;
[[NSUserDefaults standardUserDefaults] synchronize];
// Integers to store the on/off state of each switch (2)
NSInteger capsValue = [[NSUserDefaults standardUserDefaults] integerForKey:#"capitalsSwitchOn"];
NSInteger numbersValue = [[NSUserDefaults standardUserDefaults] integerForKey:#"numbersSwitchOn"];
NSLog(#"Upon load capsValue equals : %d", capsValue);
NSLog(#"upon load numbersValue equals : %d", numbersValue);
// Setting individual cell values for attributes such as image and text
if (indexPath.section == 0)
{
if(indexPath.row == 1)
{
switchCell.switchCellLabel.text = cellValue;
switchCell.switchCellImage.image = [UIImage imageNamed:#"capitalsImage.jpg"];
// Set to true or false depending on what you want the default value to be.
//switchCell.switchCellSwitch.on = FALSE;
if (capsValue == 1) {
[switchCell.switchCellSwitch setOn:NO animated:YES];
} else
[switchCell.switchCellSwitch setOn:YES animated:YES];
}
else if (indexPath.row == 2)
{
switchCell.switchCellLabel.text = cellValue;
switchCell.switchCellImage.image = [UIImage imageNamed:#"capitalsImage.jpg"];
if (numbersValue == 1) {
[switchCell.switchCellSwitch setOn:NO animated:YES];
} else
[switchCell.switchCellSwitch setOn:YES animated:YES];
}
else
{
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
else if (indexPath.section == 1)
{
if (indexPath.row == 0)
{
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else if (indexPath.row == 1)
{
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
else if (indexPath.section == 2)
{
if (indexPath.row == 0)
{
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else if (indexPath.row == 1 || indexPath.row == 2)
{
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
}
Thanks in advance to any help!
I would add that switch in your subclassed cell and not in the viewController. Then in the cell subclass create a delegate, which would call a method like switchDidChangeValue in the viewController. There set your defaults.
Add an IBAction to your SwitchCell.
In the header file add:
- (IBAction)switchChanged: (UISwitch*)sender;
And in the .m file apply:
- (IBAction)switchChanged: (UISwitch*)sender {
BOOL value = sender.isOn;
// Do your Core Data work here
}
You might have to add a property to SwitchCell, so that it knows what database entry to create/change.
Then go into your Storyboard or .nib file, click on the UISwitch, open the last Tab in the object inspector. There you should be able to see Send Events -> Value Changed. Drag a connection from that to your SwitchCell, select your IBAction and you should be good to go.
So many codes :-) and I'm not suer if I understand your mind or not. But I think maybe you can define two properties in your viewController for two switches, switchOne and switchTwo , and you assign them in function
tableView:cellForRowAtIndexPath:
I always do this when I have independent controls in grouped tableView.
Still I have some question about your code:
Do you mean the second and third cell in your first section are cells with switches?
I think in your code
if(indexPath.row == 1 || indexPath.row == 2){
SwitchCell *switchCell = [tableView dequeueReusableCellWithIdentifier:#"SwitchCellIdentifier"];
cell = switchCell;
}
the cell will always be nil, because you never build a cell with identifier "SwitchCellIdentifier"
I think you build switch controls for other cells of your first section, so I totally have no idea what you want
It's 2:00 in the morning here in China, I worked all night long and I'm very tried, so maybe your code is right and I misunderstood, if so, please let me know.
Related
I am using a array containing objects from two dictionaries and displaying objects of first dictionary on first cell having different identifier and same with second dictionary objects.
check out my code in cellforrowatindexpath
static NSString *cellIdentifier = #"cellIdentifier1";
static NSString *customCellIdentifier = #"cellIdentifiercustom";
if (indexPath.row == 0) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// if (cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
ExcName = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 2.0, 250.0, 36.0)];
ExcName.font = [UIFont systemFontOfSize:12.0];
ExcName.textColor = [UIColor blackColor];
ExcName.textAlignment = NSTextAlignmentLeft;
ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"activityname"];
ExcName.numberOfLines=3;
[cell.contentView addSubview:ExcName];
return cell;
}
else{
UITableViewCell *customcell = [tableView dequeueReusableCellWithIdentifier:customCellIdentifier];
// if (cell==nil)
{
customcell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:customCellIdentifier];
}
customcell.selectionStyle = UITableViewCellSelectionStyleNone;
ExcName = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 2.0, 250.0, 36.0)];
ExcName.font = [UIFont systemFontOfSize:12.0];
ExcName.textColor = [UIColor blackColor];
ExcName.textAlignment = NSTextAlignmentLeft;
ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"exercisename"];
// ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"exercisename"];
ExcName.numberOfLines=3;
[customcell.contentView addSubview:ExcName];
return customcell;
}
return nil;
}
now I want if any dictionary is null, cell corresponding to that dictionary should not be visible which is visible now.
A few things:
Don't return nil from -tableView:cellForRowAtIndexPath:. That method gets called exactly once for each (visible) row, and the table assumes it has as many rows as you told throught the return value of -tableView:numberOfRowsInSection:. Defaulting to nil reveals an inconsistency in your code.
By that token, cell dequeueing should never fail. I recommend that you switch to the newer method: -dequeueReusableCellWithIdentifier:forRowAtIndexPath:. It takes care of allocation of new cells for you when there is no cell in the reuse pool.
Cells are being reused. A single cell may be reuse multiple times: If you add a new label each time, they will pile up. Instead, add the label to your cells in interface builder and set up an outlet to reference them when configuring the cell.
The Solution:
Instead of "skipping rows", make your data model consistent with what you are going to display. If you have an array with objects some of which you want to skip, first filter your array and then used the filtered array as data source.
// Call this method once before reloading the table view
- (void) filterDictionaries
{
// (filteredArray is an ivar defined as NSMutableArray*)
for (NSDictionary* dictionary in exerciseAr) {
if ([dictionary objectForKey:#"exercisename"] != nil) {
[filteredArray addObject: dictionary];
}
}
// Now you can use filteredArray as the data source
// instead of exerciseAr, without worrying about skipping
// entries.
}
- (NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger) section
{
return [filteredArray count];
}
- (UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
static NSString *cellIdentifier = #"cellIdentifier1";
static NSString *customCellIdentifier = #"cellIdentifiercustom";
UITableViewCell* cell;
if (indexPath.row == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forRowAtIndexPath:indexPath];
}
else{
cell = [tableView dequeueReusableCellWithIdentifier:customCellIdentifier forRowAtIndexPath:indexPath];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.excLabel.text = [[filteredArray objectAtIndex:indexPath.row]
return cell;
}
It actually depends what you meant by your dictionary being null. It may be nil, or it just doesn't have any values for the keys e.g the values for the key is an empty string. So, to add to NibolasMiari's answer, I would say, check both of the cases.
- (void) filterDictionaries:(NSArray*) exerciseAr{
filteredArray = [[NSMutableArray alloc]init];
for (NSDictionary* myDict in exerciseAr) {
if ([myDict isKindOfClass:[NSNull class]]) {
continue;
}
else if ([myDict count] == 0) {
continue;
}
else{
[filteredArray addObject: myDict];
}
}
}
- (NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger) section
{
return [filteredArray count];
}
Now in your cellforIndexPath method everything will be same just changed the lines following way-
In the if block, you had
ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"activityname"];
Make it -
ExcName.text=[[filteredArray objectAtIndex:indexPath.row] objectForKey:#"activityname"];
In the else block, you had
ExcName.text=[[exerciseAr objectAtIndex:indexPath.row] objectForKey:#"exercisename"];
Make it -
ExcName.text=[[filteredArray objectAtIndex:indexPath.row] objectForKey:#"exercisename"];
I'm trying to set the title and display a button in my custom cell object in my second table view controller, but only my first dynamic prototype cell is displaying text.
The first prototype cell is UITableViewCell and the second is the custom object. In my first view controller I implement almost the same exact method and it works completely fine. Not sure where my error could be, the models I use have all their data in them. I have the correct cell identifier in my storyboard, and the connection from custom cell to the UIButton is set.
Thanks in advance.
Method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
static NSString *QuestionSectionIdentifier = #"QuestionSectionCell";
if (indexPath.section == 0)
{
UITableViewCell *cellQuestion = [tableView
dequeueReusableCellWithIdentifier:QuestionSectionIdentifier];
if (cellQuestion == nil) {
cellQuestion = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:QuestionSectionIdentifier];
}
if (indexPath.row == 0) {
cellQuestion.textLabel.text = _headerQuestion.question;
NSLog(#"question here %#", cellQuestion.textLabel.text);
}
return cellQuestion;
}
else
{
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (indexPath.section == 1)
{
if (indexPath.row == 0)
{
[cell.firstButton setTitle:_headerQuestion.questionAuthor.fullName forState:UIControlStateNormal];
[cell.firstButton setTag:_headerQuestion.questionAuthor.userID];
[cell.answerLabelField setHidden:YES];
NSLog(#"section 1 %#", cell.firstButton.titleLabel.text);
}
}
else if (indexPath.section == 2)
{
Answer* answer_num = [_headerQuestion.answers objectAtIndex:indexPath.row]; //object at index
[cell.firstButton setTitle:answer_num.answerAuthor.fullName forState:UIControlStateNormal];
[cell.firstButton setTag:answer_num.answerAuthor.userID];
cell.answerLabelField.text = answer_num.answer;
}
return cell;
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 3;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0)
{
return 1;
}
if (section == 1)
{
return 1;
}
else
{
return [_headerQuestion.answers count];
}
}
Make sure you register both reuse identifiers with the tableview using
- (void)registerClass:(Class nullable)cellClass
forCellReuseIdentifier:(NSString * nonnull)identifier
Or registerNib if you have a nib for the cell.
I have a table view that is being populated by the contents of my array _stations.
In total I have over 50 stations (that are automatically fetched via XML), each of course gets its own cell row with a label for its name. The problem I am running into now, is that I have manually added another item to my _stations, so this particular item I would like to stand out from the rest, Ideally I'd like for it to look a little different, perhaps a semi transparent .5 alpha greenColor on the background, and everything else the same.
To do so, I have tried to implement the following code:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
StationCell* cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (_stations.count == 1) {
// THIS BEING THE SPECIFIC CELL that I have manually added.
cell.backgroundColor = [UIColor greenColor];
}
else {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.backgroundColor = [UIColor clearColor];
Station* station = [_stations objectAtIndex:indexPath.row];
delegate* appDelegate = (delegate*)[UIApplication sharedApplication].delegate;
if([station.streamURL isEqualToString:[((AVURLAsset*)(appDelegate.mainPlayer.currentItem.asset)).URL absoluteString]])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
cell.titleLabel.text = station.name;
cell.bitrateLabel.text = [NSString stringWithFormat:#"%# kbps", station.bitRate];
}
return cell;
}
But when I run the app, none of the customization is on the specific cell... Or any cell at all for that matter, but if I replace clearColor with greenColor then it changes all of my cells to greenColor (understandably so)...
How can I customize this specific cell that is sitting in my array in position 1?
I don't think the if condition you have used is correct in your case.
if (_stations.count == 1) {
// THIS BEING THE SPECIFIC CELL that I have manually added.
cell.backgroundColor = [UIColor greenColor];
}
_stations.count will always be 50 or more as you have mentioned in your question. So this condition will never be met.
To modify the first cell of your table you need to use indexpath of the tableview.
Try replacing your if condition with this.
if (indexpath.row == 0) {
// THIS BEING THE SPECIFIC CELL that I have manually added.
cell.backgroundColor = [UIColor greenColor];
}
indexpath.row gives you the index of the cell in that tableView. If its the the first cell then just modify it.
try this . . .
(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
StationCell* cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell";
}
if (_stations.count == 1) {
// THIS BEING THE SPECIFIC CELL that I have manually added.
cell.backgroundColor = [UIColor greenColor];
}
else {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.backgroundColor = [UIColor clearColor];
Station* station = [_stations objectAtIndex:indexPath.row];
delegate* appDelegate = (delegate*)[UIApplication sharedApplication].delegate;
if([station.streamURL isEqualToString:[((AVURLAsset*)(appDelegate.mainPlayer.currentItem.asset)).URL absoluteString]])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
cell.titleLabel.text = station.name;
cell.bitrateLabel.text = [NSString stringWithFormat:#"%# kbps", station.bitRate];
}
return cell;
My problem is similar as this:
But I want to show accessoryView with DetailDisclosureButton only for cells that have details.
I have written some codes, but I donĀ“t know in which method to put it.
if (totalcount == 0 && totalcount2 == 0)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
else
{
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
totalcount and totalcount2 is count of two mutable arrays that tell me whether cells have details or not.
Please ask me if my question is not clear, thank you.
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString* identifierString;
if(totalCount == 0 && totalCount2 == 0) {
identifierString = #"0";
} else {
identifierString = #"1";
}
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:identifierString];
if(cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:YOUR_CELL_STYLE
reuseIdentifier:identifierString] autorelease];
if ([identifierString intValue] == 0) {
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
//do whatever setup you need to do
}
return cell;
}
Though to me, it seems that if totalCount and totalCount2 both equal 0, then all of the cells are going to have the same accessory type. Is that what you what? If not, you might want to rethink your logic.
You should do that in tableView:cellForRowAtIndexPath:, while you're setting up your UITableViewCell
add this to tableView:cellForRowAtIndexPath: after the cell has been dequeued or initialised
You can use this library . with MSCellAccessory, UITableViewCell's accessoryType can easily customizing the colors. support Flat Design same as iOS7.
https://github.com/bitmapdata/MSCellAccessory
This is the abridged code for my cellForRowAtIndexPath UITableView delegate method:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"blahblahblah"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"blahblahblah"] autorelease];
}
cell.textLabel.text = #"";
cell.detailTextLabel.text = #"";
cell.backgroundView = NULL; //problem here
// Initialize cell
//blah blah blah
//now to the good part...
if(indexPath.section == 1) {
cell.backgroundView = deleteButton;
cell.userInteractionEnabled = YES;
cell.textLabel.text = nil;
cell.detailTextLabel.text = nil;
}
else if(indexPath.section == 0) {
NSLog(#"section: %i row: %i", indexPath.section, indexPath.row);
switch (indexPath.row) {
case 0:
cell.textLabel.text = #"foobar";
//more stuff
break;
//lots more cases
default:
break;
}
}
return cell;
}
My problem is that the first cell in section 1 (section 0 has 10 cells, section 1 has only 1 cell) is getting assigned the information that is only supposed to be assigned to cell 0 of the first section. So, instead of getting the deleteButton background and etc, it gets the label title "foobar". I'm not really sure why this is happening, because my if statements are pretty clear. Any ideas?
Edit: setting the backgroundView to NULL causes those cells with text to, when they leave the view, come back without any background. So that isn't a viable solution. Also, the text for the detailTextLabel is still set on the cell that shouldn't have any text.
This is how it looks, with the cell backgroundViews set to nil and the text showing up on the delete cell where it shouldn't:
Solution, as recommended by Alex Deem (replacing old dequeue code with this code):
NSString* identifier;
if(indexPath.section == 0)
identifier = #"0";
else
identifier = #"1";
UISwitch *switchView;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier] autorelease];
You should read the documentation regarding the reuse of cells.
You should be using a different reuseIdentifier for each of the two sections, since they are fundamentally differently styled cells.
add a closing } bracket to these lines of code:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"blahblahblah"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"blahblahblah"] autorelease];
}
....
....
....
if(indexPath.section == 1) {
....
....
}
if(indexPath.section == 0) {
....
....
}
and I suspect you will have better results in your table.
The way things are working right now (at least as far as I can tell in your code), you create one cell and it gets initialized to something. It never gets reset to the values & data of anything else that's being requested.
Your code is structured wrong.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"blahblahblah"];
if (cell == nil)
dqueueReuseableCellWithIdentifier will return an existing previously created cell if it is no longer in use. If cell == nil you should create a new cell and set defaults common to all cells. However any setting of data unique to that indexPath should be done after the
if(cell==nil) block.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"blahblahblah"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"blahblahblah"] autorelease];
}
cell.textLabel.text=#"unique text";
return cell;