UITableView design - iphone

I have UITableView where I want design as below.
For this I have images as below.
bottomRow.png
middleRow.png
topAndBottomRow.png
topRow.png
For this I have used below code inside -(UITableViewCell *) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UIImage *selectedImage;
if (indexPath.row==0) {
selectedImage = [UIImage imageNamed:#"topRow.png"];
} else if (indexPath.row == ([products count]-1)) {
selectedImage = [UIImage imageNamed:#"bottomRow.png"];
} else {
selectedImage = [UIImage imageNamed:#"middleRow.png"];
}
if ([products count]==1) {
selectedImage = [UIImage imageNamed:#"topAndBottomRow.png"];
}
UIImageView *selectedBackgroundImageView = [[UIImageView alloc] initWithImage:selectedImage];
cell.selectedBackgroundView = selectedBackgroundImageView;
selectedBackgroundImageView = [[UIImageView alloc] initWithImage:selectedImage];
cell.backgroundView = selectedBackgroundImageView;
Now, everything works perfect.
BUT my designer is insisting on below point.
On tableview, I can have 4 cells at one time. Now let's say I have 6 rows.
Now when I have 6 rows (and tableview can show only 4), the 4th row shows bottomRow.png which is obvious. But my designer is insisting, even the tableview is scrolled, you should have same design for all 4 rows.
Edit 1
First of all, sorry for not being clear.
Well when the UITableView loads, I can only see first 4 cells even there are 6 cells because I have set the height of tableview accordingly. To see rest 2 cells, I have to scroll down. I believe this is how table view works.
Now let's say there are only 4 records. For 4 records, table view looks like image I have.
Now when I have tableview size as 6 (with id as 1-6), the fourth row gets image middleRow.png. Here what my designer wanted is to see bottomRow.png.
Now let's say I scroll down. Now I see row with cell id as 3-6. Now cell with id 3 have middleRow.png, but my designer wanted to see topRow.png.
I know this is ugly, but this is what my designer wanted to see.
Any suggestions to get this done?

One way to achieve your goal: Use numberOfRows to find out if this cell is the last cell.
In your cellForRowAtIndexPath:
if (indexPath.row==0) {
selectedImage = [UIImage imageNamed:#"topRow.png"];
.....
else if (indexPath.row == [tableView numberOfRowsInSection:indexPath.section]-1){
selectedImage = [UIImage imageNamed:#"bottomRow.png"];
}
EDIT:
Sorry, I misunderstood your question, I have another proposal that I think you may try...
customCell, with a custom method to set its image. For example,
[customCell setSelectedImage: [UIImage imageNamed:#"middleRow.png"];
2.
In cellForRowAtIndexPath, you can set all cells to be: middleRow.png
3.
After loading of tableView, run a checking method using [self.tableView visibleCells];
eg
- (void) setImageForTopAndBottomCells{
CustomCell *topCell = [[self.tableView visibleCells] objectAtIndex: 0];
CustomCell *bottomCell = [[self.tableView visibleCells] objectAtIndex: self.tableView.visibleCells.count-1];
[topCell setSelectedImage: [UIImage imageNamed:#"topRow.png"];
[bottomCell setSelectedImage: [UIImage imageNamed:#"bottomRow.png"];
}
If your tableView is scrollable, set your ViewController as a UIScrollView delegate, in your delegate method scrollViewDidScroll, run the setImageForTopAndBottomCells method again.
There could be better ways for achieving what you want than the one I proposed, let me know if you found one.

What about using a combination of scrollViewDidScroll:, indexPathsForVisibleRows and reloadRowsAtIndexPaths:withRowAnimation: with something like this:
Every time the table view scrolls you get the list of visible rows using the UIScrollViewDelegate method scrollViewDidScroll:
If it scrolled enough to change the background, call the UITableView's reloadRowsAtIndexPaths:withRowAnimation: passing all the index paths that needs new background
Instead of using indexPath.row == 0 to find the top you would use [indexPath isEqual:[tableView indexPathsForVisibleRows][0]] and the same thing for the other rows
Hope this helps.

You can use middlerow.png as background view of your UITableViewCell in CellForRowAtIndexPath method. Below is the code
cell.backgroundView = [[UIImageView alloc] initWithImage[[UIImageimageNamed:#"middlerow.png"]stretchableImageWithLeftCapWidth:0.0 topCapHeight:5.0]];
And after this you can change the Corner radius (add border color/width) of your table view. But for this first you will have to add QuartzCore framework.
self.table.layer.borderColor=[[UIColor colorWithRed:209.0/255 green:209.0/255 blue:209.0/255 alpha:1.0] CGColor];
self.table.layer.borderWidth =3.0;
self.table.layer.cornerRadius =10.0;
Hope this helps.

You can Use this:
NSArray *visible = [tableView indexPathsForVisibleRows];
NSIndexPath *indexpath = (NSIndexPath*)[visible objectAtIndex:0];
-(NSArray *)indexPathsForVisibleRows returns an array of index paths each identifying a visible row in the receiver.So once you get the visible rows, you can use the required image according to indexpath.row.

Related

Subview of a reused table view cell is nil for a specific row only

I've been trying to resolve this issue for quite some time now and I still can't figure it out. What I have is a custom table view cell in story board. In the cell, I added 6 views and each view has an imageView subview. I set the views' tag so that I can access them later on. This table serves as a thumbnail view in my app. The problem is that, in a specific row, the last thumbnail container view no longer have the imageview as subview resulting to a crash.
Below is my code in setting up the images for the table. Any help would be greatly appreciated. Thank you!
NSString *CellIdentifier = #"ThumbnailCell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//loops through each thumbnails
for (int i=1; i<=kNumberOfThumbnails; i++)
{
//get index of current thumbnail- starting value is 0
int index=((indexPath.row *kNumberOfThumbnails)+i)-1;
NSLog(#"index %d",index);
//create an indexpath given the computed index and cell section
NSIndexPath *currentIndexPath=[NSIndexPath indexPathForRow:index inSection:indexPath.section];
//get number of sections
NSArray *sections = [self.fetchedResultsController sections];
NSInteger count=0;
id <NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:indexPath.section];
//get number of objects for given section
count = [sectionInfo numberOfObjects];
NSLog(#" i: %d number of subviews ni thumn container %d",i,thumbContainer.subviews.count);
//get view container for thumbnails
UIView *thumbContainer=(UIView *)[cell.contentView viewWithTag:i];
UIImageView *imageView=[thumbContainer.subviews objectAtIndex:0];// this is where the app crashes.. thumbContainer no longer have a subview (for a specific row only)so it throws out an nsrangeexception
if (index<count)
{
//get file using the created indexpath
File *imageFile=[self.fetchedResultsController objectAtIndexPath:currentIndexPath];
//set image
imageView.image=[UIImage imageNamed:imageFile.thumbnail];
thumbContainer.backgroundColor=[UIColor grayColor];
//set tag for image view for the system to know what file is tapped
imageView.tag=(currentIndexPath.section*kImageTagMultiplier)+currentIndexPath.row;
//add tap gesture to thumbnail container view
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(resultTapped:)];
tap.numberOfTapsRequired=1;
[thumbContainer addGestureRecognizer:tap];
}
else {
imageView.image=[UIImage imageNamed:#""];
thumbContainer.backgroundColor=[UIColor clearColor];
for (UIGestureRecognizer *gest in thumbContainer.gestureRecognizers) {
[thumbContainer removeGestureRecognizer:gest];
}
}
return cell;
What I'm trying to do here is that I have an array of objects and each object is represented by a thumbnail image, i used a tableview to show these thumbnails.., for each row there is a fixed number of thumbnails. In the storyboard I have a cell that has 6 square views and each view has an imageview inside. The reason why i added a container view instead of just putting an imageview is that each thumbnails is tappable and i need to know what object is tapped by getting the tapgesture's view's(the container view) subview's(which is the imageview) tag.
By the way, I have put a comment where i encounter a crash
You should use the new iOS 6 UICollectionView as it is designed for things like this.

UITextField artifacts

I have an iPhone app which has a Table View-based data input screen with a toggle, which when on shows all rows in another section of the table.
Sometimes, when the app is first loaded, and usually when it has been fully deleted from the phone, the UITextFields from the original section of the table are displayed in addition to the new rows, as below (main table section is above)
:
THe strangest thing about this is that this behaviour only occurs the first time this screen is displayed - it seems fine after that. Oh, and it only seems to occur on the phone, not the simulator.
Given the random nature of this, could it have something to do with other apps? I did have apps with similar namespaces running at the same time. The problem seemed to go away after I closed the apps down / deleted from phone.
I have included the code block that is run when the switch is changed below:
- (void)accountSwitchChanged {
[customerDetails saveField:#"addToAccount" WithBool:addToAccountSwitch.on];
NSArray *indexes = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:1 inSection:1], [NSIndexPath indexPathForRow:2 inSection:1], [NSIndexPath indexPathForRow:3 inSection:1],nil];
if (addToAccountSwitch.on)
[detailsTable insertRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationFade];
else
[detailsTable deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationFade];
}
Any ideas?
--EDIT
cellForRowAtIndexPath code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// get rid of grey background for iPad app
[tableView setBackgroundView:nil];
UITableViewCell *cell = nil;
NSDictionary *cellData = [[dataSourceArray objectAtIndex: indexPath.section] objectAtIndex:indexPath.row];
id cellControl = [cellData objectForKey:kCellControlKey];
static NSString *kCellControl = #"CellControl";
cell = [tableView dequeueReusableCellWithIdentifier:kCellControl];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellControl] autorelease];
cell.textLabel.textColor = [[[ConfigManager sharedInstance].skin valueForKey:#"tableCellHeadingTextColour"] toUIColor];
cell.backgroundColor = [[[ConfigManager sharedInstance].skin valueForKey:#"tableCellBgColour"] toUIColor];
cell.textLabel.font = [UIFont boldSystemFontOfSize:17];
} else {
// a cell is being recycled, remove the old control (if it contains one of our tagged edit fields)
UIView *viewToCheck = nil;
viewToCheck = [cell.contentView viewWithTag:kDetailsViewTag];
if (!viewToCheck)
[viewToCheck removeFromSuperview];
}
// if control is not a text field, make it a button
if (![cellControl isKindOfClass:[UITextField class]])
cell.selectionStyle = UITableViewCellSelectionStyleGray;
else
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.text = [cellData objectForKey:kCellTitleKey];
[cell.contentView addSubview:cellControl];
return cell;
}
Something to check and eliminate If it is only occurring the first time view is displayed, it's always worth checking whether you are assuming the view is loaded before you are doing work on it. This doesn't come up very often, but when it does it can be very hard to track down. To check whether this might be your issue, add a [viewController view] call just after you create the view for the first time. This forces the view to be loaded. If the issue goes away when you add this, you've tracked the source of the issue. And you can even leave the [viewController view] call in as a fix, or work through your code to allow for lazy instantiation.
All this said, far more likely to be something funny in your tableView:cellForRowAtIndexPath: code. If you want good stack overflow, post all or relevant fragment of the that code (or the relevant code it calls).
Follow up on posted code:
(1) if (!viewToCheck) [viewToCheck removeFromSuperview] won't do anything. It'll only send a removeFromSuperview message when viewToCheck is nil and that won't do anything. Not sure if this will be key issue, but it's something to put right.
(2) [cell.contentView addSubview:cellControl]; - this looks problematic - it's usually best to only add subviews when you create the cell. You can hide or show in the main body of tableView:cellForRowAtIndexPath: - that would, I suspect, work for you here. With this code there's a risk here that you'll either add multiple subviews when you only want one or (which may be what's going on here) that you end up not removing a subview when a cell is recycled. If the viewToCheck code is supposed to be removing the added cellControl then, as (1), it won't do that just now because your if condition is wrong.

When an UITableView is empty, show an UIImage

This is related to another question of mine which wasn't answered in a helpful way (message when a UITableView is empty).
I'm trying to show an UIImage graphic that says You haven't saved any bookmarks over an UITableView when it's empty. I have NSNotification set-up so that when bookmarks are added or deleted, a message is sent so that the UITableView can be updated.
I've been trying to do it with this code. Why won't this work?
- (void)bookmarksChanged:(NSNotification*)notification
{
[self.tableView reloadData];
UIImageView* emptyBookmarks = [[UIImageView alloc] initWithFrame:CGRectMake(75, 100, 160, 57)];
emptyBookmarks.alpha = 1;
emptyBookmarks.image = [UIImage imageNamed:#"emptyBookmark.png"];
[self.view addSubview:emptyBookmarks];
[emptyBookmarks release];
if ([self.dataModel bookmarksCount] == 0)
{
emptyBookmarks.alpha = 1;
}
else
{
emptyBookmarks.alpha = 0;
}
}
I'm probably approaching this the wrong way... But if salvageable, what am I doing wrong?
When I initially have an empty bookmarks tableview, there's no image displayed. After I add a bookmark and then delete it, the image shows. Grrh.
Another way (and IMO the correct way) to do this is to manipulate the backgroundView property on the UITableView.
While making a single cell with a custom image cell would certainly works, I think it overly complicates the logic of your UITableViewController's data source. It feels like a kludge.
According to UITableView documentation:
A table view’s background view is automatically resized to match the
size of the table view. This view is placed as a subview of the table
view behind all cells , header views, and footer views.
Assigning an opaque view to this property obscures the background color
set on the table view itself.
While you probably don't want to just set it to your UIImageView, it is very easy to make a UIView that contains the UIImageView that you want.
Well first off if you were going to do it that way, you would need to reload the tableView after updating the image or model etc. and not before.
But you are probably making things more complicated than they need to be!
Why not just check to see if the data for section 0 and indexPath.row 0 are empty and if so in cellForRowAtIndexPath display a text message accordingly.
// First make sure there is always one row returned even if the dataModel is empty.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger numRows = 0;
if ([self.dataModel lastObject]) {
// Return the number of rows in the section.
numRows = [self.dataModel count]; // etc.
}
if (numRows < 1) numRows = 1;
return numRows;
}
// Then display the data if there is some, otherwise a message if empty.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if ([self.dataModel lastObject]) {
// setup the cell the normal way here.
} else { // the datasource is empty - print a message
cell.textLabel.text = nil;
cell.detailTextLabel.text = NSLocalizedString(#"You haven't saved any bookmarks", #"");
cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.7];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
Are you sure [self.dataModel bookmarksCount] is equal to 0 ?
While I agree that you are probably going about this the wrong way,
your image is allocated and added in your bookmark changed, your notification does not trigger when there are no bookmarks initially. Hence you don't see the image. Call the bookmar changed when your table view inits or appears.
Probably the best way to achieve this is to perform a check in your numberOfRowsInSection method to return 1 if your data source is empty. Then in cellForRowAtIndexPath check if your data source is empty and if it is, create a custom cell that contains whatever you want. In heightForRowAtIndexPath you need to return your custom cell height if your datasource is empty, but only if you want the cell larger than the default. At least that is how I would approach it.
when bookmarks count is nil add one to your row method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
int c;
c = bookmarks.count;
if(c == 0){
c = 1;
}
return c;
}
and then the same check again in your cellforrowatindexpath.
Another thing to be aware of in this situation is that if you're using core data and you're datasource is feeding off an entity, you will want to make sure your model matches. You can get some weird side-effect behavior in certain situations. This is especially true if you allow editing and core data has an empty model but you're tableview is still showing a cell.

How to add image in tableview in iphone

I have to add image in tableview in each. I have 2 list one is for twitter and second is for facebook. I want to show each list with their image icon. here is my code to attach value in table of twitter.
for (NSDictionary *wallText in sortedarray) {
NSString *wallNameValue = [wallText objectForKey:#"message"];
if(![[NSNull null] isEqual:wallNameValue] && [wallNameValue length])
{
[tableList addObject:wallNameValue];
}
}
I want to show twitter image in left of row. How will i add image.
Thanks in advance
Just try using in this Way:
cell.imageView.image = [UIImage imagedNamed:[wallText objectForKey:#"message"]];
& plus there is no need to run any loop for this.This is optimize your code.
Just access the array via indexpath.row
NSDictionary *wallText = (NSDictionary *)[[sortedarray objectAtIndex:indexPath.row]valueForKey:message];
Hope you are going to do all these things in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath .
In your table view's delegate, in the method that sets up your UITableViewCell's, do something like this:
cell.imageView.image = myImage;
// myImage is a UIImage *.
Every UITableViewCell comes with a UIImageView built in that displays an image for you on the left side of the cell.
Reference:
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UITableViewCell_Class/Reference/Reference.html
In cellForRowAtIndexPath just do
cell.imageView.image = [UIImage imageWithData:[yourImageDataArray objectAtIndex:indexPath.row]];

UITableViewCell caching its backgroundView property

I have a UITableView which makes use of cells with custom backgroundViews. I'm assigning the backgroundViews in tableView:cellForRowAtIndexPath:indexPath: as suggested here. In a nutshell, dependent upon the position of a UITableViewCell within its UITableView, I want to assign certain backgroundView images. Here's the code in question:
UIImage *rowBackground = nil;
if (row == 0 && row == sectionRows - 1) {
rowBackground = [UIImage imageNamed:#"row_bg_start_and_end.png"];
} else if (row == 0) {
rowBackground = [UIImage imageNamed:#"row_bg_start.png"];
} else if (row == sectionRows - 1) {
rowBackground = [UIImage imageNamed:#"row_bg_end.png"];
} else {
rowBackground = [UIImage imageNamed:#"row_bg.png"];
}
((UIImageView *)cell.backgroundView).image = rowBackground;
This works fine, and I see the results I'm expecting. I have issues, however, when it comes to the removal or addition of rows, which invariably means certain rows retain their previous backgroundViews, rather than recalculating their placement and updating the view in question.
I understand the purpose of dequeueReusableCellWithIdentifier and why this is happening. But I'm not sure how to go about fixing it correctly. I can tell that the rows are recalculated correctly when tableView:cellForRowAtIndexPath:indexPath: is called by scrolling them off screen, which results in their backgroundView being reset correctly.
Should I be setting the backgroundView property in willDisplayCell:forRowAtIndexPath: also? How should I handle this situation?
The easiest approach to fix the problem you're having is to reprocess the background images of all visible rows when you add new rows to a section.
i.e.
for (UITableViewCell *cell in aTableView.visibleCells)
{
NSIndexPath *cellIndexPath = [aTableView indexPathForCell:cell];
/* look at the cell.backgroundImage and if it's not appropriate
for the indexPath, update it */
}
You could carefully override all the UITableView methods that insert new rows and only check perform this work for the specific rows that need to be updated but I don't think this would be worth the effort.