I'm trying to display a no results message on my tableview when it is empty. I have done the uilabel approach where it appears when it is empty but it seems like it's not how Apple has done it in the Contacts etc where the "No Results" move as well when you try to scroll up and down. Mine just stays there in one spot.
Does anyone know how to do this?
I think they added a No Results cell?
Yes. If you have no results to display, do the following
Create a boolean flag named noResultsToDisplay, or something else.
If you have no results to display then set noResultsToDisplay = YES, set it to NO otherwise.
In numberOfRowsInSection, if (noResultsToDisplay) return 3;
In cellForRowAtIndexPath, if (noResultsToDisplay && indexPath.row == 2) cell.textLabel.text = #"No Results";
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 3;
}
// Customize the appearance of table view cells.
- (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];
}
// Configure the cell...
if (indexPath.row == 2) {
cell.textLabel.text = #"Empty cell";
}
return cell;
}
Two way of doing it: Either do as you suggest yourself, make a "No Results Cell" and have a state in your tableViewController that is BOOL resultIsEmpty = YES. In the cellForRowAtIndexPath you first test for this empty BOOL and return only the no result cell , remember to also check in numberOfRowsInSection so you can return 1 in the case of empty (otherwise it will probably return the length of your model array which of course is 0).
The other way is to make a y inset on the table view and place your label there. This can be done because UITableView is a subclass of UIScrollView.
self.tableView.contentInset = UIEdgeInsetsMake(heighOfNoResultLabel, 0, 0, 0);
Then in the "resultsDidLoad" or what your delegate for new data is called, you test if it is 0 and inset the tableView and place a label there. If it is not 0 set the inset to
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
all 0's. You can animate this property, so that when there are no results, the tableview "scrolls" down to reveal the the "No Results" Label.
Bot solutions are valid I would say, about the same amount of code. The difference is probably what you can do with it afterwards.
I have resolved this by maintaining a label view with the same farme as the table has, and play with the "hidden" attribute of the label and table (always one is YES and the other is NO).
i have edited the accepted answer to be look like the no search results in tableView
Create a boolean flag named noResultsToDisplay, or something else.
1-If you have no results to display then set noResultsToDisplay = YES, set it to NO otherwise.
2-(this step changed)In numberOfRowsInSection, if (noResultsToDisplay) return 1;
// 1 returned not 3
3-(this step changed) In cellForRowAtIndexPath,
static NSString *CellIdentifier;
if (noResultsToDisplay){
CellIdentifier = #"Cell";
// in my case i use custom cell this step can be skipped if you use the default UITableViewCell
}
else{
CellIdentifier = #"DocInqueryCell";
}
DocumentationInqueryCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// don't forgt to add cell in your storyboard with identifier Cell and class your custom class , again this step can be skipped if you use the default cell
then
if (noResultsToDisplay) {
cell.textLabel.text = #"No Results";
}else{
do what you want in your custom cell
}
Related
I'm learning The development of iOS ,and I have a question .I can't debug it ,Google can’t give me answer.
Maybe I did not find a right way.
This question is when I create UITableViewCell objects and I reuse them ,and these cells are stored in a MutableArray, when I reuse cells from Buffer pool ,and get the value from the MutableArray, but the value of textLable.text is not right .
my code :
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * timeRing = #"ring";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:timeRing];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:timeRing];
}else{
while ([cell.contentView.subviews lastObject]!=nil) {
[[cell.contentView.subviews lastObject] removeFromSuperview];
}
}
cell.accessoryType = UITableViewCellAccessoryNone;
if (self.cellArray.count<self.ringArray.count) {
self.timeRingItem = self.ringArray[indexPath.row];
cell.tintColor = [UIColor redColor];
cell.textLabel.text= self.timeRingItem.ringName;
[self.cellArray addObject:cell];
}
else{
cell = self.cellArray[indexPath.row];
}
return cell;
}
when the code run to cell = self.cellArray[indexPath.row]; ,the cell.textLable.text is wrong
so why? thanks for giving me answer.
The nature of a tableview means that cells will be re-used. When the user scrolls, it's going to take the top cell that just disappeared, and use it for the new cell. If you want to reference data, you need to keep the data in a separate array that's independent from the cells.
I have a tableview that has a UISegmentedControl as the tableHeaderView for my tableViewController. I have two arrays as properties that will populate the cells based on which index is selected in the segmentedController. When the two arrays have a different number of items in them, I get an out of bounds exception, which I would expect. When the number of items is the same in each array, I can reload the tableView data and everything works fine. How can I prevent the controller from trying to reuse a cell when the cell isn't needed anymore?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Downloads Cell" forIndexPath:indexPath];
// 1st option selected
if (self.downloadTypeSelector.selectedSegmentIndex == 0) {
if (indexPath.row < self.talkDownloads.count) {
// Configure the cell...
Talk *talk = [self.talkDownloads objectAtIndex:indexPath.row];
cell.textLabel.text = talk.title;
cell.detailTextLabel.text = talk.speaker;
}
// 2nd option selected
} else if (self.downloadTypeSelector.selectedSegmentIndex == 1) {
if (indexPath.row < self.speechesDownloads.count) {
Speech *speech = [self.speechesDownloads objectAtIndex:indexPath.row];
cell.textLabel.text = speech.title;
cell.detailTextLabel.text = speech.speaker;
}
}
return cell;
}
I see that you have a condition that checks downloadTypeSelector.selectedSegmentIndex and uses the respective array accordingly, which is fine.
Do you have the same if else condition in - tableView:numberOfRowsInSection: that checks the selectedSegmentIndex and returns the correct count of the respective array?
If that works then I think checking if indexPath.row < array.count will be obsolete too.
Cheers!
I have created a tableview with multiple prototype (4) cells in order to display different content in each cell but being a newb - I am not clear on how to then code this in the tableviewcontroller to pull data into the multiple cells (just using multiple simple arrays with one data point for each label for now to test it) but I can only get the data to populate in the first cell - and not clear how to code to get the data into the remaining cells. I created 4 separate customtableviewcell files as well. Can someone point me in the right direction on how to code so I can get data into the four separate prototype cells? I need it to be able scroll as it won;t all fit on the the screen which is why I chose table view to do this - but there will only ever be these four sections in the view (with different data depending on what you pushed to get here) should I not be using tableview? if I should be using something different like a view controller with 4 views instead? will it scroll so the user can see all sections? Thanks in advance for any help and suggestions.
You should assign unique Identifier to the each cell in the Storyboard. Then, you can populate appropriate cells like this:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section)
{
case 0:
{
MyCustomCell1 *cell = [tableView dequeueReusableCellWithIdentifier:#"cell_1"];
// Configure cell
return cell;
}
case 1:
...
default: return nil;
}
}
Consider creating custom subclasses of UITableViewCell to provide handy IBOutlets.
(1) You need 4 cells. So, prepare 4 custom cells by creating subclass of UITableViewCell. You will know how to create custom cell by search on google.
(2) Set number of sections to 1.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
(3) Set desired height for each cell
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch(indexPath.row)
{
case 0:
{
return 40;
}
case 1:
{
return 50;
}
case 2:
{
return 30;
}
case 3:
{
return 45;
}
default:
{
return 0; // Default case
}
}
}
(4) Set contents of each cell. The data will come from your data source i.e. Array or Dictionary.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"CellIdentifier%d%d",indexPath.section,indexPath.row];
if(indexPath.row == 0)
{
CustomCell1 *objCustomCell1 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(objCustomCell1 == nil)
{
objCustomCell1 = [[CustomCell1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
objCustomCell1.selectionStyle = UITableViewCellSelectionStyleNone;
}
// Set row specific data here...
NSDictionary *dicObj = [arrYourDataSource objectAtIndex:indexPath.row];
objCustomCell1.myLabel.text = [dicObj objectForKey:#"your key"];
return objCustomCell1;
}
else if(indexPath.row == 1)
{
CustomCell2 *objCustomCell2 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(objCustomCell2 == nil)
{
objCustomCell2 = [[CustomCell2 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
objCustomCell2.selectionStyle = UITableViewCellSelectionStyleNone;
}
// Set row specific data here...
NSDictionary *dicObj = [arrYourDataSource objectAtIndex:indexPath.row];
UIImage *theImage = [UIImage imageNamed:[dicObj objectForKey:#"your key"]];
objCustomCell2.myImageView.image = theImage;
return objCustomCell2;
}
// Do same for remaining 2 rows.
return nil;
}
First do this and then add comment. We will move ahead.
I'm playing with the cell identifier in a TableviewController, I'm using storyBoard with dynamic cells.
I set 2 cells with 2 identifiers and used a custom row height:
In my viewDidLoad, I just insert some checking information to a mutableArray:
- (void)viewDidLoad
{
[super viewDidLoad];
_arrayCellContent = [[NSMutableArray alloc]init];
for (int i=0; i<15; i++) {
if (i%2 == 0) {
[_arrayCellContent addObject:#"white"];
}
else
[_arrayCellContent addObject:#"Brown"];
}
}
My cellForRow delegate method is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* identifierCellSlim = #"cellSlim";
static NSString* identifierCellFat = #"cellFat";
UITableViewCell *cellSlim = [tableView dequeueReusableCellWithIdentifier:identifierCellSlim forIndexPath:indexPath];
UITableViewCell *cellFat = [tableView dequeueReusableCellWithIdentifier:identifierCellFat forIndexPath:indexPath];
if (indexPath.row%2 ==0) {
cellSlim.textLabel.text = self.arrayCellContent[indexPath.row];
return cellSlim;
}
// Configure the cell...
else {
cellFat.textLabel.text = self.arrayCellContent[indexPath.row];
cellFat.detailTextLabel.text = #"yalla";
return cellFat;
}
}
The final outcome is:
Even cell row heights, not costumed. Why is that? (I know I might work it out with the right delegate method, but I just want to know why the IB not doing his job)
Also in the beginning the cells appear to be in the right color but when I click some of them they will transform to the other cell,
Does clicking the cell change the indexPath ?
Example:
You are dequeuing both cells in the same method call.
You should check for which cell is necessary for the given NSIndexPath and dequeue the relevant cell.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = NO;
}
I use the above code to disable a cell after a user clicks on it once. The problem I've run into is that when a cell is added to the table, that new cell is disabled, and the previously disabled one isn't anymore.
How can I fix this problem?
Cells get reused as the user scrolls the table. You need to keep track of which rows the user has disabled so in your cellForRowAtIndexPath you can set the userInteractionEnabled property (to YES or NO as needed) for every cell every time it is requested.
Update - more details.
You need to keep track of which index paths the user has selected. Add an instance variable of type NSMutableSet and add each indexPath to this in your didSelectRow... method.
Then in your cellForRow... method you need to check if the current indexPath is in the set or not. Based on the result you set the cell's userInteractionEnabled property:
cell.userInteractionEnabled = ![theSelectedPathsSet containsObject:indexPath];
where theSeletedPathsSet is your NSMutableSet instance variable.
This solution assumes the rows and sections in your table are fixed. If the user can do things that results in rows being added, removed, or moved, then you can't simply track the index paths. You need to use some other key to know which rows have been selected.
Are you using dequeueReusableCellWithIdentifier in your cellForRowAtIndexPath?
You should have something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *reuseIdentifier = #"myTableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell) {
cell = [[ArticleTableViewCell alloc] init];
}
// customise cell here (like cell.title = #"Woopee";)
if (self.selectedCells containsObject:[NSString stringWithFormat:#"%d", indexPath.row]] {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = NO;
}
return cell;
}
Expanding on the other answer, you can keep track of whether a specific cell has been previously selected (are therefore should be disabled) by doing something like this with the above:
Declare a property like #property (nonatomic, strong) NSMutableArray *selectedCells; then:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
[self.selectedCells addObject:[NSString stringWithFormat:#"%d", indexPath.row]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = NO;
}
My laptop is about to die, but if it crashed you should look at the code the initialises the cell (alloc and init) or keep what you had before there.
You need to keep a record of which cells have been disabled. You could store the indexPath of the selected cells in an array and then use that to determine which cells should be active and not active in your cell:forRowAtIndexPath: method.