I have a question regarding UITableView on Xcode. I have text in the cell but when I test it, it doesn't show. I just started programming so any help would be appreciated. If anyone can email me so I can provide the project file that would be most helpful.
You will need to be aware of how table views work. I would suggest carefully reading some of the excellent online documentation on this topic, including sample code if available.
You will need to remember that the system is responsible for deciding which parts of the table view are supposed to be visible, then asking your code to provide the details of what should be seen.
If you have actually provided the routine to provide the contents of the cell, try putting in a breakpoint or some logging to ensure that your code is being called. If so, start by returning something simple and verifying that this is displayed.
Also, prepare code with an explanation of what it is doing to add to the question. You never know, just doing that may clarify to you what is going wrong. If not, post it up and give someone else a chance to help you.
I cannot really directly solve your problem because you surprisingly haven't given code so as far as I know you could be missing a semicolon! (JK)
This is a detailed answer I gave to someone a while ago, very relevant to your situation
But for a table view use this code:
- (UITableViewCell *)tableView:(UITableView *)tableView2 cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"UITableViewCell"]
autorelease];
cell.textLabel.text = nil;
}
if (cell) {
//customization
cell.textLabel.text = #"Text Label";
}
return cell;
}
You say you are starting coding.... let me explain. First of try picking up the book:
The Big Nerd Ranch Guide
I suggest you read a heck load of books to get you juiced up on language and API and programming skills
Dequeuing is basically nilling and cacheing any cell that is not visible, aka you scroll past it. Therefore, cell == nil will be called in probably four situations (that I can think of):
When we first setup the table view (cells will be nil)
When we reload data
Whenever we may arrive at this class
When the cells becomes invisible from the table view
So, the identifier for dequeuing is like an ID. Then in the statement to see if cell is nil, we initialize cell, you can see the overridden init method: initWithStyle. This is just what type of cell there is, there are different types with different variables you can customize. I showed you the default. Then we use the reuseIdentifier which was the dequeuing identifier we said earlier. THEY MUST MATCH! I nil textLabel just for better structure, in this case each cell has the same text so it won't matter really. It makes it so the cell that dequeues comes back with the right customization you implemented. Then once cell is actually valid, we can customize.
Also, you are using the same text for each cell. If you do want to have different text for each cell, familiar yourself with NSArray. Then you could provide the array count in numberOfRowsForSection and then do something like this:
cell.textLabel.text = [array objectAtIndex: [indexPath row]];
Where indexPath is the NSIndexPath argument provided in the cellForRowAtIndexPath method. The row variable is the row number, so everything fits!
Wow, that was a lot to take in right! Now go stop being an objective-c noob and start reading some books!
For more info read:
Table View Apple Documentation
Related
I am somewhat new to iOS, but am experienced in Android.
I have an app I am working on and it needs to populate a page with your "history" of past people you've interacted with, and it shows their picture, name, rating, and some other information.
This needs to populate in a vertical list, maybe a table? See the image below...
Now, in android, I would create a custom class with a layout that houses the picture, name, information, rating, and what not in one xml file, and in the activity I would call that class in a for loop, grabbing all the users and then programmatically it would add each view one after another, with their own unique user information until there is no more users to populate with.
How exactly can I do this in iOS and xcode? Do I need to make an XIB and add the picture, name, rating, and info place holders in that, and create a custom class for it that I would use to run in a for loop as well? I am a little stuck on how to do this with iOS.
Any help is much appreciated, and I can provide any additional information! Thanks :)
In iOS, you probably want to use a UITableView, with each row being a custom subclass of UITableViewCell. You can either create the layout for those cells in a separate XIB, or put the whole lot, tableView and "prototype" cells in a storyboard. You can achieve a lot without even subclassing, so fire up a dummy project in XCode and play (using one of Apple's templates gives you a good start). Enjoy.
What you probably want is to use a UITableView.
You don’t do the for-loop yourself. What you do is implement a set of delegate methods that the table view calls back to.
You can create your prototype cell in your XIB or Storyboard. When you add a Table View to the layout, you can then add a cell to that table view, and that cell will be your prototype. It looks like you only need one prototype cell, but you can create as many as you need. In Interface Builder you give the prototype cell a “reuse identifier”, which is just an arbitrary tag you use to refer to the prototype in your code. Your prototype cell can be your own subclass of UITableViewCell, or if you don’t need any custom code in it, you can just use UITableViewCell.
Then you implement several delegate methods. One is where you set the number of sections in the table view; it looks like you will only have on section.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tv
{
return 1;
}
Then you tell it how many items are in the table view. Assuming you have the objects you want to display in an array, you just return the length of the array.
- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section
{
return self.objects.count;
}
Then, for each item in the array, cellForRowAtIndexPath will be called. Make that method return the actual cell. You call dequeueReusableCellWithIdentifier to retrieve your prototype cell, using the reuse identifier you assigned in Interface Builder. Then use the corresponding object to set up the UI elements in your cell.
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)i
{
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:i];
Thingy *item = self.objects[i.row];
cell.textLabel.text = item.name;
return cell;
}
That should be enough to get you started with the documentation, now that you have the overview of what you need to implement.
The first thing you have to do in switching from Android to iOS is to learn the terminology. Then you'll know what to search for on Google, SO, etc.
What's you're looking to do is create a UITableView.
Here is a link to a super basic 'how-to' to get you started with tableviews.
http://www.appcoda.com/uitableview-tutorial-storyboard-xcode5/
Once you've got the basics down, you'll want to take that a step further with learning how to customize the UITableViewCell within your tableview, so you can accomplish the look you've detailed in the question.
http://www.appcoda.com/customize-table-view-cells-for-uitableview/
I'm not sure I can help anymore than that at the moment. Jump in, learn tableviews, and start searching on OS to answer the million other questions you'll have a long the way.
Good luck!
So yesterday i began learning iOS programming, and i followed the tutorial for the to-do app.https://developer.apple.com/library/ios/referencelibrary/GettingStarted/RoadMapiOS/SecondTutorial.html#//apple_ref/doc/uid/TP40011343-CH8-SW1
I have created my own tablelistviewcontroller as the guide tells me, and all my previous static data is gone when i boot, and the tutorial tells me this
you’ll notice that it implements three methods—numberOfSectionsInTableView:, tableView:numberOfRowsInSection:, and tableView:cellForRowAtIndexPath:. You can get your static data to display again by commenting out the implementations of these methods. Go ahead and try that out if you like.
So i commented out the cellForRowAtIndexPath, it was the only one commented out. And then i got an error at the "reuseIdentifier", so after a time of googling, i have managed to name my cells "Cells" and i ended up with this code,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cells";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;
}
But everytime i build and start the app, i cant see my previous data, i can see it in my storyboard, but not in app.. And now i am out of ideas and since i am a newbie i believe the solution is pretty easy.
Please notify if you need any additional data.
I think you really misunderstood these lines:
So why doesn’t your data show up? Table views have two ways of getting
data—statically or dynamically. When a table view’s controller
implements the required UITableViewDataSource methods, the table view
asks its view controller for data to display, regardless of whether
static data has been configured in Interface Builder. If you look at
XYZToDoListTableViewController.m, you’ll notice that it implements
three methods—numberOfSectionsInTableView:,
tableView:numberOfRowsInSection:, and
tableView:cellForRowAtIndexPath:. You can get your static data to
display again by commenting out the implementations of these methods.
Go ahead and try that out if you like.
The thing is when you are adding data using your app, the data is no more static. It is dynamic. UITableView doesn't store your data. It just displays it. And when you are using dynamic data, you will need a datasource which will give the tableView the data to display. You must store the data by some possible storage mechanisms in IOS. Here is a link to one such tutorial which will get you started with Core Data.
Here is a good tutorial which will get you started with UITableView that will display a dynamic data.
The problem is you got the concept totally wrong.
EDIT
You might have added something like <UITableViewDataSource> in the interface file and may have added the ViewController as the datasource for the table(either using ctrl-drag in story board or tableView.datasource = self; in the code). Just remove these two and your static content will display again.
Before I get crucified for this, let me state... Yes, I have read every "relevant" answer on this topic and have not found a workable solution. Most "correct" answers are pre-ARC and discuss "releasing" a cell, which just isn't done anymore. Secondly, my problem is not "global", meaning some views have no problems, while others do. So here is my question...
I have sub-classed uitableviewcell and setup some uilabels & custom uiviews. From there I wired everything up in ib (Xcode 5.x iOS 7.x). Once I put in the appropriate code and create the tableview & dynamic cells from a nsarray "not mutable" everything works exactly as expected with no issues.
This is the fun part. I am making changes to allow the data source of the tableview, which is an nsarray to be mutable to allow adding and removing of items / cells. This is where things get hairy. When I start to add more objects to the array and when the reuse cell is being put on screen visual data from old cells is being reused on new cells. I say "visual" because once the cell is selected the view updates to display the correct information. The part that is interesting is that as I stated I have some uilabels which never have any problems being redisplayed, my custom views however are now the piece of the puzzle that is displaying info from past cells, and when scrolling back up, the original cells no longer display the correct information. Once the cell is tapped, then the cell updates and displays the correct information.
the most confusing bit of this is that before my array was mutable and had a static amount of objects this worked fine. Even if a cell went off screen and came back, it was still the correct information being displayed. Now I know that shouldn't have anything to do with it, but it is strange that it worked using the same tableview & cell code that I am using now.
I have tried adding in
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (!cell) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"]; // note: obviously as stated, tacking on "autorelease" here as mention in other suggestions is not going to work.
}
Which doesn't fix the issue.
I tried overriding the "prepareForReuse" method on my custom cell subclass and that does not resolve the issue either. I have made the views, "strong" & "weak", and all that and still every 3rd or so cell gets repeated with garbage data until it is refreshed. Again, the uilabels which are setup the same way as the views have no problems and data is never reused. I would say there is a problem with my custom views, but setting up the table from a static source of identical information there is no problem.
I would like to post some code, but it's all pretty generic code for tableviews & delegates. any suggestions would be greatly appreciated.
As i said the code is all pretty generic, but apparently it needs posing anyway so here it is..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyThing *thing = self.stuffArray[indexPath.row];
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.thisLabel.text = thing.someText;
cell.thatLabel.text = thing.otherText;
cell.view1.someProptery = thing.object1.property
cell.view2.someProptery = thing.object2.property
cell.view3.someProptery = thing.object3.property
//"someProperty" on "view..." is an NSInt that is used to determine custom drawing in the view.
return cell;
}
I think the key to the solution lies in your comment about the custom views in the cells. If cellForRowAtIndexPath is altering the states of those views, they need to know that they must be redrawn, so you'll need to augment the synthesized setter in your custom view.m that has someProperty.
If the someProperty determines how this view get's drawn, then it's incumbent upon the setter to indicate that the view is out of date....
- (void)setSomeProperty:(NSInteger)someInt {
_someProperty = someInt;
[self setNeedsDisplay];
}
I have a question regarding a design convention.. See I have this tableview filled with editable information. Editable as in changing the text in the right detail of the cell, not as in deleting or moving a cell. I wonder how to design/model this, the only Apple product that I know has this feature is the contacts app. The solution there is to make lots and lots of groups, but this does not fit my problem at all, partly since I already have groups. The simplest way would just be to have the right detail be a text field and enable it in edit mode, but that would of course be a stupid solution since no visual feedback is given..
Any ideas on how to design/model this, or how Apple would like to have it?
EDIT:
To be more clear in what I mean, this is a screenshot explaining what I have said. Once again, my problem is how to show the table cells when they are in edit mode. All values are changeable, and none of them have any kind of presets to choose from, they are all based on text written by the user. This part of the app is basically a CRM system, where you can edit all the information about yourself.
What you are trying to do is pretty standard for Dynamic Prototype cells (as opposed to the Static Cells you probably used to make that screenshot). You're best bet is probably going to be to just watch a couple of these tutorials.
To give a quick summary, you're going to put all the strings you want to show in the Value part of your screenshot into an NSArray. There are UITableViewDelegate and UITableViewDataSource methods that will automatically handle putting the ith item in the array into the ith cell (so the 3 item in the array will go into the 3rd cell, etc.). To allow the user to edit things, you're going to want to make a whole new second screen (this is where you're UITextfields will be). When the user goes back from the second screen to your table screen, you replace items in the array with whatever the user entered in the UITextfield, then tell the UITableViewDelegate methods to reload the table with the new Values.
Most of the tutorials I linked to probably aren't going to have anything about having multiple Groups, so I'll add a bit on that here (what follows will probably only make sense if you watch the tutorials first, so I'd suggest following along with the tutorials, then coming back here afterward and making the changes I'm about to suggest). The NSIndexPath that is sent to the tableView:cellForRowAtIndexPath method contains information on the cell row and section. Each "Group" is it's own section (I'm really not sure why Apple decided to use two different names for the same thing, but so it goes). The easiest way to do this is to have different arrays for each section (so you'll have lines for NSMutableArray *firstSectionArray = [[NSMutableArray alloc] init];, NSMutableArray *secondSectionArray = [[NSMutableArray alloc] init];, and so on). Then, at the very top of your tableView:cellForRowAtIndexPath method, you put some if statements in to see what section of the table you are "building", and use values from the correct array accordingly. Something like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
// bunch of stuff from the tutorials here to create "cell"
[cell.detailTextLabel.text = firstSectionArray objectAtIndex:indexPath.row];
// bunch more stuff
}
else if (indexPath.section == 1)
{
// bunch of stuff from the tutorials here to create "cell"
[cell.detailTextLabel.text = secondSectionArray objectAtIndex:indexPath.row];
// bunch more stuff
}
// else if (keep going for however many sections you have)
return cell;
}
I wonder why cellForRowAtIndexPath function is called when scrolling the UITableView. Does it mean on every scrolling cell configuration code runs again? I have a slowness problem when scrolling the table.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CountryCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
NSString *continent = [self tableView:tableView titleForHeaderInSection:indexPath.section];
NSString *country = [[self.countries valueForKey:continent] objectAtIndex:indexPath.row];
cell.textLabel.text = country;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
As others have said, yes, when each new cell is about to scroll onto the screen, cellForRowAtIndexPath is called for each cell. While this may strike you as a performance hit, the alternative (for iOS to create all of the cells up-front) would be worse: If you're finding it slow to create one cell, imagine having to create hundreds, many of which the user may never see. Anyway, the just-in-time nature of cellForRowAtIndexPath is probably more efficient, both in terms of the up-front performance hit, as well as in terms of precious memory consumption on a small mobile device.
In terms of slow performance, there's nothing here that could be the culprit. A lot of us have cellForRowAtIndexPath methods that are significantly more complicated and speed is not an issue. Maybe the there are more efficient alternatives to a valueForKey lookup that I might advise if you were doing millions of lookups, but for one cell the difference would not be observable to the human eye. I think you have to look elsewhere. Is titleForHeaderInSection doing anything strange (i.e. something other that just looking up a value in an array)? Maybe you can share that with us. Maybe something in the UI. Maybe you should run this through the Profiler's "Time Profiler" and maybe something will stick out. But a cellForRowAtIndexPath this simple should result in a perfectly smooth user experience.
When you scroll the table, cells that dissappear are discarded and cells that appear must be created and configured using cellForRowAtIndexPath.
Or better said - cells are not discarded, they are moved to a queue of cells that can be reused and no new cells are created - the reusable cells are taken from the queue and reconfigured.
The method cellForRowAtIndexPath gets called for each and every row in your UITableView, when a row disappears from the visible view, its memory is cleared and a new cell that is coming into visibility, for that new memory is allocated (or a older one might be re used) and the new cell is configured using cellForRowAtIndexPath method.
It comes to know from this method only that what needs to be configured for this particular cell.
It is slow may be because of for each cell you are performing some heavy operation.
It's correct that cellForRowAtIndexPath is called when scrolling the table, that's exactly what it's for. Every time a new cell shows up during scrolling, a UITableViewCell instance should be created or re-used, and then configured.
Slowness is usually caused by performing an expensive operation in the configuration phase. In your case, the potentially slow operation would be the country lookup.