I am working with a UICollectionView and am having a bit of difficulty replicating something that I have working in a UITableView. I am looking to set the selectedBackgroundView of a selected UICollectionView cell and have that selection maintained every time the view is loaded.
I have a basic application with two tabs in a Tab Bar controller where both tabs are UITableViewControllers. The second tab is the In-App Settings and I have two cells in there; one for App Themes and one for Keyboard themes. The keyboard themes is another Table View but the App Themes is a UICollectionView with a grid of 3x4 cells. Although this is dynamically created, there's not going to be any more cells and this is it.
Working
Right now, I can select a UICollectionViewCell and happily apply a custom image on the top; that works very well.
Problem
The problem I am facing is the fact that the selected cell does not maintain that custom image on the top when the view reloads even though the value is saved in NSUserDefaults.
Because I have the same concept working with my Keyboard Themes (UITableView), I have copied the principles here, but am not sure if what I have done is actually correct.
Here's some code:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
ThemeCell *themeCell = (ThemeCell *)[self.cView cellForItemAtIndexPath:indexPath];
self.selectedThemeString = themeCell.cellLabel.text;
if(self.checkedIndexPath)
{
UICollectionViewCell *uncheckCell = [self.cView cellForItemAtIndexPath:self.checkedIndexPath];
uncheckCell.selectedBackgroundView = nil;
}
themeCell.selectedBackgroundView = dot;
[themeCell addSubview:dot];
self.checkedIndexPath = indexPath;
UIImageView *dot = [[UIImageView alloc]initWithFrame:CGRectMake(5, 0, 1, 2)];
dot.image=[UIImage imageNamed:#"check-white-hi.png"];
themeCell.selectedBackgroundView = dot;
[themeCell addSubview:dot];
[[NSUserDefaults standardUserDefaults] setObject:self.selectedThemeString forKey:#"Selection"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
In the cellForItemAtIndexPath, I have:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ThemeCell *themeCell = (ThemeCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"Theme Cell" forIndexPath:indexPath];
NSString *cellData = [self.themeLabels objectAtIndex:indexPath.row];
themeCell.cellLabel.text = cellData;
themeCell.cellImages.image = self.themeImages[indexPath.row];
UIImageView *dot = [[UIImageView alloc]initWithFrame:CGRectMake(5, 0, 1, 2)];
dot.image=[UIImage imageNamed:#"check-white-hi.png"];
self.selectedThemeString = [[NSUserDefaults standardUserDefaults] objectForKey:#"Selection"];
NSLog(#"Selected Theme String = %#", self.selectedThemeString);
if (!self.selectedThemeString)
{
NSLog(#"111");
self.checkedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
themeCell.selectedBackgroundView = dot;
[themeCell addSubview:dot];
}
if ([self.selectedThemeString isEqualToString:#"Original"])
{
NSLog(#"222");
self.checkedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
themeCell.selectedBackgroundView = dot;
[themeCell addSubview:dot];
}
if ([self.selectedThemeString isEqualToString:#"Mystical"])
{
NSLog(#"333");
self.checkedIndexPath = [NSIndexPath indexPathForRow:2 inSection:0];
themeCell.selectedBackgroundView = dot;
[themeCell addSubview:dot];
}
return themeCell;
My viewWillAppear has:
self.selectedThemeString = [[NSUserDefaults standardUserDefaults] objectForKey:#"Selection"];
if ([self.selectedThemeString isEqualToString:#"Original"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Peacock"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:1 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Mystical"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:2 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Zebra"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:3 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Simplicity"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:4 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Rainbow"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:5 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Prosperity"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:6 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Leopard"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:7 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Hypnotic"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:8 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Dunes"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:9 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Twirl"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:10 inSection:0];
}
else if ([self.selectedThemeString isEqualToString:#"Oceanic"])
{
self.checkedIndexPath = [NSIndexPath indexPathForRow:11 inSection:0];
}
[self.cView reloadData];
The order of the cells are defined above, so the first row first cell is called Original. The second cell first row is called Peacock and the third cell first row is called Mystical, etc.
My thinking was, I could define the indexPathForRow for each string and in the cellForItemAtIndexPath, when the string hits, it would apply the selectedBackgroundView to the image.
Status
What's happening now is, I can successfully select a cell and the image appears; if I reload the view (by going out and coming back in), the selectedBackgroundView from the cell doesn't exist. However, I know NSUserDefaults is working because in my cellForItemAtIndexPath, the "111", or the "222" or "333" is getting logged successfully, but it's just not setting the highlight state of the selected UICollectionCell.
What I require is to:
1) Have a selected cell display the selectedBackgroundView image when the view has been reloaded.
Update:
In the cellForItemAtIndexPath, if I put themeCell.backgroundView = dot instead of themeCell.selectedBackgroundView = dot, when I reload the view, every single cell highlights with the custom image instead of just the selected cell, so I think I'm making progress somewhat, but still lost.
Any guidance would be really appreciated.
Situation averted.
I have fixed this issue.
For reference, I will put the fixed code here for anyone experiencing the same issue.
In the cellForItemAtIndexPath, I did the following:
if (!self.selectedThemeString)
{
NSLog(#"111");
self.checkedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
themeCell.backgroundView = dot;
[themeCell addSubview:dot];
}
if([self.checkedIndexPath isEqual:indexPath])
{
NSLog(#"Loaded");
themeCell.backgroundView = dot;
[themeCell addSubview:dot];
}
else
{
NSLog(#"Not Loaded");
themeCell.backgroundView = nil;
}
This is similar to the code earlier, but essentially copied from my UITableView code which checks the indexPath.
The other change is the fact that I used the backgroundView property rather than selectedBackgroundView.
I did this change throughout my class, so wherever I had the selectedBackgroundView, I changed that to backgroundView.
I'm happy and this works like a treat.
Related
I have an old iOS app written in objective c. It has several UILabels that has given fixed x,y,width and height. Now what I want to do is when click on the acessoryView expand the cell and expand the UILabels inside it. Also I want to change the positions of those UILabels. So far I have done cell expanding part in this way.
- (void)expandCell :(UIButton *)sender {
NSLog(#"NSINdex Path-------%li",(long)sender.tag);
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];
MetadataTableViewCell *cell = [_metadataListTVCTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:sender.tag inSection:0]];
if (newIndexPath == cellExpandedRow) {
cell.isExpanded = true;
}
else {
cell.isExpanded = false;
}
cellExpandedRow = newIndexPath;
[_metadataListTVCTableView beginUpdates];
[_metadataListTVCTableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForItem: sender.tag inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[_metadataListTVCTableView endUpdates];
[_metadataListTVCTableView reloadData];
}
But I have no idea where I can do inner label frame updatings. Please help me. Thanks
I have a tableview. It will receive mess of data from server. Every time tableview receive data I wanna it show the latest one at bottom.I called scrollToRowAtIndexPath:atScrollPosition:animated: but it lag my app i can do nothing else in my screen when scroll to bottom of tableview.
Here is my main code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"tableViewCell";
DLtestCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
NSDictionary* dict = arr[indexPath.row];
cell.myLabel.attributedText = dict[#"content"];
[cell.myImageview sd_setImageWithURL:dict[#"imageUrl"] placeholderImage:[UIImage imageNamed:#"transparentPlaceHolder"]];
return cell;
}
and here is scrolling to the bottom main code
- (void)reloadTableView
{
dispatch_async(dispatch_get_main_queue(), ^{
NSIndexPath *lastRow = [NSIndexPath indexPathForRow:row inSection:0];
[self.tableView insertRowsAtIndexPaths:#[lastRow] withRowAnimation:UITableViewRowAnimationLeft];
});
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:NO];
}
You should use scrollToRowAtIndexPath method.
And in viewWillAppear write below code.
[theTableView reloadData];
NSIndexPath* ip = [NSIndexPath indexPathForRow:rowNumber inSection:sectionNumberHere];
[theTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:NO];
here 'rowNumber' is the row number in the data source you want to scroll to.
or you can calculate 'rowNumber' as follows:
int lastRowNumber = [tableView numberOfRowsInSection:0] - 1;
and pass this lastRowNumber to indexPathForRow.
NOTE : numberOfRowsInSection returns row count, not the last row number (which is row count - 1).
I don't know why this doesn't work. indexOfIcon is correct, section is correct (checked with NSLog) If I select one everything is correct. But this line doesn't do a thing...why? If selected it should have a blue border. This works great while doing it "manually" but not with code..
- (void)viewWillAppear:(BOOL)animated
{
NSUInteger indexOfIcon;
if(self.mainCategory.icon){
indexOfIcon = [self.icons indexOfObject: self.mainCategory.icon];
} else {
indexOfIcon = 0;
}
[self.collectionView selectItemAtIndexPath:[NSIndexPath indexPathForRow:indexOfIcon inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionBottom];
}
add
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
cell.selected = YES;
and the cell will be selected.
The command [self.collectionView selectItemAtIndexPath:[NSIndexPath indexPathForRow:indexOfIcon inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionBottom]; tells the collectionView, that the cell is in selected state, but don't set the state of the cell.
After years selection seems to work fine (even in viewDidLoad:)
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
[_collection selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionLeft];
successfully triggers cell's -setSelected:
My question is clear,
I have a UITableView and UIMapView with annotations, when an annotation is tapped on the map, it will be found on the table and be selected since the user can recognize it.
But, if i try something it is only in visible cells, i am not able to do as i expected.
- (void)annotationTapped:(UITapGestureRecognizer *)recognizer{
if ( recognizer.state == UIGestureRecognizerStateEnded ) {
//NSLog(#"%#",[recognizer.view subviews]);
UIImageView *temp = (UIImageView*)[recognizer.view viewWithTag:1000];
UILabel *temp2 = (UILabel*)[temp viewWithTag:1001];
NSArray *tapAndFind;
if(isFiltered)
{
tapAndFind = filteredListContent;
}
else
{
tapAndFind = eczaneler;
}
for(int i=0;i<tapAndFind.count;i++)
{
Pharma *tempPharm = [tapAndFind objectAtIndex:i];
if([tempPharm.CustomerIndex isEqualToString:temp2.text])
{
EczaneCell *cell = (EczaneCell*)[tableView1 cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
for(EczaneCell * cell2 in [tableView1 visibleCells])
{
cell2.selected = NO;
}
cell.selected = YES;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[tableView1 indexPathForCell:cell].row
inSection:[tableView1 indexPathForCell:cell].section];
[tableView1 scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
}
}
This is because your UITableView is just a presentation of your data, not a data itself. So, when you tap on annotation, you should somehow find a correspondence with a data, the position of your annotation data in collection. Then you may calculate/find out the index of row in table, and then you can perform UITableView's scrollToRowAtIndexPath:atScrollPosition. Alternatively, you can mark the cell to make it distinguishable.
In your code
EczaneCell *cell = (EczaneCell*)[tableView1 cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
can return nil for cell when the cell for given index path is invisible. That's why you should check against data, not table cells.
I am having problems with deleting rows from table view. I am using the code below when the delete button in the row was pressed:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:control.tag-100 inSection:0];
[resultList removeObjectAtIndex:indexPath.row];
[resultView beginUpdates];
[resultView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[resultView endUpdates];
//[resultView reloadData];
First row was deleted successfully but then, indexes were not correct. So when I delete the last row, it gives index out of bounds exception.
The cell generation code is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"personalizeTableCell";
PersonalizeCell *cell = (PersonalizeCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[PersonalizeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.title.text = #"text";
cell.rateView.tag = indexPath.row + 100;
return cell;
}
Where am I wrong?
UPDATE:
for (NSInteger j = 0; j < [venuesTableView numberOfSections]; ++j)
{
for (NSInteger i = 0; i < [venuesTableView numberOfRowsInSection:j]; ++i)
{
PersonalizeCell* cell = (PersonalizeCell*)[venuesTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]];
cell.rateView.tag = 100 + i;
}
}
solved my problem. Thanks to Nenad M.
Assuming your [NSIndexPath indexPathForRow:control.tag-100 inSection:0]; returns the right index path....
Did you try moving the removeObjectAtIndex call inside the begin-/end-updates "bracket"?:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:control.tag-100 inSection:0];
[resultView beginUpdates];
[resultList removeObjectAtIndex:indexPath.row];
[resultView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[resultView endUpdates];
UPDATE:
Obviously your cell.rateView.tag become wrong, after youe delete your first cell.
So after every deletion (i.e. every removeObjectAtIndex...) you must re-iterate over your remaining tableview-cells an re-assign the proper tag-value (cell.rateView.tag = indexPath.row + 100)! Otherwise your [NSIndexPath indexPathForRow:control.tag-100 inSection:0]; will return a wrong indexPath, therefore leading to your out of bounds error!
Re-assigning the tag-values:
You don't have to reload the entire table, just loop through the remaining cells an re-assign the tag-value after [resultView endUpdates];:
NSMutableArray *cells = [[NSMutableArray alloc] init];
for (NSInteger j = 0; j < [tableView numberOfSections]; ++j)
{
for (NSInteger i = 0; i < [tableView numberOfRowsInSection:j]; ++i)
{
[cells addObject:[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]]];
}
}
Now do:
for (PersonalizeCell* cell in cells)
{
cell.rateView.tag = // new calculated tag
}
(Or do the re-assignment even in the inner-loop of the first code-snippet directly.)
Here's some really typical code for the whole process, two lines from the table in the example:
Note that facebookRowsExpanded is a class variable you must have:
if ( [theCommand isEqualToString:#"fbexpander"] )
{
NSLog(#"expander button......");
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSArray *deleteIndexPaths;
NSArray *insertIndexPaths;
facebookRowsExpanded = !facebookRowsExpanded;
// you must do that BEFORE, not AFTER the animation:
if ( !facebookRowsExpanded ) // ie, it was just true, is now false
{
deleteIndexPaths = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:2 inSection:0],
[NSIndexPath indexPathForRow:3 inSection:0],
nil];
[tableView beginUpdates];
[tableView
deleteRowsAtIndexPaths:deleteIndexPaths
withRowAnimation: UITableViewRowAnimationMiddle];
[tableView endUpdates];
}
else
{
insertIndexPaths = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:2 inSection:0],
[NSIndexPath indexPathForRow:3 inSection:0],
nil];
[tableView beginUpdates];
[tableView
insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation: UITableViewRowAnimationMiddle];
[tableView endUpdates];
}
// DO NOT do this at the end: [_superTableView reloadData];
return;
}
NOTE: your code for numberOfRowsInSection must use facebookRowsExpanded
(it will be something like "if facebookRowsExpanded return 7, else return 5")
NOTE: your code for cellForRowAtIndexPath must use facebookRowsExpanded.
(it has to return the correct row, depending on whether or not you are expanded.)