iPhone UI Design question - Best way to design forms? - iphone

I want to design an app that needs user to input few things like start date, end date, bunch of other options and some text comments for which I am planning to use pickers to select the data that will slide up modally. I will need to move the view up and down to make sure that the element being filled stays in focus when the pickers and keyboard slides up and down.
My question is what would be the best view to implement such a "form"? I was thinking grouped table view where I could separate the fields section wise.
Is there any other way to implement these things?
By experience or best practices, are there any better alternatives or sample code or apps out there that I can explore?
Dev.

The most iPhone-like interface for forms is going to be a grouped table view. It is what most users will expect, after using other apps which use grouped table views for adding and editing structured data.
A good practice is to create an enum (enumeration) for sections and for rows within sections, e.g.:
typedef enum {
kFormSectionFirstSection = 0,
kFormSectionSecondSection,
kFormSectionThirdSection,
kFormSections
} FormSection;
typedef enum {
kFormFirstSectionFirstRow = 0,
kFormFirstSectionSecondRow,
kFormFirstSectionRows
} FormFirstSectionRow;
...
In this example, you can use this enumeration to refer to sections by name instead of number.
(In practice, you probably wouldn't use kFormSectionFirstSection as a descriptive name, but something like kFormSectionNameFieldSection or kFormSectionAddressFieldSection etc., but this should hopefully illustrate the structure of the enum.)
How would you use this?
Here's an example of a few table view delegate methods which demonstrate how this is useful:
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
return kFormSections;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch (section) {
case kFormSectionFirstSection:
return kFormFirstSectionRows;
case kFormSectionSectionSection:
return kFormSecondSectionRows;
...
default:
break;
}
return -1;
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// cell setup or dequeue...
switch (indexPath.section) {
case kFormSectionThirdSection: {
switch (indexPath.row) {
case kFormThirdSectionFourthRow: {
// do something special here with configuring
// the cell in the third section and fourth row...
break;
}
default:
break;
}
}
default:
break;
}
return cell;
}
This should quickly show the utility and power of enumerations.
Names in code are much easier to read than numbers. When you're dealing with delegate methods, if you have a good descriptive name for a section or a row, you can more easily read the logic of how the table view and cells are managed.
If you want to change the order of sections or row, all you have to do is rearrange the order of enumerated labels in the enum construct. You wouldn't need to go into all the delegate methods and change magic numbers, which quickly becomes a tricky and error-prone dance once you have more than a couple sections and rows.

Related

How to move labels in ios

Here i need to hide the phone number, email , birthDate, anniversary date and other labels in case there is no values for those fields. How can i do this?
Many ways, starting with the simplest:
self.emailLabel.hidden = YES;
But you probably want to reformat the other parts of the view to fit the empty space. Keeping it simple, you would then do something like this:
self.phoneLabel.frame = CGRectOffset(self.phoneLabel.frame, 0, -self.emailLabel.bounds.size.height);
... and so on for anything below. But you can see how this would become tedious. The next and probably best alternative is a UITableView that adjusts it's section count based on whether some of that data is present. That would go like this. Prepare a mutable array of arrays with the parts of your model and their values.
- (void)prepareModel {
self.model = [NSMutableArray array];
[self.model addObject:#[#"Name", #"Judy"]; // get "Judy" from your data
if (/* model has email */) {
[self.model addObject:#[#"Email", #"judy#gmail.com"]; // get email from your model
}
// and so on for conditional parts of your model
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.model.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
return self.model[section][0];
}
And the cellForRow would init the cell using self.model[section][1].
What you can do is simply hide the UILabel's if the value for the NSStrings that you are putting them in is NULL/nil .
NSString *labelString;
if([labelString length]>0)
{
}
else
Label.hidden = YES;
It is probably a better idea to use a UITableView to do this, by putting the labels in rows of the tables. If the labels are empty, you can delete the table rows and iOS will dynamically resize the table height for you.

Accessing "default" methods of UITableView

I try to set up a tableView. I use standard cells for all sections' rows except in the last section (containing one row). Thus, I would also like to use the standard layout for all those sections except that special one.
A short example is the following, my "special" cell is in section 3 (there is only one row):
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (section == 3)
return 5;
return **????**;
}
At ??? I would like to return the width calculated from UITableView (just as if I did not implement the method).
[super self:tableView
heightForHeaderInSection:(NSInteger)section];
does not work. I know I can access
[tableView setionHeaderHeight]
which is by default 10 and obviously does not take into account that I have section headings for the other sections, which will require additional space. I tried that, but it will then get the sections too close (see screenshot):
(Note: the section I am interested in is the one which does not look like a cell: the one with the dates (invisible background)).
So, the easiest thing would be to hand over the layout to the standard implementation which is perfect - except for section3.
What are my options?
Just in case: there is a new constant introduced in iOS 5, called UITableViewAutomaticDimension. As the documentation says, you should return it from your delegate method when you want UITableView to use a default value.
So, the code for your case would be:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (section == 3) {
return 5;
} else {
return UITableViewAutomaticDimension;
}
}
You seem a bit confused about heightForHeaderInSection - it returns the height of a table section header (this is the "title" of a table section), not a row. iOS calls this method to ask for the height of just a single section header, irrespective of any other section headers there might be.
If you want to use the default, just return [tableView sectionHeaderHeight] for any section other than 3 - you don't need to "take into account that [you] have other section headers", as it's asking for the height of the header for section alone. It will ask again for the heights of others (and compute the relative positions with of rows and other sections automatically).
You do not have a super implementation tableView:heightForHeaderInSection: since you are not subclassing any abstract base implementation for UITableViewDelegate. The table view is instead decided if the default height should be used by inspecting your delegate implementation to see if the method is available.
It is a quite a huge concept to wrap your head around, especially if coming from Java or C#. Methods in Objective-C protocols can be optional, and their absence means use default.
Your method should probably be implemented as:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (section == 3) {
return 5;
} else {
return 36;
}
}
The default height for grouped and plain tableviews are different (22points for plain). The default values are not exposed by UITableView, not even as private methods. File bug at http://bugreport.apple.com to make this a public constant.
After overriding heightForHeaderInSection and doing a side-by-side comparison, the height for the header in the first row is larger than the rest. This isn't pixel perfect, but it's very close:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (section == 0 ) {
return 46.0;
} else if (section == myCustomRow) {
return 12345.0; // custom height
} else {
return 36.0;
}
}

Filtering UITableViewCells with animation - iPhone Development

This seems simple enough but as yet I am unable to find a solution.
Basically I have a segmented control with two options. The first is the default (and is automatically displayed on load) and when selected displays all rows in a table view. The second is a filter limiting the rows displayed. This is the exact same set-up as used on the "Recents" tab of the iPhone's Phone app that filters 'All' and 'Missed' calls.
At present I have the data loading from two different arrays. The problem is that when I swap the data there is no animation to denote that the rows have been filtered. Apple have implemented this in their Phone app but I can see no way of acheiving this.
Perhaps each cell will need to be deleted and re-added as the user switches between the two states - or perhaps setting the height of the cells that I wish to hide to 0 would acheive the same effect? Does anyone have any experience of producing this accordian-type animation?
I have looked here for some clues but am having problems rolling some code that works. Has anyone implemented this before? If so, how did you get it to work?
You can accomplish a similar effect by calling deleteRowsAtIndexPaths:withRowAnimation: and insertRowsAtIndexPaths:withRowAnimation: on your table view with a UITableViewRowAnimationFade animation.
Have you looked into reloadSections:withRowAnimation:?
The basic idea is to call reloadSections:withRowAnimation: and in your UITableViewDataSource implementation switch on the segmented control's selectedSegmentIndex.
Assuming your data is flat (only one section) it would look something like this:
- (IBAction)segmentSwitch:(id)sender
{
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (self.segmentedControl.selectedSegmentIndex)
{
default:
case 0:
return [self.allRows count];
case 1:
return [self.onlySomeRows count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
id data;
switch (self.segmentedControl.selectedSegmentIndex)
{
default:
case 0:
data = [self.allRows objectAtIndex:[indexPath row]];
break;
case 1:
data = [self.onlySomeRows objectAtIndex:[indexPath row]];
break;
}
//TODO: use data to populate and return a UITableViewCell...
}

iPhone UITableView with index can I push a different detailed view with every different cell?

I know its possible to create a table that has an index on the side and a search bar at the top that a user can type in to find an item, but is it possible to say to the table if array isEqual to "item1" push view1? I would like to push a different view with each cell. Anyone have any advice?
Sure. Just create the appropriate view (controller) depending on the cell's indexPath in tableView:didSelectRowAtIndexPath:.
Create the cell based on the index path. If you create all the cells ahead of time, store them in an array by row index. If you are creating them as needed, do something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *result;
switch ( [indexPath row] ) {
default: result = [self tableView:tableView normalCellForRowAtIndexPath:indexPath]; break;
case 3: result = [self tableView:tableView detail3CellForRowAtIndexPath:indexPath]; break;
case 5: result = [self tableView:tableView detail5CellForRowAtIndexPath:indexPath]; break;
}
return result;
}
Check out the sample programs from the book Beginning iPhone Development. You must sign up, but its free. You should look specifically at chapter 9's sample code called Nav. Shows you exactly what you need. This was my first book on the subject and was well worth it.
http://www.iphonedevbook.com/forum/viewforum.php?sid=3010c045df967353c6b3894889e6b8f5
Cheers!
Ken

How to find the row index of a UIButton is a TableCell? (iPhone)

In my app, I have a table displaying a cell in each row.
In Interface Builder, I dragged a button onto the cell, styled it as a Dark Info button, and connected it to a IBAction.
That is working fine.
Only, I want the button to behave differently, depending on the row of the table where the cell of the button is.
How would I get that row index?
I realize that I might display a lack of basic understanding of the object hierarchy, but I hope you guys will forgive me
Thanks
Sjakelien
It's definitely not easy to do if you don't have some data set up first. If you can, have an NSDictionary where the buttons are the keys and the values are the index paths, that you update whenever you return a cell from -tableView:cellForRowAtIndexPath:. Something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
[indexDict setValue:indexPath forKey:theButton];
return cell;
}
- (void) buttonPressed:(UIButton *)button {
NSIndexPath *indexPath = [indexDict valueForKey:button];
...
}
You can maintain tags. When you drag and drop the button, check the interface build and you will see "tag" property for these buttons. Assign different values for each of your button ( I assume you have different buttons for different rows, this solution will not work if you have same cell identifier for different rows ). And when you receive an event check for tag value.
I had similar problem with my work and i was maintaing NSArray for each button tag created.
In your tableView delegate and datasource methods (check the docs!) you have several methods, the best one for this is
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Drop this method in your implementation and say something like
switch (indexPath.row) {
case 0:
//set variable or do method based on row 0 (first row)
break;
case 1:
//set variable or do method based on row 1 (second row)
break;
case 2:
//set variable or do method based on row 2 (third row)
break;
}//and so on
}
another way is to change the base class of your UIButton in your view,
then using this other class wich basically extends an UIButton with an added NSInteger row #property (remember to #synthesize it).
You'll then set this property during the cell setup, and you can retrieve this property within the message method