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!
Related
I am new in iOS programming and I would like to create I view for the settings of my app that looks like iPhone's one (like in the picture). Some of rows will call other views when taped and other will not.
What is the best way to do that? I thought about TableView with prototype cells, but is there a best way to do it? or a best-practice for what I want to do? Or maybe a tutorial online?
The fast way in Interface Builder:
Use a UITableViewController, make it STATIC and use the GROUPED style (all in IB).
You can setup the cells to show disclosure indicators (or not) in IB also.
You can segue directly from the rows or the UITableViewController to where you want to go.
If you segue from the UITableViewController, implement the "didSelectRowForIndexPath" method and call "performSegueWithIdentifier" accordingly.
A structure like this is best by UITableView.
First you select how many sections you want, and customize each section with a data structure that you have to be filled with (Probably an array.)
Then you fill up each rows inside
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
method, and call your value from the array/dictionary that you have.
for going to a next view when clicked upon
Use the method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Hope this helps
The best way to do that is using static UITableViewCell.
See UITableView Programming.
The optimal solution here is undoubtedly UITableView. This is because firstly, you have the need to display a list of options that would have external links to other pages and UITableView is designed and used for this purpose.
In an addition to that, if you want, you can also expand and collapse the rows of your parent TableView into a Child TableView i.e a UITableView as a subview of its parent UITableView.
Put up a UITableView and populate it with UITableViewCell. That will be just fine with the requirement you have.
Hope this helps.
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.
I am trying to set up a UITableView inside of a UIViewController. I'm doing this because it allows me to add a top bar with save and cancel buttons. I'm using storyboard and static cells to model the tableview to get input from the user (think of the create new event in Apple's calendar app). I have the view in Xcode, but when running it on my phone or the simulator, the tableview does not display. Here is the simple view in Xcode:
And this is how it displays when running it:
I've read about adding delegates and setting the datasource and such, but really this is all just going to be static cells with text fields, no data being loaded. Why is this happening and what can be done to fix it? Thanks!
#Made2k It looks like you found a solution, but for others who come across this with the same issue, note that you can only use a UITableView with static cells inside of a UITableViewController. This is not a well-documented fact, but apparently only UITableViewController knows how to handle the layout of static cells, whereas a regular UIViewController just needs to implement the UITableViewDelegate and UITableViewDataSource protocols in order to handle display of content in a UITableView added either programmatically or via Storyboard/Nib.
Hopefully this will save someone some time. #Made2k's solution is fine, but does make your storyboard unnecessarily busy. If you don't need to use a UIViewController, then just do your work inside a regular UITableViewController and save yourself a headache.
If you want to use a UITableView in a UIViewController you have to make the ViewController a data source and a delegate of the TableView and then implement methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
In case of a static table, in the cellForRowAtIndexPath you'd return outlets of the static cells. For a detailed description check out this answer:
https://stackoverflow.com/a/19110821/3110536
I ended up thinking about this in a different kind of way. The problem I thought I was having was I wanted all the features that a navigation controller provides, but I needed this to be the base of the controller, i.e. nothing to go back to. I was thinking that the only way to do this was to create a UIViewController and add the table view and such in there, but what I came up with is to simply just create a new navigation controller and now this view shows up as the root view like so:
I don't know if this is the best practice, but hopefully it can help somebody else if they are having this problem.
Man, following Hack really works!
You should give it a try!
In my requirement I wanted to add buttons in my Static cells too!and Toggle the visibility of the TableView
[self.tableView setHidden:YES/NO];
and Reload it with new data
[self.tableView reloadData];
and so many things is possible with that way of doing it!
https://stackoverflow.com/a/19110821/1752988
Hope the above link would help you! (Y)
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 have a static UITableView built from a Storyboard that works well. I want to fill the first category programmatically, though, from a user-defined file
Simply put, I want to go through all the strings in an array and add them as cells for the rows of the first category. For the second category, I have a series of mildly complex cells (containing a number of labels, textfields, buttons and other controls), defined in the storyboard, that I don't feel like recreating in code.
As far as I understand, the default behaviour for a UITableView built from a storyboard is to use the nib file as an implicit datasource. If I use a custom class as datasource, my second section doesn't work. I have thought of two possible ways to fix this:
Fill my first category from the datasource and delegate the rest to the nib file. Is this possible? Is there some method to programmatically ask the nib to fill my UITableView?
Export my storyboard-built cells into code and paste this code into my datasource. This method has the disadvantage of making my second category harder to modify.
Is one of those two options feasible? Is there another option?
I would use dynamic prototype cells. Then, I would set up the ViewController as the delegate and the dataSource. I would then create a custom subclass of UITableViewCell and connect the elements of the second section to IBOutlets in the custom UITableViewCell.
If the first section wasn't something that could be done with one of the generic cell types, I would also create a custom subclass of UITableViewCell for that section as well.
I would then use the cellForRowAtIndexPath: method to set up the cells with the information that I want in them. So if my first section used FirstSectionCell and my second section used SecondSectionCell as custom subclasses of UITableViewCell my cellForRowAtIndexPath: would look like this:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.section==0)
{
FirstSectionCell *firstCell = [tableView dequeueReusableCellWithIdentifier:#"First Cell Prototype"];
//Set up the first cell.
return firstCell;
}
else if(indexPath.section ==1)
{
SecondSectionCell *secondCell = [tableView dequeueReusableCellWithIdentifier:#"Second Cell Ptototype"];
//Set up second cell.
secondCell.someLabel.text = #"whatever";
//etc.
return secondCell;
}
else
{
//if you have another section handle it here.
}
}
There are two kinds of table views when you use Storyboards:
Static
Dynamic
You're currently using the former. You define everything in the Storyboard and have very little code.
But you need to change to the latter.
You can still keep your UITableViewCells in the Storyboard; there's no need to do that in code (though you can if it makes things easier). You can refer to the template cells using the "reuse identifer."
Otherwise you've pretty much got it. You'll need to write code to implement the data source and (possibly) more methods of the table view delegate.
It's kind of fiddly switching from static to dynamic. I keep meaning to raise a Radar because I'm sure Xcode could be making it easier to do...