Assigning a separate user interface for every table view item - objective-c

Can someone tell me how to assign a interface to a table view element, using storyboards? I'm making a medical calculator that has different calculators for every equation, and I need help making code that points a element to push to another interface. This is because for every equation, there are different fields to fill out (such as age, oxygen levels, whether someone has diabetes or not, height, etc.) Not every equation needs the same fields.
I have tried doing this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Deselect row
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Declare the view controller
UIViewController *anotherVC = nil;
// Determine the row/section on the tapped cell
switch (indexPath.section) {
case 0:
switch (indexPath.row) {
case 0: {
// initialize and allocate a specific view controller for section 0 row 0
anotherVC = [[BmiViewController alloc] init];
break;
}
case 1: {
// initialize and allocate a specific view controller for section 0 row 1
/anotherVC = [[AaOxygenGradientViewController alloc] init];
break;
}
}
break;
}
}
But after doing this, it refers back to what was originally in the storyboard document (which is empty because I have created the interface programmicatally), instead of showing my test alert popup.
Also, is it possible to maybe make a bunch of table view cells, then have every one segue to every other view controller in the storyboard?
Thanks a lot in advance!

First, you are running deselectCellAtIndexPath? What is the reason for this? If you are just trying to remove the blue highlight then it's better to change the UITableViewCellSelectionStyle (or something like this) of the cell.
I'm not sure what you're asking for the first part but for the segue part then yes.
In Storyboard set up the segues from the tableViewController to each other VC that you want to segue to and give them all sensible identifiers.
i.e. medicalCalulatorSegue
graphSegue
userDetailsSegue
etc...
Then in your didSelect method you will have something like...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//some stuff...
switch(indexPath.row) {
case 0:
[self performSegueWithIdentifier:#"medicalCalulatorSegue"];
break;
case 1:
[self performSegueWithIdentifier:#"graphSegue"];
break;
case 2:
[self performSegueWithIdentifier:#"userDetailsSegue"];
break;
}
}
This will then segue to each of the different view controllers depending on which cell is selected.
The reason for not deselcting the cell is that in your method prepareForSegue you can then still access the indexPath of the selected cell...
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];

Related

Updating subviews in cells on a UITableView

I'm developing an application in iPad 6.0 using Storyboards.
Let me first explain my goal. I'm trying to achieve a Master-Detail (SplitViewController-like) View Controller using 2 UITableViewControllers.
The first UITableView("Master"), let's call this HeaderTableView, as the name implies, lists down the Headers for the...
...Second UITableView("Detail"), let's call this the EncodingTableView, which contains a programmatically changing CustomTableViewCell (subviews contained within each cell may be a UITextField, UIButton or UISwitch).
See EncodingTableView.m
- (void)updateEncodingFields:(NSArray *)uiViewList
{
// Add logic for determining the kind of UIView to display in self.tableView
// Finally, notify that a change in data has been made (not working)
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *encodingFieldsTableId = #"encodingFieldsTableId";
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:encodingFieldsTableId];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:encodingFieldsTableId];
}
// Change text in textView property of CustomTableViewCell
cell.encodingFieldTitle.text = uiViewList.title;
// added methods for determining what are to be added to [cell.contentView addSubView:]
// data used here is from the array in updateEncodingFields:
}
My HeaderTableView.m, contains the didSelectRowAtIndexPath to update the EncodingTableView
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (![selectedIndexPath isEqual:indexPath]) {
selectedIndexPath = indexPath;
[self updateDataFieldTableViewForIndexPath:indexPath];
}
}
- (void)updateDataFieldTableViewForIndexPath:(NSIndexPath *)indexPath {
[self.encodingTableView updateEncodingFields:self.uiViewList];
}
Question
- Data is all ok but why doesn't EncodingTableView "redraw"ing the fields? My
suspicion is that reusing cells has something to do with this but I just can't figure out why.
Screenshots on the result:
Initial Selection in HeaderTableView
Second Selection in HeaderTableView
What I've tried :
I kept seeing suggestions such as [UITableView setNeedsDisplay],
[UITableView reloadData] and [UITableView setNeedsLayout] but none of
them worked.
Removing the reuse of tableViewCells works fine but this causes parts of my
CustomTableView.encodingFieldTitle to disappear. Not to mention that this might cause performance issues if I were to drop reusing cells.
Restrictions:
I know that a good idea is to use a SplitViewController but this is just a subpart of my app (hence not the RootViewController).
Finally, thanks for reading such a long post. ;)
It looks like you are most likely adding subviews inside tableView:cellForRowAtIndexPath:.
The issue is that if you use cell reuse then are not always starting from a blank slate inside tableView:cellForRowAtIndexPath: instead you can possibly be given a cell back that has already been configured once. This is what you are seeing, a cell that has previously had labels added to it is handed back to you and then you add some more labels over the top.
There are a few way to deal with this:
(My preferred option) Create a subview of UITableViewCell with these extra sub views available as properties.
Ensure the cell setup is only done once
A great place to do this is when you actually create a cell when one does not already exist e.g. inside the if (cell) check
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:encodingFieldsTableId];
// add subview's here and give them some way to be referenced later
// one way of doing it is with the tag property (YUK)
UILabel *subView = [[UILabel alloc] initWithframe:someFrame];
subView.tag = 1;
[cell.contentView addSubview:subView];
}
UILabel *label = (id)[cell.contentView viewWithTag:1];
label.text = #"some value";
One problem i can see in your code is that the cell identifiers used are different in tableView cellForRowAtIndxPath function.
While dequeueing you are using this identifier - > "encodingFieldsTableId"
&
while creating a cell you are using this identifier - > "dataFieldUiGroupTableId".
Ideally these two identifiers should be same !!!
Try adding,
cell.encodingFieldTitle.text = nil;
Before if(cell == nil)
So that whenever your cellForRowAtIndexPath method is called, the string already present in the cell you are going to reuse will get deleted and the new text in uiViewList.title will be displayed.

table view data loading issue

I have a doubt in table view. I have placed a label & switch in table view cell. I set the switch value as NO by default. Now when table view is loaded in simulator the table view displays switch with NO value. Now I selected switch value as YES. But table view uses dequeue reusable cell method Property when table view is scrolled objects will reload every time now what will be the switch value will it be NO or YES?
It will be YES.
One more thing on scrolling tableview it not call reload method. Its just reusing already created tableview cells if you are using deque reusable cell method Property.
TableViewCells get destroyed and created where necessary when the table view gets scrolled.
When a cell gets destroyed as it goes out of the visible screen area, your switch control being a subview of the cell also gets destroyed.
So when you scroll back to one of your previously set switches, what you're actually seeing is another instance of a UITableViewCell with a switch view added to it, and so it looks like the switch value changed back to NO.
What you need is a third thing that remembers what the value of each switch should be in each row. If you're display a table of Core Data entity information, then perhaps you can define a property for your entity like "active". In that case, every time you change a switch value, you set your core data entity "active" property to the switch value e.g.:
-(UITableViewCell *)tableView:(UITableView)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
if(cell == nil)
{
// set switch control tag number (don't use 0)
}
// get switch control by tag number
// pseudocode
Engine *myEngine = [arrEngine objectAtIndex:indexPath.row];
mySwitchControl.active = myEngine.active;
...
}
// when you change switch value, remember to update your core data entity value
-(void)updateSwitchValue:(BOOL) newValue
{
// update value
}
Otherwise, you can simply use a NSMutableArray of bool values to identify which row each switch should be YES or NO:
// my header file (.h file)
#interface
{
NSMutableArray *arrSwitchValues;
}
// my implementation file (.m file)
-(UITableViewCell *)tableView:(UITableView)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
if(cell == nil)
{
// set switch control tag number (don't use 0)
}
// get switch control by tag number
// pseudocode
BOOL switchVal = [arrSwitchValues objectAtIndex:indexPath.row];
mySwitchControl.active = switchval;
...
}
// when you change switch value, remember to update your array
-(void)updateSwitchValue:(BOOL) newValue
{
// update value
}
Hope that helps.
to guarantee not have duplicated cells:
use this code in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
as follows :
for(UIView *v in [cell subviews])
{
if([v isKindOfClass:[UILabel class]] ||[v isKindOfClass:[UISwitch class]])
[v removeFromSuperview];
}

Reload Data in TableView and group

i have a normal table view, and i want to reload the data and make de table separated with groups changing the tableview style to group,i was trying to use the method to give a title for each header its goes ok,but only if i init with it,not for change in runtime, is that possible,or i have to load another view?
-(NSString *) tableView: (UITableView *) tableView titleForHeaderInSection: (NSInteger) section {
if(group){
switch (section) { case 0: return #"A"; break; case 1: return #"B"; break; case 2: return #"C"; break; case 3: return #"D"; break; case 4: return #"E"; break; } }return nil;
}
i try to put a boolean to verify the time to reload data but didnt work....
Well I think tableView:titleForHeaderInSection: only gets called when loading the UITableView or when reloading it. So if you want to change it a runtime, you could have an NSArray that holds the titles for all the headers and have the tableView:titleForHeaderInSection: use that array.
return [YourArray objectAtIndex:section];
And you can modify this array wherever you want in your code. You would have to call [YourTableView reload] anyways to fire up the delegate and get the headers titles updated in your view.
I don't know if you still need the information bellow so I'll leave it here.
To reload a UITableView all you have to do is call [YourTableView reload];.
As for groups, that's a UITableView style. You can modify this either through IB in the Attributes Inspector in Table View > Style > Grouped. Or programmatically using UITableViewStyleGrouped in the TableView's init.
For example:
YourTableViewController *foo = [[YourTableViewController alloc] initWithStyle:UITableViewStyleGrouped];

Change UITextfield on a detailview by clicking on different Cells

This is what I have so far:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row)
{
// row 0 -> DetailViewController1 anzeigen...
case 0:
{
TableViewController *fvController = [[TableViewController alloc] initWithNibName:#"TableViewController" bundle:[NSBundle mainBundle]];
fvController.selectedCellItem = selectedCellItem;
[self.navigationController pushViewController:fvController animated:YES];
[fvController release];
fvController = nil;
}
break;
default:
break;
}
So my question is: How can I change a UITextfield of the detailView by selecting a cell. Thank you for your help so far;)
Do you want to edit the text in a UITextField in a cell? If so, there are a few ways you can do this, I'll touch on two of them.
First, why don't u just edit the cell right from within the tableView? This entails you to keep track of each cell's contents but if you always have a custom cell with a UITextField in it, you're half way there.
If you want to make changes to the text in a regular cell, then the user selects it and you give them an edit screen, this is a lot simpler. You can have your tableView push a view controller with a textField that has the text from your cell. They can edit this textField, and then press save. When they save, pop the view off the stack, save the changes to the data model, and update the tableView.
I'm not sure exactly what you're trying to do, but having a textField in a cell and then making edits in a detail screen seems redundant.
If you provide us with a little more info, I'm sure we can help.
UPDATE based on your clarification
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row)
{
// row 0 -> DetailViewController1 anzeigen...
case 0:
{
ViewControllerName *vController = [[UIViewController alloc] initWithNibName:#"ViewControllerName" bundle:[NSBundle mainBundle]];
vController.textField.text = //set the text here from the data source. Assuming textField is an property of vController.
[self.navigationController pushViewController:vController animated:YES];
[vController release];
}
break;
default:
break;
}

How to use a UITableView to return a value

How would I use a tableView as a value selector?
So I have a series of input fields and what I want is when you select a cetian field it opens a tableview of options that you can pick from as a value for that field.
Upon selecting an option it returns to the previous View with the selected value filling that field.
This is what I do, similar to the Settings > General > International > Language table view in the iPhone/iPod.
The user can tap a row and a check mark will appear. The view is dismissed when "Done" or "Cancel" is tapped.
First, create a UITableViewController that will display your options. Have a toolbar on the top with a Cancel and Done button. Also have these properties:
SEL selector; // will hold the selector to be invoked when the user taps the Done button
id target; // target for the selector
NSUInteger selectedRow; // hold the last selected row
This view will be presented with the presentModalViewController:animated: method so it appears from the bottom of the screen. You could present it in any other way, but it seems kind of standard across iPhone applications.
Before presenting the view, set target and selector so a method will be called when the user taps the "Done" button.
Now, in your newly created UITableViewController you can implement the thetableView:didSelectRowAtIndexPath:` method as:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark; // show checkmark
[cell setSelected:NO animated:YES]; // deselect row so it doesn't remain selected
cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:selectedRow inSection:0]];
cell.accessoryType = UITableViewCellAccessoryNone; // remove check from previously selected row
selectedRow = indexPath.row; // remember the newly selected row
}
Also implement cancel and done methods for the toolbar buttons:
- (IBAction)done:(UIBarButtonItem *)item
{
[target performSelector:selector withObject:[stringArray objectAtIndex:selectedRow]];
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)cancel:(UIBarButtonItem *)item
{
[self dismissModalViewControllerAnimated:YES];
}
You should use UITableViewDelegate's tableView:didSelectRowAtIndexPath:, remember the value somewhere in another object (a share instance/singleton maybe? - depending on your architecture) and then dismiss this table view.
I implemented a ViewController for Date pick.
I create a protocol to return the date picked to the previous view.
#protocol DataViewDelegate
#optional
- (void)dataViewControllerDidFinish:(NSDate*)dateSelected;
#end
...
- (void) viewDidDisappear:(BOOL)animated
{
if ([ (id)(self.delegate) respondsToSelector:#selector(dataViewControllerDidFinish:)])
{
[self.delegate dataViewControllerDidFinish:self.data];
}
[super viewDidDisappear:animated];
}
In the picker view you can use the
tableView:didSelectRowAtIndexPath:
to select the row you want. Here i set the data property.
The previous view is the delegate for the protocol.