How can I store my UITableViewCells in a NSMutableArray? - iphone

Basically I'm making a list view that you can add things to the top of. The best way I can think of doing this is to store the UITableViewCells themselves in a NSMutableArray — Because I can simply pull them from the array them with all their data inside the object, and this list view will never be over 10 cells long.
Also note that I'm using Storyboards, hence the initWithCoder use.
The following code is what I'm trying, and it doesn't work:
// This is where my NSMutableArray is initialized:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
if (!_CellsArray) {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"TestCell"];
_CellsArray = [NSMutableArray arrayWithObject:cell];
}
}
return self;
}
//UITableView Delegate & DataSource Methods
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"TestCell"];
[_CellsArray insertObject:cell atIndex:0];
return [_CellsArray objectAtIndex:indexPath.row];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 10;
}
I realize I may be approaching this in the wrong way, that's why I'm here though :)
Thank you.
edit: fixed a type in the code (TimerCell -> UITableViewCell)

Let's look at the order things get called in and what happens.
Your view controller is unarchived, so your initWithCoder: method is called. This method creates a mutable array and puts one instance of TimerCell into it. Said instance is not further configured (unless you've overridden initWithStyle:reuseIdentifier: to do some configuration).
Your data source method tableView:numberOfRowsInSection: is called, and it tells the table view there are ten rows.
Thus, your tableView:cellForRowAtIndexPath: is called ten times. Each time, it creates a new instance of UITableViewCell and inserts it into your mutable array. (After ten calls, your mutable array contains one TimerCell at index 10 and ten UITableViewCells at indices 0-9.) It does nothing to configure the cell's contents or appearance, then it returns the cell at the specified row index. On the first call, you're asked for row 0, so the cell you just created and inserted at index 0 is returned. On the second call, you're asked for row 1, so the cell at index 1 in your array is returned -- since you just inserted a new cell at index 0, the cell you created on the last call has shifted to index 1, and you return it again. This continues with each call: you return the same unconfigured UITableViewCell ten times.
It looks like you're trying to out-think UIKit. This is almost never a good thing. (It's been said that premature optimization is the root of all evil.)
UITableView already has a mechanism for cell reuse; it's best to just keep track of your own cell content and let that mechanism do its thing. I took so long to type this that other answers have been written describing how to do that. Look to them, or to Apple's documentation or any third-party UITableView tutorial.

Why don't you just store the cell information in an array. Then in the -cellForRowAtIndexPath: method, just extract the data needed to change each cell.
Here is a simple example:
//Lets say you have an init like this that inits some cell information
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
cellArray = [NSArray alloc] initWithObjects:#"firstCell",#"secondCell",#"thirdCell",nil];
}
return self;
}
//then for each cell, just extract the information using the indexPath and change the cell that way
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
cell.textLabel.text = [cellArray objectAtIndex:indexPath.row];
return cell;
}

Table views don't store things. Rather, they just ask for the data they want to display, and you typically get that data from elsewhere (like an NSArray, or an NSFetchedResultsController). Just store the things you want into some data container, and let the table display them for you.
// Probably your data model is actually a member of your class, but for purposes of demonstration...
static NSArray* _myArray = [[NSArray alloc] initWithObjects:#"Bob", #"Sally", #"Joe", nil];
- (NSInteger) tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [_myArray count];
}
- (UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString* CellIdentifier = #"TestCell";
// Make a cell.
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if( cell == nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Setup the cell with the right content.
NSString* aString = [_myArray objectAtIndex:[indexPath row]];
cell.textLabel = aString;
return cell;
}
Now if you want more stuff in the list, add it to your array, and you're done.
Edit: On another note, initWithCoder: isn't generally the best place to do initialization for a view controller. Reason being, at the point that it's called, there's a good chance that stuff isn't loaded yet (IBOutlets, for example). I tend to prefer viewDidLoad (don't forget to cleanup in viewDidUnload in that case), or awakeFromNib.

Related

Take data from a static label and use it to populate tableView

I'm working on an application that has multiple views, taking data from one view and storing it in a table in another. I have labels that are updated with data when a calculate button and a button that is hopefully going to store the data from those labels on their own cell in the UITable in another cell. I'm currently lost on how I would set up my UITable to create a new cell and pass the data to that cell every time the validate button is pressed.
This is basic MVC behavior. Table cells are loaded at display time in the UITableView datasource delegate methods. The data should be loaded from some type of store, in your case most likely an array.
When you want to update the data (from anywhere), simply update the data store (array).
Reload the UITableView at will with the reloadData method (or whenever the view appears).
So the idea is that you want to store your UILabels' text values in UITableViewCells on the press of a button?
If this is the case, I would store each text value as an element in an NSArray after every time your button is clicked, like so:
// Given:
// 1.) Your labels are IBOutlets
// 2.) Your labels follow the naming convention label1, label2, label3, etc
// 3.) You have an initialized class variable NSMutableArray *labels
// 4.) NUM_OF_LABELS_IN_VIEW is the number of UILabels in your view
// 5.) myTableView is an outlet to your UITableView, and its delegate and datasource are set to your view controller
-(IBAction)buttonPressed:(id)sender{
self.labels = [[NSMutableArray alloc] init];
for (int i=0; i < NUM_OF_LABELS_IN_VIEW; i++){
[labels addObject:[self valueForKey:[NSString stringWithFormat:#"label%i", i]].text ];
}
[self.myTableView reloadData];
}
And your data source methods should look something like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.labels count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:Cellidentifier];
}
cell.textLabel.text = self.labels[indexPath.row];
return cell;
}
If the UITableView is in a separate view controller, just assign the NSArray *labels to a #property on the presenting view controller.

Table View Returning Funny Error

Okay I am creating a Table View using objective c, but the data source is not working correctly...
My error:
2012-06-02 20:14:39.891 Dot Golf Scoring[195:707] *** Assertion failure in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:], /SourceCache/UIKit/UIKit-1914.85/UITableView.m:6061
2012-06-02 20:14:39.895 Dot Golf Scoring[195:707] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
My Code:
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 16;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return #"Comments On Your Round";
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.textLabel.text = #"Text Label";
return cell;
}
Why is the table view not getting filled with this fake data???
You are never initializing cell. 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 a noob.... let me explain
First of try picking up the book:
The Big Nerd Ranch Guide
What you are thinking is that dequeueing is basically initializing right? NO! Dequeuing is basically nilling 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
I don't think you read the Table View Programming Guide or understood the reuse mechanism of UITableViews ;)
Cells in UITableViews are reused/recycled, to avoid reallocating an instance of the UITableViewCell class each time you need a cell. This is because UITableView needs a lot of reactivity, especially when scrolling the tableview as the scrolling needs to be fast, and allocating a new UITableViewCell instance each time would make the tableview hang for a second while the instance is created.
So the idea behind UITableViewCell reuse mechanism is to allocate the minimum amount of cells, and each time you need a cell, try to reuse/recycle a cell that was previously allocated but is no longer user (because it is offscreen since you scrolled).
But if there is no cell available to reuse, you need to allocate one yourself!.
You forgot to do this part in your code, that's why you end up returning a nil cell, which throws the exception.
So the typical code to do this is :
static NSString* kCellId = #"Cell";
// First, try to reuse a cell that was previously allocated
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:kCellId];
// here, if a cell is returned, that means that we have an old cell
// that was used before but is no longer onscreen (so we can recycle it
// and just actualize its content)
// but if cell is nil, this means the UITableView didn't have a cell available to reuse
// so we need to create a new one
if (cell == nil)
{
// So if we didn't have a old cell ready to reuse that have been returned, create one
cell = [[[UITableViewCell alloc] initWithReusableIdentifier:kCellId] autorelease];
// And configure every properties of the cell that will be common to every cell
// and won't change even if the cell is recycled, eg:
cell.textLabel.textColor = [UIColor redColor];
cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
// etc
}
// And at this point, we have a cell, either newly created or that have been recycled
// So we configure every property that is row-dependant and change for each row, eg:
cell.textLabel.text = [myTextsArray objectAtIndex:indexPath.row];
NB: I never used storyboard but AFAIK, when you use storyboard, you don't need to have the "if" statement and create the cell when no reusable cell is avaiable, as storyboard will create it for you using the cell design in your storyboard. But this is the only case when you don't need to allocate the cell youself.

Disable cell reuse for small fixed-size UITableView

I have a small, fixed-size table, and I want to load UITableView entirely into memory, and never reuse cells if they scroll out of view. How do I achieve this?
I'm not using a UITableViewController; just a simple UIViewController that implements the proper protocols (UITableViewDataSource and UITableViewDelegate).
Set nil for reuse identifier in the line
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
Or just remove the line and add,
UITableViewCell *cell = nil;
Just do not implement the method UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SomeID"]; and none of your cells will be reused. Each time it ask for a cell you create a new one and configure it.
You should pass nil in the method initWithStyle:reuseIdentifier: if you don't want to reuse cells but keep in mind the performance. As long as it is good, you should be ok passing nil.
First three answers are completely correct, you just need to make sure that you do not call dequeueReusableCellWithIdentifier function on a UITableView. In addition to this, you can simply allocate the cells in code. First you will need an instance variable that will store the pointers to your cells (I will assume you use a normal view controller):
#interface MyViewController
#property (nonatomic, strong) NSArray* myTableViewCells;
#end
Then you can lazily instantiate this array, by overriding it's getter:
- (NSArray *)myTableViewCells
{
if (!_myTableViewCells)
{
_myTableViewCells = #[
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil],
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil]
];
}
return _myTableViewCells;
}
Add more cells into array if you like, or use NSMutableArray. Now all you have to do is to wire up this array to proper UITableViewDataSource methods.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.myTableViewCells.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = self.myTableViewCells[indexPath.row];
//
// Add your own modifications
//
return cell;
}
This makes for a much cleaner code, less prone to memory leaks (static variables get deallocated when program ends, so why are we keeping table view cells in memory if the view controller that is displaying them is already gone?).
The addition of new cells is also much easier (no switch or if statements required) and code is more nicely structured.
EDITED
Sometimes you need some cells to be static, for example, you need the first cell to be downloading cell which has download progress bar. and other cells to be waiting for download cells. In this case, the first cell should be accessible for pause and resume functions(outside tableView:cellForRowAtIndexPath:).
you could try to create static cells like this:
1st: subclass UITableViewCell, to create your own cell (this is a option)
2nd: crate static cell in your view controller
static YourSubclassedTableViewCell *yourCell_0;
static YourSubclassedTableViewCell *yourCell_1;
static YourSubclassedTableViewCell *yourCell_2;
static YourSubclassedTableViewCell *yourCell_3;
3rd: Init cells in viewDidLoad (viewDidLoad is a good choice to put init code)
- (void)viewDidLoad
{
yourCell_0 = [[YourSubclassedTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
yourCell_1 = [[YourSubclassedTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
yourCell_2 = [[YourSubclassedTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
yourCell_3 = [[YourSubclassedTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
// or simply
yourCell_0 = [[YourSubclassedTableViewCell alloc] init];
yourCell_1 = [[YourSubclassedTableViewCell alloc] init];
yourCell_2 = [[YourSubclassedTableViewCell alloc] init];
yourCell_3 = [[YourSubclassedTableViewCell alloc] init];
}
4th: Load cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row) {
case 0:
yourCell_0.textLabel.text = #"1st Row";
return yourCell_0;
case 1:
yourCell_1.textLabel.text = #"2nd Row";
return yourCell_1;
case 2:
yourCell_2.textLabel.text = #"3rd Row";
return yourCell_2;
case 3:
yourCell_3.textLabel.text = #"4th Row";
return yourCell_3;
default:
defaultCell....(ignore)
return defaultCell;
}
}
**As described above, cells are created once and can be accessed outside tableView:cellForRowAtIndexPath:
You also could declare cells as #property to make it accessible for other class.
Just alloc a new cell in place of dequeue no need to do any of the above. Performance implications are negligible for small tableView (< 100 cells).
Example in swift 3
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier:"Cell")
return cell
}
Cheers

UITableView not scrolling smoothly...(iPhone SDK) ..!

UITableView not scrolling smoothly...(iPhone SDK) ..!!
I have implemented UITableView DataSource and Delegate methods in an individual separate classes.(one for delegate and one for datasource) in main program i write only:
//assume that all objects are allocated
ObjTableView.dataSource=ObjDataSource;
ObjTableView.delegate = ObjDelegate;
[self.view addSubView: ObjTableView];
when i run this code , UITable view appears but when i try to scroll it, it doesn't scroll smoothly.
I have also checked that UITableViewCell doesn't redraw once the cell is initialized.
can any one tell me why this happens ? How can i solve this problem ??
From comments:
ListDataSource *ObjListDataSource = [[ListDataSource alloc]initWithArray:[[sender object] valueForKey:#"List"]];
ListDelegate *ObjListDelegate = [[ListDelegate alloc]initWithArray:[[sender object] valueForKey:#"List"]];
tblList = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, 320, 460)];
tblList.dataSource = ObjListDataSource; tblList.delegate = ObjListDelegate;
[self.view addSubview:tblList]; [tblShopList release];
More from comments:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = [NSString stringWithFormat:#"%i",indexPath.row];
UITableViewCell *cell = (UITableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0,0,320,100) reuseIdentifier:CellIdentifier] autorelease];
//custom cell code
}
return cell;
}
More Information:
I have used NSNotification which notifies to current class when parsing is complete, after receiving notification , current class method calls DataSource, Delegate methods (which is defined in a separate class file).
So UItableViewCell customization (which is in ListDataSource) and table view(in current class) both are in different classes.
A problem is
NSString *CellIdentifier = [NSString stringWithFormat:#"%i",indexPath.row];
The id needs to be the same for all cells of the same class, otherwise you never reuse them. As you can see in most examples, it is indeed a constant in most (all?) cases.
Little explaination on the reuseIdentifier: every time a cell gets out of screen, you can reuse it instead of creating a new one. To reuse it, you need a cell in queue with the same identifier as the one you pass to dequeueReusableCellWithIdentifier. The way you did, the cells are never reused, because each id is unique (they may or may not be reused in case a row reappears on screen, depending on queue size, which is not configurable AFAIK). This is why personalization of the cell should happen OUTSIDE the "cell == nil" block. Long story short, you are using the reuseIdentifier not as intendend.
I think Michele is correct, but I would also add that it looks like you are doing your cell customization where the cell gets created. What you should be doing is something more like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = (UITableViewCell)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0,0,320,100) reuseIdentifier:CellIdentifier] autorelease];
//custom REUSABLE cell code here, e.g. text color, etc.
}
NSString *cellText = [dataArray objectAtIndex:indexPath.row]; //assuming you have a simple array for your data
cell.textLabel.text = cellText;
return cell;
}
I would also add that I'm not sure why you are able to run the app with the code you have here, since UITableViewCell cell = ... is an invalid initializer. It should be UITableViewCell *cell = ....
It would be helpful to see how you are customizing your cell, since without that it's hard to see what's happening.

How to populate a label field from a selected row in a uitableview

I have a uitableview that is populated from a sqlite query.
I want to select or click on a row and then display that row's value in a uilabel field. To show the user that the row was selected.
I also want to pass that value on to different controllers that will be called later.
Here is a copy of my cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"psystem";
PSystem *psystem = [self.ppdm_systems objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
// self.accessoryType = UITableViewCellAccessoryCheckmark;
cell.textLabel.text = psystem.system_id;
return cell;
}
I took out the _label.text .... in my various experiments.
Now what is not working is the passing of the value to different controllers.
Using the example listed here, the source controller is TableViewController and is where the value is set. The target controller is DetailViewController.
I can pass the title of the tab bar in, but that's from TableView --> DetailView.
I am not sure how to pull from tableview; ie: Tableview <-- DetailView when I am in DetailView.
thx
In your UIViewController, implement:
- (MyObject *)valueForSelectedRow {
MyCell *cell = (MyCell *)[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]];
return cell.myObject;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Get value
MyObject *object = [self valueForSelectedRow];
// Update the label, assumed that _label is a pointer to a UILabel view object.
_label.text = object.myValue;
}
When you want to push a new view controller, you just call -valueForSelectedRow and then use that value to push the controller.
This is assumed that you have a UITableViewCell subclass, with a property set to some model object. When you don't have that and just set the text property, that NSString object will be your 'model' object, although it would be easier when your cells handle custom model objects.
EDIT: Thanks for editing your answer. I now have the information I need. In this line: cell.textLabel.text = psystem.system_id, you setup the cell by simply setting the textLabel's text property. This is what I described in the paragraph above. I always create a UITableViewCell subclass, with a property set the the complete PSystem object. When you assign a PSystem object to the cell, it will handle it's contents, so you can easily manage your view in the, well, view. That's a very compelled approach since you never have to look at the controller again to alter the view's contents.
However, it can be done the way you currently have it. It would look something like:
- (NSString *)valueForSelectedRow {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]];
return cell.textLabel.text;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Get value
NSString *value = [self valueForSelectedRow];
// Update the label, assumed that _label is a pointer to a UILabel view object.
_label.text = value;
}
In this case, your PSystem model has been replaced with an NSString object. For this, it's enough, but it could be so much easier to have the object itself. Okay, that can also be done by selecting the PSystem object again from the p_system array by the NSIndexPath, but things will become harder once you come up with more complex tableviews.