2 NSArrays in one UITableView with Label as subView? - objective-c

I'm having a problem with my current App. It has one UITableView in the UIViewController. I have one UIButton at the bottom (out of the UITableView). It works in that way:
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"bla"]) {
[[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:#"bla"];
[tableView reloadData];
} else {
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:#"tasks2do"];
[tableView reloadData]; }
This worked when I had the cell.textLabel.text Method in this way:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ident = #"indet";
cell = [tableView dequeueReusableCellWithIdentifier:ident];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:ident] autorelease];
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"bla"]) {
cell.textLabel.text = [firstArray objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [secondArray objectAtIndex:indexPath.row];
}
return cell; }
Now I want to use an UILabel instead of cell.textLabel, because I need it for some reasons (eg. setting the labels frame)
For that I used the following code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ident = #"indet";
cell = [tableView dequeueReusableCellWithIdentifier:ident];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:ident] autorelease];
}
UILabel *thislabel = [[[UILabel alloc] initWithFrame:CGRectMake(10, 10, 250, 44)] autorelease];
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"bla"]) {
[thislabel setText:[firstArray objectAtIndex:indexPath.row]];
} else {
[thislabel setText:[secondArray objectAtIndex:indexPath.row]];
}
[cell.contentView addSubview:thislabel];
return cell; }
That works fine, until I push the UIButton for switching. It switches, the cell shows the new text but behind the new text is still the old text as you can see here:
http://d.pr/Rqx2
(the firstArray contains the letter L, the secondArray contains the Letter J, it mixes up both up)
Do you have any idea for solving this problem since I tried some stuff (for example using 2 UILabels for the arrays and hide one)? Would be cool. :)
I hope my English is not too bad to understand, my English skills for writing aren't the best, I'm sorry for that.
If you need further information / code just post it, shouldn't be a problem.

I recommend you create a UITableViewCell subclass in which you configure the label (set frame and add it as subview in UITableViewCell's initializer). Add a property for setting the text in the label and write a setter like this for the property:
- (void)setLabelText:(NSString *)newLabelText
{
if ([self.labelText isEqualToString:newLabelText]) return;
[labelText autorelease];
labelText = [newLabelText copy];
self.label.text = labelText;
[self.label setNeedsDisplay]; // or perhaps [self setNeedsDisplay];
}
Edit: by the way, the issue you're dealing with is the caching. You recreate a new label every time a cell comes in view, even if the cell already had a label before. This happens because you initialize an UILabel outside the UITableViewCell initializer (which only should be called once for every cached cell, afterwards it can be retrieved from cache, including all it's subviews).

Related

How to indicate new message in UITableViewCell

I have a problem which should be a trivial one but I still can't find a good solution to it.
I have a tableView in which I store cells representing different threads/chatrooms. I have an NSArray with data for cells/chats.
I want to display a badge (with a custom badgeCell) indicating a new message on a corresponding cell.
When I receive a push notification my method searches through the cells and finds indexPath for a cell connected with that new message and adds it to NSArray. Then I reload the tableView.
In tableView:willDisplayCell:forRowAtIndexPath: I check if the cells' indexPath is inside that NSArray of indices and if so I display a badge.
Everything works just fine until I scroll tableView so the cell is not visible. When it comes back again it doesn't have any badge.
Also if my cell is not visible when the Push Notification comes it also doesn't display the badge.
I know it has something to do with ReuseIdentifiers but I can't think of any good solution. Please help me !
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier1 = #"MyCell";
ProblemsTableViewCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if(!cell) {
cell = [[ProblemsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier1];
}
if ([cell respondsToSelector:#selector(setSeparatorInset:)]) {
cell.separatorInset = UIEdgeInsetsZero;
}
[cell setSelectionStyle:UITableViewCellSelectionStyleGray];
cell.delegate = self;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell.contentView.superview setClipsToBounds:NO];
[cell.contentView setClipsToBounds:NO];
cell.clipsToBounds = NO;
return cell;
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(nonnull UITableViewCell *)cell forRowAtIndexPath:(nonnull NSIndexPath *)indexPath{
ProblemsTableViewCell * Cell = (ProblemsTableViewCell*)cell;
/* … some customization - > backgroundColor/images etc. … */
for(NSIndexPath* index in self.newMessages){
if(indexPath.row == index.row){
Cell.badgeString = #"new message"; // the _badge property is a built in view of a custom Cell class with a badge.
Cell.badgeColor = [UIColor whiteColor];
Cell.badgeColorHighlighted = [UIColor whiteColor];
Cell.badge.radius = 0;
Cell.badge.fontSize = 15;
Cell.badge.layer.opacity = 0.55f;
NSLog(#"update 0");
break;
}
}
}
Instead of trying to maintain an array of indices, use the array with cell data. Add an indicator for each element in there to say whether a badge is needed or not and use it to turn the badge on or off during cellForRowAtIndexPath.

Multiple checkbox in tableview iOS Objective-C

Im working on Multiple checkbox Functionality in tableview, I have to search in google finally i got it one solution.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
if ([selectedRowsArray containsObject:[contentArray objectAtIndex:indexPath.row]]) {
cell.imageView.image = [UIImage imageNamed:#"checked.png"];
}
else {
cell.imageView.image = [UIImage imageNamed:#"unchecked.png"];
}
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleChecking:)];
[cell.imageView addGestureRecognizer:tap];
cell.imageView.userInteractionEnabled = YES; //added based on #John 's comment
//[tap release];
cell.textLabel.text = [contentArray objectAtIndex:indexPath.row];
return cell;
}
- (void) handleChecking:(UITapGestureRecognizer *)tapRecognizer {
CGPoint tapLocation = [tapRecognizer locationInView:self.tableView];
NSIndexPath *tappedIndexPath = [self.tableView indexPathForRowAtPoint:tapLocation];
if ([selectedRowsArray containsObject:[contentArray objectAtIndex:tappedIndexPath.row]]) {
[selectedRowsArray removeObject:[contentArray objectAtIndex:tappedIndexPath.row]];
}
else {
[selectedRowsArray addObject:[contentArray objectAtIndex:tappedIndexPath.row]];
}
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:tappedIndexPath] withRowAnimation: UITableViewRowAnimationFade];
}
In that code i have one doubt. what is selectedRowsArray and what is ContentArray Please explain anyone. Thanks in advance.
The selectedRowsArray is another array maintained beside your contentArray which is used to hold the content for of the cells that are currently selected in the UITableView. The contentArray holds the actual content which is populated the cells.
Make note of the following lines:
if ([selectedRowsArray containsObject:[contentArray objectAtIndex:indexPath.row]])
{
cell.imageView.image = [UIImage imageNamed:#"checked.png"];
}
else
{
cell.imageView.image = [UIImage imageNamed:#"unchecked.png"];
}
The cell contents are checked for their presence in the selectedRowsArray which means that the cell is selected.
The selection and deselection is handled by the handleChecking: method which adds or removes the object from the same index in the contentArray into the selectedRowsArray.
You must have understood by now that cells in a UITableView are reused, so the actual number of UITableViewCells in the memory are most probably less than the number of cells to be shown in the UITableView. Hence the need for a system which can actually determine whether the cell is currently selected so this status can be populated when the cell is rendered again in the view.
Alternative methods to achieve the same involve using Booleans in an array representing the status of each cell, or simply the indexes of each selected cell in an array.

why UITableView is showing data when I touch it?

I have a UITableView loading some tweets, all works nicely but when I populate the table it doesn't enter to the CellForRow method til I touch the table view.... does somebody know how to solve this?
tab = [[UITableView alloc] initWithFrame:CGRectMake(10, 300, 400, 200) style:UITableViewStylePlain];
tab.dataSource = self;
tab.delegate = self;
then...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"entra a cellula");
Tweet *t = [tweets objectAtIndex:indexPath.row];
static NSString *MyIdentifier = #"MyIdentifier";
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil) {
// Use the default cell style.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:MyIdentifier];
}
cell.textLabel.text = t.screenName;
cell.detailTextLabel.text = t.text;
cell.imageView.image = t.profileImage;
return cell;
}
Best guess based on what we know is that you aren't adding the table as a subview on the main thread. This means that redrawing doesn't occur until you scroll. Try wrapping the addsubview call in a GCD call like so:
dispatch_async(dispatch_get_main_queue(), ^() {
[self addSubview:tab];
});
When you finished loading your tweets call this method:
[tab reloadData];
to reload your tableView data source.

Reloading searchResultsTableView

I have, in my main tableView, some custom cells (cells with an imageView, basically).
The imageView is set only if a value on my plist is false. Then, when it's true, the imageView is nil.
Basically when the user enters the detailView, the value is set to YES and the cell.imageView is nil.
And it's okay, it works
I'm using a searchDisplayController, when i search for something that has a cell.imageView, going into the detailView and then coming back to the searchResultsTable, the cell has still the image, while it shouldn't, but the main tableView has the correct cell (so, with no image).
I thought that it could depend on searchResultsTableView, but i'm not sure.
I tried with
[self.searchDisplayController.searchResultsTableView reloadData];
with no effect.
How could i reload the searchResultsTableView so that it shows the right cells, those with the image and those that don't have the image anymore?
Any help appreciated!
EDIT
This is my cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSArray *rows;
if (tableView == self.searchDisplayController.searchResultsTableView) {
rows = filteredList; //for search
} else {
NSDictionary *section = [localSortedTips objectAtIndex:indexPath.section];
rows = [section objectForKey:#"Rows"];
}
NSDictionary *item = [rows objectAtIndex:indexPath.row];
cell.textLabel.text = [item objectForKey:#"name"];
if ([[item valueForKey:#"isRead"] boolValue] == NO) {
cell.imageView.image = [UIImage imageNamed:#"unread.png"];
} else {
cell.imageView.image = nil;
}
cell.textLabel.font = [UIFont boldSystemFontOfSize:15.0];
cell.textLabel.adjustsFontSizeToFitWidth = YES;
return cell;
}
If I understood you right, then you can have a workaround but searching again with the same search string:
if (self.searchDisplayController.active) {
self.searchDisplayController.searchBar.text = self.searchDisplayController.searchBar.text;
}
put it in viewWillAppear: or viewDidAppear: which will be called each time the view is shown up (eg. you go back from the detail view to your search view). And reloading the data in this place would be nice too, to get the right data (for example if you marked the cell as read like in your sample code)
Just [self.tableView reloadData]; and not the searchResultsTableView (it will be automatically use the updated data after the new search)
It sounds as if perhaps your cells are being recycled and not reset properly. UITableView uses view recycling, so it's important that if you do something such as set an image you make sure it is explicitly set even when their isn't an image to display.
If you share your cellsForRowAtIndexPath code you might be able to get some more help.

Animating a font size change in a UITableView

Hey Simple little app for educational purposes, its a simple uitableview and I want users to be able to use a uislider to adjust the font size as needed. The code I have now works to change the font but only when the view is updated, ie when i pull up or down the table view to reveal other cells. I'd like the font change to be reflected immediately as a user moves the uislider if possible, here's the code that I have working half way:
-(UITableViewCell *) tableView: (UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: SimpleTableIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier: SimpleTableIdentifier] autorelease];
NSUInteger row = [indexPath row];
cell.text = [listData objectAtIndex:row];
UIImage *image = [UIImage imageNamed:#"star.png"];
cell.font = [UIFont systemFontOfSize:[fontSlider value]];
cell.image = image;
return cell;
}
You could make an IBAction for the fontSlider for the "Value Changed" option as follows:
-(IBAction) refreshResize
{
[self.view setNeedsDisplay];
}
That should work. setNeedsDisplay refreshes the screen.