Add row programmatically in static UITableView - ios

I have a static UITableView as shown in the first image, but now I need to add a row with a Button that when I touch it adds a dynamic cell to the table as shown in the second image, the issue is that I can't add new cells to a static UITableview.
What would be the best practice to accomplish this?

Basically static TableView is not supposed to be changed at runtime (except cell content). This is clearly mentioned in docs:
Use static cells when a table does not change its layout, regardless of the specific information it displays.
The best practice in this case is to create a dynamic TV and populate it with appropriate amount of cells. You'll need to use DataSource delegate to do so. DataSource itself is typically done through dictionaries or arrays.
E.g. you have a dict 'phoneNumbers' and a button that is supposed to add a new one.
First, you add a selector to the button in cellForRowAtIndexPath: via tag for example. Then button action is going to look like:
-(void)yourButtonClicked:(UIButton*)sender
{
[self.phoneNumbers setObject:phoneNumber forKey:numberKey];
[self.tableView reloadData];
}
//Swift
func yourButtonClicked(sender: UIButton) {
self.phoneNumbers["numberKey"] = phoneNumber
self.tableView.reloadData()
}
(sorry it's Obj-C but I'm quite sure swift isn't much different at this point)
reloadData is needed to refresh TableView layout after changes to DataSource objects are made. It's quite close to 'redraw' in this case.
On the image from Contacts App you showed object is '(555)555-5555' NSString and key is probably 'other'. You can use and store these any way you like
So after all you only need to setup numberOfRowsInSection: so that for section where you want to add cells it returns the count of objects in dictionary phoneNumbers

Related

UITableView with: Static Cells with dynamic content and dynamic cells

I have a UITableView that always has 4 sections. The first section always has 1 row, the second section always has 3 rows. Then the 3rd and 4th section can have any number of rows. Although the first two sections are essentially static cells, the content in them changes.
The first two sections are all I had to begin with so I just used static cells with outlets to the content directly in the TableViewController. Then I added two more sections which can have any number of rows.
I found that to do this, now I have to consider the whole table as using dynamic prototypes and I have to implement all the delegate/datasource classes for every cell now. In addition to that, I can no longer have my outlets (even for the first 2 sections) directly in the tableViewController but instead create custom subclasses for them so I can change their content.
This seems silly because they are so simple. Is there a better way I can do this? Maybe I can have two tableViews in the TVC, one that is static and handled the same way for the static cells and one that uses dynamic prototypes? If that's the best way, how should I implement that? I have never implemented a TableViewController before that handles more than 1 TableView.
For reference, the View is a profile view with simple data about the person's profile. The first 2 sections are just the person's picture, name, email, and description.
The second 2 sections contain 1) a list of favorited postings on the app. And 2) a list of postings that the user has created.
Here are 2 pictures showing the view:
No, fortunately Apple did add static table cell for UITableViewController but the road ends there. If you want to start modifying content you need to switch to a "real" UITableView, setup arrays and handle the cells correctly.
2 options.
Create IBOutlets to the static cells content and change the objects directly.
or...
Setup the tableView delegate methods and change content using the UITableView delegate and datasource.
class CustomCell: UITableViewCell {
#IBOutlet weak var labelName: UILabel!
}
In reference to the comment about getting the indexPath of a button click from the viewController check out the following code. Basically I created an extension of UITableView that includes the indexPathForView function. This way you can get the exact view(button) you pressed to handle any actions from your UIViewController. I prefer this technique over creating a protocol/delegate on the cell then calling back to the viewController.
#IBAction func pressedItem(sender: AnyObject) {
let button = sender as! UIButton
var indexPath = self.tableView.indexPathForView(button)!
let object = self.arrayOfObjects[indexPath.row]
// Now you can fire any action and have it related to that specific cell
// Create a HelpfulExtensions.swift class to hold your convenience extensions such as this
// MARK: - UITableView
extension UITableView {
func indexPathForView (view : UIView) -> NSIndexPath? {
let location = view.convertPoint(CGPointZero, toView:self)
return indexPathForRowAtPoint(location)
}
}
//
And might I add, EXTENSIONS ROCK!!!
Instead of using two tableViews it may be enough to setup a header view with static content. Probably it's easy to do inside the same and only one table view controller. But also it can be done separately and then a table view below with dynamic content, all within a simple view controller

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!

iOS: Adding row to tableview

I have a tableview that is based on a array of DB results, these results contains a date field. I have a custom cell that contains an image and labels. I'm trying to do:
At cellForRowAtIndexPath I verify if the date of current item (objectAtIndex:indexPath.row) has date field bigger than the last item (objectAtIndex:indexPath.row-1). If this is true: I want to add a cell filling the ImageView with a certain image, but I need to add a new row just for show this image.
How can I do this? I'm already doing this verification, but I need to add a new cell...
Do not use the cellForRowAtIndexPath to decide how many cells you want to have. At the point this method is called you should have already setup the data source to provide table view with all information needed.
Here is what you need to do. Refactor your code in a way so you:
Setup the data source first.
Force reload of the table view either by calling the reloadData method.
hey you can add the object in your data base(for example ns array) and refresh the table view with method
[tableView reloadData];
then the method cell for row at index path will be called again and it will refresh the table view's items.just make sure the method cellforrawantindexpath in your code knows to handle the new data type(make validations).
Your tableView data source should not contain any of that logic where the content of once cell depends on the content of another cell. Instead, you should have a data item for each requested indexPath and that data item should contain ALL logic necessary for the cell to be configured. If an action on that cell has an effect on how another cell should look, you apply a change to the corresponding data-item, and then call reloadRowsAtIndexPaths: for the indexPaths.
In short: configure cells ONLY in tableView:cellForRowAtIndexPath: or tableView:willDisplayCellAtIndexPath, and ONLY do configuring. Other logic should be placed in some data(-controller) object.
As suggested, you should add an item to your data-array. Then call -insertRowAtIndexPath: on the tableView. ReloadData is like a big ugly hammer that you only use when ALL of the data for that tableView changes.

iOS 7: Two different heights for cells in a table inheriting from the same UITableViewCell

I need "Two different heights for cells in a table inheriting from the same UITableViewCell".
A bit more on this. I am using iOS 7 and storyboard. I created on the story board two different UITableViewCell prototype for a UITableView with custom cells.
I then created a class, MyUITableViewCell which defines the beheaviour of the cell as well as a protocol method that is then implemented by the delegate (which is in my case is the UITableViewController class where the UITableView containing the cells is).
Now.. I would like to dynamically set the row of the cells according to whether the cells is of type 1 or type 2.
I have found this method:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// if(indexPath) refers to cell of type 1 then
// return 240;
// else return 120;
// I created a NSDictionary with data that includes the type of cell information, however I am not sure how to link indexPath to the dictionary. The keys of the dictionary are ids and not array indexes.. so I am a bit lost in here..
}
As said in the comment:
"I created a NSDictionary with data that includes the type of cell information, however I am not sure how to link indexPath to the dictionary. The keys of the dictionary are ids and not array indexes.. so I am a bit lost in here.."
I would like to find a solution to this but not sure if it is possible using only NSDictionary or if I need some other hack...
Sure you can do that. The cells in your table view are data backed, you must have access to the data that defines the table in the table view delegate.
If your table is simple with only one section then you might only need to look at indexPath.row. There must be some way to relate this value to your data, if there is not you will not be able to populate the cell in cellForRowAtIndexPath either.
Often the indexPath.row value can be used as an index into an array containing data, such as [tableDataArray objectAtIndex:indexPath.row]; In your case, if the data is a dictionary, what are the keys? Maybe you can make a key directly from the row ([NSNumber numberWithInteger:indexPath.row], since a key must be an object) or maybe you need to use an array to translate the NSNumber produced from the index paths into the keys that are used in your dictionary. If your data is pre-existing and it would be a lot of work to change it this might be the best way, otherwise think about organising your data for easy access.
An alternative would be to use a UICollectionView, where with a custom UICollectionViewLayout you can define the frame of every cell individually at the time you report attributes for the cell. However you still need a way to relate index path to the underlying data. It is more versatile than a UITableView and is possibly a more useful skill to develop with the state of iOS development today.

Adding rows to UITableView built from Storyboard

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...

Resources