I am still in the process of getting myself acquainted with the iPhone SDK.
This is what I am trying to do :
I have a UIScrollView and each scroll view has an UITableView and I have implemented a custom UITableViewCell.
The desired functionality is initially there is no selection, then the user selects the row and scrolls and makes another selection in the next scroll view and continues. I want the selection to stay, so the user can change the selection at a later point.
However what is happening in my case is - the first and the second UITableViews are fine, the selected row stays selected, but in the third UITableView I see a row "already" selected which is same as the first UITableView. I do not want to see any selected.
I know I am doing something stupid but cannot figure out what. Any help would be really appreciated.
Thanks,
Amy
Here are the relevant data source and delegate methods.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%s", __FUNCTION__);
static NSString * ChoiceTableCellIdentifier = #"ChoiceTableCellIdentifier";
choiceTableCell = (ChoiceTableCell *)[tableView dequeueReusableCellWithIdentifier:ChoiceTableCellIdentifier];
if( choiceTableCell == nil )
{
choiceTableCell = [[[ChoiceTableCell alloc]
initWithFrame:CGRectZero
reuseIdentifier:ChoiceTableCellIdentifier] autorelease];
}
return choiceTableCell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%s", __FUNCTION__);
int newRow = [indexPath row];
int oldRow = [lastIndexPath row];
if ( (newRow != oldRow) || (newRow == 0) )
{
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
UIImageView *indicatorN = (UIImageView *)[newCell.contentView viewWithTag:SELECTION_INDICATOR_TAG_1];
indicatorN.image = [UIImage imageNamed:#"selected.png"];
newCell.backgroundView.backgroundColor = [UIColor clearColor];
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:lastIndexPath];
UIImageView *indicatorO = (UIImageView *)[oldCell.contentView viewWithTag:SELECTION_INDICATOR_TAG_1];
indicatorO.image = [UIImage imageNamed:#"notSelected.png"];
oldCell.backgroundView.backgroundColor = [UIColor clearColor];
lastIndexPath = indexPath;
}
}
You are reusing a cell that has the image "selected.png".
In the method cellForRowAtIndexPath you have to "select" or "deselect" your cell depending your last selection. That is: if indexPath is equals to lastIndexPath you have to put the background "selected.png", else "noSelected.png".
When you reuse a cell, it keep its previous state, so you have to initialize it all.
Related
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.
I have UIView(LeftMenu) with UITableView (UITableViewStylePlain). I have around 7 controllers , Where on selection of each cell I wanna push corresponding controller. I tried custom highlight for cell with yellow color as below ,
UIColor *yel=[[UIColor alloc]initWithRed:240/255.0 green:197/255.0 blue:67/255.0 alpha:1.0];
UIView *bgColorView = [[UIView alloc] init];
[bgColorView setBackgroundColor:yel];
[cell setSelectedBackgroundView:bgColorView];
in cellForRowAtIndexPath. But I am unable to retain selected cell If I move to next controller. In didSelectRowAtIndexPath , I am capturing last selected index ( When I select new cell , old should be unhighlighted). But it seems , If I make custom its not retaining. If I keep UITableViewCellSelectionStyleNone , cell.backgroundColor it works. But not highlighting :(
[[NSUserDefaults standardUserDefaults]setInteger:indexPath.row forKey:#"SSLastSelectedLeftMenuIndex"];
I am initializing UIView(LeftMenu) with frame.
Issue : Gray Color on Highlight after few mins custom yellow highlight coming and Not retaining selected cell color.
I know I am missing some silly thing. But its eating my time. Advance thanks :)
Update:-
below is my -cellForRowAtIndexpath method
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if([[NSUserDefaults standardUserDefaults]integerForKey:#"SSLastSelectedLeftMenuIndex"]==indexPath.row)
{
///<HIGHLIGHT CODE>;
}
to select the last selectd cell .. (try)
- (void)viewWillAppear
{
[uper viewWillAppear];
//hear u can set the selected cell
//get indexpath of row
int k = your saved row
NSIndexPAth *path = [NSIndexPath indexPathWithIndex:k];//if u hav single section or u can use other class method
[tableView selectRowAtIndexPath:_selctedIndex animated:NO scrollPosition: UITableViewScrollPositionNone];//hear u are directly setting the last selected cell once view will appear
}
hop this helps u :)
if u are using UITableViewCell then u do something like this,for yellow color for selecting the cell
//in controller do like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
UIColor *yel=[[UIColor alloc]initWithRed:240/255.0 green:197/255.0 blue:67/255.0 alpha:1.0];
UIView *bgColorView = [[UIView alloc] init];
[bgColorView setBackgroundColor:yel];
[cell setSelectedBackgroundView:bgColorView];
}
return cell;
}
in the subclassed table cell there is one method, to display yellow color for selecting the cell
//in CustomCell.m
//in the custom cell set the color for selected state
//override this method
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
if(selected)
{
//same code of urs
UIColor *yel=[[UIColor alloc]initWithRed:240/255.0 green:197/255.0 blue:67/255.0 alpha:1.0];
UIView *bgColorView = [[UIView alloc] initWithFrame:self.bounds];//for entaire cell,set the frame
[bgColorView setBackgroundColor:yel];
[self setSelectedBackgroundView:bgColorView];
}
}
You have misunderstood selection and highlighting a cell you should change your cellForRowAtIndexPath like below to select the previously selected item.
before that Declare a globle variable _selctedIndex of type NSIndexpath to store the last selected cell
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
UIColor *yel=[[UIColor alloc]initWithRed:240/255.0 green:197/255.0 blue:67/255.0 alpha:1.0];
UIView *bgColorView = [[UIView alloc] init];
[bgColorView setBackgroundColor:yel];
[cell setSelectedBackgroundView:bgColorView];
}
if([[NSUserDefaults standardUserDefaults]integerForKey:#"SSLastSelectedLeftMenuIndex"]==indexPath.row)
{
//<HIGHLIGHT CODE>;
_selctedIndex=indexPath; //code Updated
}
return cell;
}
After your tableView has reloaded call the below line of code
[tableView selectRowAtIndexPath:_selctedIndex animated:NO scrollPosition:UITableViewScrollPositionTop];
}
The UITableViewCell class will assign the view you have mentioned to the property selectedBackgroundView instead of the Default blue color view for showing in the background when a cell is selected
I ran into the exact same problem in a project, when tapping on a cell long enough to engage highlight then moving to a scroll the UITableView holds onto the highlighted index somehow so that the next attempt to select uses that previous indexPath.
To resolve I did the following
Add Property to Track Offending indexPath
...
#property (nonatomic,strong) NSIndexPath *cachedPath;
...
Track indexPath with didUnhighlightRowAtIndexPath
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
self.cachedPath = indexPath;
}
On Drag reload offending indexPath
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if (self.cachedPath)
{
[self.tableView reloadRowsAtIndexPaths:#[self.cachedPath] withRowAnimation:UITableViewRowAnimationNone];
}
self.cachedPath = nil;
}
For good measure clear the property on selection
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.cachedPath = nil;
}
I am trying to use Storyboards on this project. I cntrl drag from a static tableview cell to a new viewcontroller select push.
When I run the app and click the tableviewcell, which I dragged from in the previous step, nothing happens?
I am not sure if I have screwed things up by also putting in my tableviewcontroller class the following method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier1 = #"Cell1";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier1];
}
[cell.textLabel setFont:[UIFont fontWithName:#"GothamRounded-Light" size:18]];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.detailTextLabel.backgroundColor = [UIColor clearColor];
[cell.detailTextLabel setFont:[UIFont fontWithName:#"GothamRounded-Light" size:12]];
cell.contentView.backgroundColor = [UIColor statOffWhite];
if (indexPath.row == 0) {
cell.textLabel.text = #"Profile";
}
else if (indexPath.row == 1) {
cell.textLabel.text = #"Support";
}
else if (indexPath.row == 2) {
cell.textLabel.text = #"Share";
}
else if (indexPath.row == 3) {
cell.textLabel.text = #"About";
}
else if (indexPath.row == 4){
cell.textLabel.text = #"";
cell.frame = CGRectMake(0, 0, self.view.bounds.size.width, 200);
UIImageView *watermark = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"watermark.png"]];
watermark.frame = CGRectMake((cell.frame.size.width/2) - (watermark.image.size.width/2), 80, watermark.image.size.width, watermark.image.size.height );
[cell addSubview:watermark];
}
return cell;
}
// UPDATE ////////
I took out the cellForRowAt method and the storyboard thing worked. But since I have taken out that method how can I set my font on my cell to be a custom font that isn't in Xcode's selections? I have included the font in my project, which I use everywhere.
What happen with your implementation of cellForRowAtIndexPath: is that your are creating cells from scratch and overwriting the storyboard's cells (hence your segue not being triggered).
If you defined static cells in your storyboard, you should not dequeue and create cells yourself. If your class is a subclass of UITableViewCellController, you could call the implementation of super and use the returned cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
// Customize your cell here
}
Note however that doing so makes little sense : all the things you are doing in your cellForRowAtIndexPath: method could and should for simplicity sake be done in the storyboard.
If you really need to customize a static cell programmatically, the way to go is to define an outlet to this cell in your view controller and customize the cell in the appropriate method (viewDidLoad for example).
How can I set first row of tableview checkmarked when the app started? I mean when I start app now, there are few rows in tableview and when I click on some of them its accessory change to checkmark. When click another one, the another one comes checkmarked and previous checkmark dissapears. So now I want first row of tableview to be checkmarked when app is started and then when I click another row it 'uncheckmark' and 'checkmark' new row etc...
Try the options suggested in these posts how to apply check mark on table view in iphone using objective c? or iPhone :UITableView CellAccessory Checkmark
Basically you need to keep track of the checkmarks using say a dictionary or so. And In viewDidLoad or init method make the first cell as checked in the dictionary. While drawing cells, always check if the corresponding entry in the dictionary is checked or not and display check mark accordingly. When user taps on a cell, modify the value in dictionary to checked/unchecked.
Update:
Here is a sample code.
In .h file declare a property as
#property(nonatomic) NSInteger selectedRow;
Then use the below code,
- (void)viewDidLoad {
//some code...
self.selectedRow = 0; //if nothing should be selected for the first time, then make it as -1
//create table and other things
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// create cell and other things here
if (indexPath.row == self.selectedRow)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// some other code..
if (indexPath.row != self.selectedRow) {
self.selectedRow = indexPath.row;
}
[tableView reloadData];
}
You could set an integer to a variable similar to "checkedCell", have that value default to cell 0, and in the cellForRowAtIndexPath check to see if the cell is already checked, if not change the accessory to make it checked and update your variable.
-(void)viewWillAppear{
currentCheckedCell = 0;
}
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
// Create cells -- if indexpath.row is equal to current checked cell reload the table with the correct acessories.
//
static NSString *cellIdentifier = #"RootMenuItemCell";
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"MyCell" owner:self options:nil];
for (UIView *view in nibContents) {
if ([view isMemberOfClass:[MyCell class]]) {
cell = (MyCell *)view;
break;
}
}
//OTHER CELL CREATION STUFF HERE
// cell accessory
UIImage *accessory = [UIImage imageNamed:#"menu-cell-accessory-default.png"];
UIImage *highlighted = [UIImage imageNamed:#"menu-cell-accessory-selected.png"];
if(indexPath.row == currentCheckedCell){
//DEFAULT ACCESSORY CHECK
// cell.accessoryType = UITableViewCellAccessoryCheckmark;
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:accessory highlightedImage:highlighted];
}else{
//DEFAULT ACCESSORY NONE
// cell.accessoryType = UITableViewCellAccessoryNone;
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:accessory highlightedImage:accessory];
}
[cell setAccessoryView:accessoryView];
}
return cell;
}
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//UPDATE SELECTED CELL & RELOAD TABLE
currentCheckedCell = indexPath.row;
[self.tableview reloadData];
}
Also worth noting that my examples uses custom images for accessories.
If you check out the link #ACB provided you'll find a very similar concept.
You can use,
if (firstCellSelected)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
Set the firstCellSelected flag in viewDidLoad method.
I have a case but the detailtext is not showing? Does anyone know what te problem is?
i used: UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *controller;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
switch(indexPath.row) {
case 0:
{
NSLog(#"Case 0");
controller = [[wed1 alloc] init]; //WithStyle:UITableViewStylePlain];
[self.navigationController pushViewController:controller animated:YES];
cell.detailTextLabel.text = #"Wedstrijden!";
}
break;
case 0:
{
NSLog(#"Case 0");
controller = [[wed1 alloc] init]; //WithStyle:UITableViewStylePlain];
//created a controller
[self.navigationController pushViewController:controller animated:YES];
//at his point, you are showing next ViewController's view
cell.detailTextLabel.text = #"Wedstrijden!";
//here you are changing the text of the text for current tableView's cell (which is actually going to be off the screen as new controller's view will be shown after previous statement execution)
}
with the piece of code you shared - I don't find anything wrong here, the detail text should not be visible as this view will be gone.
I don't fully understand why you'd want to set the detail text on a cell after pushing a new view controller. As samfisher said, when the user presses the first cell, in case 0, the new view controller will be alloc'd, init'd and then pushed, and after that you set the detail text to "Wedstrijden".
I'm assuming you're probably just confused between which property to use. My question to you is: when and where do you want this text to appear?
If the cell was created with the style UITableViewCellStyleDefault, the detail text label isn't displayed. In tableView:cellForRowAtIndexPath: implementation, you may create the cell with style UITableViewCellStyleSubtitle or UITableViewCellStyleValue1.
Use UIListContentConfiguration instead, or subtitle won't show.
#import <UIKit/UIListContentConfiguration.h>
And in the cellForRowAtIndexPath method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"AddressCell" forIndexPath:indexPath];
if(cell==nil){
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"AddressCell"];
}
NSRange range = [self.dataArray[indexPath.row] rangeOfString:#","];
NSString *street;
NSString *restAddress;
if (range.location != NSNotFound) {
street = [self.dataArray[indexPath.row] substringToIndex:range.location];
restAddress= [self.dataArray[indexPath.row] substringFromIndex:range.location + 1];
}
UIListContentConfiguration *configuration = cell.defaultContentConfiguration;
configuration.text=street;
configuration.image=kImage(#"Location");
configuration.secondaryText=restAddress;
cell.contentConfiguration = configuration;
return cell;
}
Will look like this: