Editable UITableView design - ios

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;
}

Related

A lot of Switch cases in cellForRowAtIndexPath

I have a question regarding set up table view with hard coded data. Sometime developer face with situation when you need to display data that is received not form DATA MODEL layer, but stored as hard coded information.
What I mean
For example when you build Settings screen, there can be some UITableViewCell like:
CustomTableViewCellName - Configuration data: placeholder name/surname, icon.
CustomTableViewCellPhone - Configuration data: phone placeholder, icon.
CustomTableViewCellLogout - Configuration data: logout/sign out text, icon.
Let's assume I have 3 table view section and each section contains 5 different cells that I described above as example of different cases.
if we will implement all these cells in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section)
..... A LOT OF CASES THAT CONFIGURE EACH CUSTOM CELLS
As you see there is no DATA MODEL only placeholders and icon images that will be stored in UITableViewCell as a default data for the labels, textviews image views and etc. I don't say about model here as about model USER for example that stores user name, surname and etc. I mean that I need to store some default data somewhere, maybe in a dictionary. Actually a placeholder is just a placeholder, though it has a TEXT and TEXT is a data =)
In this case we have data for configure default state of views and data that will use for example as model USER.
What I worry about that I will have a big Switch and very Massive View Controller.
I would like to create few methods that will represent sections with a switch using indexPath.row in them:
- (UITableViewCell *)cellsForSection1:(NSIndexPath *)ip
- (UITableViewCell *)cellsForSection2:(NSIndexPath *)ip
- (UITableViewCell *)cellsForSection2:(NSIndexPath *)ip
and move these methods to Interactor that will return for me a cell. I know that Interactor should prepare only data and push it over Presenter to a View.
Maybe I overthink a problem. I just don't want to have a big switch in VC =) I understand that there is no right solution. But maybe someone faced with it.
One more time in my case the data are the default cells that I want to configure somewhere to prevent overload view controller. Yea maybe it looks crazy that cells are data, but look at ViewController that contains 1000 lines of code for configuration cellForRowAtIndexPath is also crazy =)
If your data is static, then instead of Switch cases I will prefer to use enumerations. Use Enums and in its body give tag to your each section and then in cellForRowAtIndexpath enumerate this Enum and display your data accordingly.
Consider creating a seperate class to act as your tableview data source and then have your view controller instance one. That way you avoid having an overly large VC class and your code is more easily unit testable.
switch(model.id)
case cellKind1:
cell = [tableview dequeue..];
//do
cell.data = model
//don't do like this
cell.title = model.title;
cell.abc = model.abc

Pattern for no data in a section in collectionView/TableView

I am contemplating on what is the recommended way to display a notice that there is no data for a specific section in a collectionView/TableView
One way is to create a special cell and to put that in instead of the data cells. That feels odd, since the "empty notice" cell doesn't correlate with the data, which means I would need to spread a lot of conditions in didSelectItem, configuring the cell, etc
Using https://github.com/dzenbot/DZNEmptyDataSet is appropriate only when the entire view is empty, not in a specific section
Another way would be (which is what I do now) to insert a UIView into the place where the data would be as a subview of the collectionview, But this also would require maintenance when reloading the data, scrolling, tapping. Also this requires calculation of where to place the view, which means I need to change it per collectionview, since it is not part of the collectionviewlayout
Is there a recommended pattern to deal with these situations?
Maybe you shouldn't have that section at all if the section contains empty data. Reduce section count by 1 at - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView datasource method and recheck the cell to be displayed during - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath datasource method.
In reply to you, I think you would have some type of "Add" button to add comments, photos, friends, etc. Make the option to add a comment or photo less obtrusive than taking an entire tableView row, and omit the comment or photo section, if there are no comments or photos.
If you put an "Add comment" or "Add photo" row while displaying details, users will likely get tired of unnecessarily reading that, as they use the app.
Note that tableViews generally handle insertion in editing mode, and when not editing, no section is shown if it is empty.

How to create a repeatable view in iOS and XCode

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!

How is tableView:cellForRowAtIndexPath: protocol method updating cell content in Cocoa Touch?

So I'm using the required protocol methods from UITableViewDataSource to display rows on my application. It's working fine and everything but the book I'm learning from doesn't show me exactly how these rows are being updated.
In the method below there is no for loop so I'm wondering if the updating of the rows is done in a for loop in the background or something?
If not is it just updating all rows at once? So let's say in the other required method tableView:numberOfRowsInSection: I'm returning an int with the value of 5. Doe's the method below just take that info and say ok you have 5 rows I'll set the textLabel text for each of them in one go?
I'd like to get a deeper understanding of this.
Code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of UITableViewCell, with the default appearance/style and name
// a reuseIdentify, used to identify cells with the same content
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
// Use the index path row number to grab the BNRItem out of the allItems an
BNRItem *p = [[[BNRItemStore sharedStore] allItems] objectAtIndex:[indexPath row]];
// Set text label of cell using the BNRItem stored in "P".
[[cell textLabel] setText:[p description]];
return cell;
}
Kind regards
The method is called by the table view whenever it feels it needs to update a cell. So, if you have five rows on screen, it will call it for those five rows (and maybe a couple of extra). Then, as you scroll, it will call it for rows that are about to appear on the screen. Cells that have scrolled off screen will be added to the reuse pool, limiting the total number of sub views that need to be created fresh and added to the table.
This is a common pattern in Cocoa; to adapt an existing control (like a table), you wouldn't subclass it, you'd configure a separate object (in this case, the datasource) which implements various methods. When the control needs to know something, it calls the relevant method on your specially configured object. It's basically the delegate pattern, except a table view already has a delegate, and the data source does a slightly different job.
By the look of your sample code you're using the Big Nerd Ranch book - their Mac OS X book had one of the best descriptions of subclassing versus delegation that I've read. Something like:
Robocop is a subclassed human. Every part has been replaced with a custom one. Michael Knight uses a powerful delegate object (KITT) instead.
Which do you think is the more lightweight and flexible design?
jrturton covered the answer pretty well.
I would like to add some thoughts.
You are thinking procedurally: Write a for-loop that fills your UI with content from an array.
iOS is an event-driven OS, and Objective C is an object-oriented language. Events happen in the OS, and in response messages get sent to objets.
A table view is an autonomous object that does things on it's own. When a table view is added to the current screen's view hierarchy, it wakes up and figures out what content to display by asking the data source how many sections of data it has, and how many rows per section. It also asks how tall each cell should be. Once it has that information, it decides which items it should display, and asks the data source for cells to display.
If the user scrolls the table view it will recycle cells as they go off-screen and ask the data source to configure new cells for newly exposed data.

Toggle showing/hiding child table cells iOS

I'm trying to create a UITableview that contains STATES and CITIES. To start with, I'd like the table to display only the STATES, with each value having a downward facing disclosure arrow on the right hand side of the cell which lets the user know it has values underneath it. If the user clicks the arrow, the table would expand to show the CITIES associated with the selected STATE. The user could then either select a CITY, or click on a upward facing disclosure arrow which would then "hide" the CITIES.
I've downloaded and reviewed Apples "Table View Animations and Gestures" example.
I was hoping someone might know a simpler way of accomplishing what I'm asking for.
http://developer.apple.com/library/ios/#samplecode/TableViewUpdates/Introduction/Intro.html
Good Job on explaining exactly what you want to do. This is how I would tackle this, I figure it would be easier to explain it without giving you meaningless code and answer any coding concerns you might have after.
I would set up the table view data source to be an array of arrays. The idea would be that every index in the array would represent a state and therefore contain a number of cities, hence ever index contains an array (array of cities). The parent array(of states) will be empty at the beginning, but the arrays(of cities) that will later fill it would be populated with the appropriate cities.
I would then set up the table view to contain sections (I a tempted to put code here, but read along). The sections would represent the cities. You can then fill out the table view with sections using (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section delegate. On the first run you can fill the actual table with one cell per section (maybe with the word "expand" or "more" and your down arrow). When the user touches that cell you can use the didselectrowatindexpath to know which section was touched (the indexpath object contains the section), now that you know which section it is, just modify the data source by adding the array of cities for that section of the array of arrays (parent array) and update the tableview.
This essentially gives your tableview an according style and feel. It is very easy to implement but requires a little bit of code. I have done this and am willing to provide the code you might need, the trickiest part would be to update the tableview in the correct way.
EDIT CODE:
I did a simple example following above explanation. The code is on Github , feel free to ask any questions about it.
I think a better way to represent this information would be to have a UITableView containing the states with each state showing a tiny arrow pointing to the right. Then, when a user clicks on the state, load the next UITableView that shows all of the cities in that particular state. Using a UIViewController for pushing the City list will allow users to easily return to the States list.
This approach will make much more sense to iOS Users, because this is how they expect Tables to work.
There are many tutorials explaining how to use UITableView. Here is a link to a site with many UITableView tutorials. The tutorial I linked to explains pushing a UIViewController on to the stack so that there is a simple back button back to the State list.
Normally such thing is done by seguing to another view and showing more details about cell.
What you want you could achieve by actually making custom UITableViewCell which would contain UIButton with arrow image and UITableView. In case button is clicked for the first time you could reload your cell and create inner UITableView which could show cities. Another click would simply reload cell again and not return inner UITableView at all. You also need to keep selected state somewhere because you may have to reload previous cell and hide inner table in case arrow in other cell is clicked.

Resources