How to implement live word count for UITableViewCell TextView object? - swift

On another view I have this function (below) that does a live word count on a TextView object based upon the given conditions then displays it on a label. I would like to do the same for my TableView Cells. However, this function clearly won't work since it requires the 'savedHashtagTextView' (textView) and 'hashtagCount'(label) fields from the cell its going to count from. I'm not sure exactly how to make this work. Please help - I've been stuck on this for hours!
func textViewDidChange(_ textView: UITextView) {
let components = savedHashtagTextView.text.components(separatedBy: "#")
hashtagCount.text = String(components.count)
if hashtagCount.text == "0" {
hashtagCount.text = ""
}
}

You will want to create a custom UITableViewCell object with the UITextViewDelegate protocol so you can get the word count for the specific UITableViewCell UITextView. Furthermore, you will want to somehow send the word count to the ViewController so you can update the word count UILabel. You can achieve this by creating your own CustomCellDelegate protocol which requires a updateWordCount() function and then use this protocol in your ViewController.
When the word count is updated you will now want to call the updateWordCount() function which will be located in your ViewController utilizing the CustomCellDelegate protocol.
As an example here is what you will want to do:
Create a CustomCell.swift file as follows:
// Create the protocol
protocol CustomCellDelegate {
func updateWordCount(count: Int)
}
class CustomCell: UITableViewCell, UITextViewDelegate {
// Your UITextView
#IBOutlet weak var textView: UITextView!
// The cells delegate -- which we will set to self when initiated
var delegate: CustomCellDelegate?
func textViewDidChange(_ textView: UITextView) {
// Get the word count here
let wordCount = 10
// Now we call the delegate to send the wordCount to the ViewController
delegate?.updateWordCount(count: wordCount)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
textView.delegate = self
}
}
Now create the Custom Cell in your storyboard and use the CustomCell class for it along with setting its identifier to "CustomCell". Also, make sure you link your UITextView to the outlet in the CustomCell class.
Now in the ViewController which contains the UITableView:
// Use the CustomCellDelegate protocol here
class YourViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CustomCellDelegate {
#IBOutlet weak var wordCountLabel: UILabel!
// This is the function required by the CustomCellDelegate
func updateWordCount(count: Int) {
wordCountLabel.text = "Word Count: \(count)"
}
// Set up the custom Cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
// Setup the delegate
cell.delegate = self
return cell
}
}
Warning: This code is untested and I wrote it all out on StackOverflow. There may be syntax errors but aside from a few potential syntax errors this will work.
Edit:
Based on your comment the word count label is in the cell. This means we no longer need the CustomCellDelegate protocol and can make some minor changes to make this work.
CustomCell.swift:
class CustomCell: UITableViewCell, UITextViewDelegate {
// Your UITextView
#IBOutlet weak var textView: UITextView!
// Your Label
#IBOutlet weak var yourLabel: UILabel!
func textViewDidChange(_ textView: UITextView) {
// Get the word count here
let wordCount = 10
// Change the wordCount labels text
yourLabel.text = "Word Count: \(wordCount)"
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
textView.delegate = self
}
}
Make sure you utilize both the outlets in the CustomCell.
Your ViewController:
class YourViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Set up the custom Cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
return cell
}
}

Related

How to UITableView Click image and title

I've created a tableView, but when I click it, I don't get any results. I wanted to add a new feature to improve my project, but I couldn't add the videos I watched and the things I researched.
Table View
import UIKit
class ViewController1: UIViewController {
#IBOutlet weak var FoodView: UITableView!
let dogfoods = ["pork", "banana", "chicken-leg"]
override func viewDidLoad() {
super.viewDidLoad()
FoodView.delegate = self
FoodView.dataSource = self
// not tapped no see
FoodView.allowsSelection = false
}
}
extension ViewController1: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dogfoods.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = FoodView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
let dogfood = dogfoods[indexPath.row]
cell.foodImageView.image = UIImage(named: dogfood)
cell.nameLabel.text = dogfood
return cell
}
}
CustomCell
class CustomCell: UITableViewCell {
#IBOutlet weak var dogView: UIView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var foodImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
When I click on one of the cells in the picture, I want to write a larger version of the picture and a description, how can I do this? When I searched on the internet, I applied similar ones, but I couldn't get any results.
You have a few choices. You can add one or more buttons to your custom cell. You can attach a tap gesture recognizer to your cell's content view.
Probably the easiest way to respond to a tap on the whole cell is to have view controller conform to the UITableViewDelegate protocol and implement the tableView(_:didSelectRowAt:) method.
That method will be called when the user selects a cell, and you would use the indexPath of the tapped cell to figure out which one was tapped and do whatever is appropriate.
You can do that with easy and efficient way using Delegate in Swift
//create protocol as we used interface in Java
#objc protocol TableViewCellDelegate {
#objc func click(indexPath: IndexPath?)
}
// Modify your class CustomCell as:
#IBOutlet weak var dogView: UIView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var foodImageView: UIImageView!
var delegate: TableViewCellDelegate?
var indexPath: IndexPath?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
foodImageView.isUserInteractionEnabled = true
foodImageView.addGestureRecognizer(tapGestureRecognizer)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
let tappedImage = tapGestureRecognizer.view as! UIImageView
self.delegate.click?(indexPath: self.indexPath)
}
// Modify your tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) in ViewController1 as
let cell = FoodView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
cell.delegate = self
cell.indexPath = indexPath
let dogfood = dogfoods[indexPath.row]
cell.foodImageView.image = UIImage(named: dogfood)
cell.nameLabel.text = dogfood
return cell
// And now add extension add the end of ViewController1
extension ViewController1: TableViewCellDelegate {
func click(indexPath: IndexPath?) {
// open image for preview here
}
}
You can do it in many ways
use a add a UITableViewDelegate method which is
override func tableView(_ tableView: UITableView, didSelectRowAt
indexPath: IndexPath){
//your code...
}
it will called every time when cell clicked.
if you prefer to trigger any button click rather then cell click then go for delegate (Izaan Saleem already explained), or you can use NotificationCenter, but for this task I prefer didSelect or delegate solution.

Configure in TableView is not being reached

So I am trying to have a TableView displayed, but I'm currently only getting an empty tableview. Upon further inspection, I see that the configure block is not being run. Why is this?
Code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: teamCellIdentifier, for: indexPath)
print("reached")
func configure(cell: UITableViewCell,
for indexPath: IndexPath) {
print("not reached")
guard let cell = cell as? TeamCell else {
return
}
let team = fetchedResultsController.object(at: indexPath)
cell.teamLabel.text = team.teamName
cell.scoreLabel.text = "Wins: \(team.wins)"
if let imageName = team.imageName {
cell.flagImageView.image = UIImage(named: imageName)
} else {
cell.flagImageView.image = nil
}
}
return cell
}
}
TeamCell
class TeamCell: UITableViewCell {
// MARK: - IBOutlets
#IBOutlet weak var teamLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var flagImageView: UIImageView!
// MARK: - View Life Cycle
override func prepareForReuse() {
super.prepareForReuse()
teamLabel.text = nil
scoreLabel.text = nil
flagImageView.image = nil
}
}
The reason why your tableView is empty or it looks like empty and your tableView methods are not being called are two reasons.
1)- Table view heigh or width is equal to 0 and there is no need to load and display tableView, respectively tableView cell.
2)- You did not connect tableView data source and delegates to your view controller.
2.1) You can add TableView delegates and data sources through storyboard (see image down below)
2.2) You can add TableView delegates and data sources thrugh code in your view controller, like:
2.2.1) In viewDidLoad() method:
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// TableView Delegates & DataSource
tableView.delegate = self
tableView.dataSource = self
}
2.2.2) Or view tableView outlet didSet (I prefer this way, if I don't do it via storyboard):
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.delegate = self
tableView.dataSource = self
}
}
Your code has a structure like this:
// This next line defines a function called a
func a() {
print("a")
// This next line only defines a function called b:
// that is scoped inside the 'a' function
func b() {
print("b")
}
// Defining a function does not invoke the function
// That would need an actual call here like this:
// b()
}
// I can't call b here because it's nested inside the function 'a'
First you have a nested func inside function?
Take it out....
then after this line :
let cell = tableView.dequeueReusableCell(withIdentifier: teamCellIdentifier, for: indexPath)
do this:
configure(cell: cell,for indexPath: indexPath)

How do I call a different function for each TextField in a UITableView (Swift)?

I have a UITableView and my prototype cell consists of a label and a TextField. I also have a class MyClass that contains functions func1, func2, fun3, ... I have several ViewControllers that use the same tableViewCell prototype. Each viewController will have an instance of MyClass, called inst1, inst2, and inst3. When I enter text into FirstViewController's TableView I want each row to call a function from the instance of MyClass that corresponds to the row.
So when I enter text into row 1 on the FirstViewController I want to pass the data entered into the textField into func1 of inst1. When data is entered into row 2 of FirstViewController I want the data in the textfield to be passed into func2 of inst1. And so on and so forth down the rows.
I am very new to this and would really appreciate some help figuring out how to do this. Let me know if that doesn't make sense and I can try to rephrase it. I really need help with this. Thanks in advance!
*Updated question to show my code
Below is my Code:
FirstViewController.swift
extension FirstViewController: MyCellDelegate {
func MyCell(_ cell: UITableViewCell, didEnterText text: String) {
if let indexPath = tableView.indexPath(for: cell) {
if (indexPath.hashValue == 0) {
inst1.func1(one: text)
}
if (indexPath.hashValue == 1) {
inst1.func2(two: text)
}
}
totalText.text = inst1.getMyTotal()
}
}
import UIKit
class FirstViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let inst1 = MyClass()
#IBOutlet weak var totalText: UILabel!
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 11
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell") as! TableViewCell
let text = cell.cellData[indexPath.row]
cell.myTextField.tag = indexPath.row
cell.delegate = self
cell.myLabel.text = text
cell.myTextField.placeholder = text
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
TableViewCell.swift
import UIKit
protocol MyCellDelegate: class {
func MyCell(_ cell: UITableViewCell, didEnterText text: String)
}
class TableViewCell: UITableViewCell {
weak var delegate: MyCellDelegate?
public var cellData: [String] = ["1","2","3","4","5","6","7","8","9","10","11"]
#IBOutlet weak var myLabel: UILabel!
#IBOutlet weak var myTextField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
}
}
When I set a breakpoint in the FirstViewController extension it never runs that code.
In WillDisplayCell add the tag to the UITextField. Also create a protocol to notify the Corrosponding viewController and set itself as the delegate here.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier")
cell.textField.tag = indexPath.row
cell.delegate = self
}
The protocol in your cell class will look something like this
protocol MyCellDelegate: class {
func MyCell(_ cell: UITableViewCell, didEnterText text: String)
}
class MyCell: UITableViewCell, UITextFieldDelegate {
weak var delegate: MyCellDelegate?
override fun awakeFromNib() {
super.awakeFromNib()
textField.delegate = self
}
//All the remaining code goes here
func textFieldShouldReturn(_ textField: UITextField) -> Bool { //delegate method
textField.resignFirstResponder()
delegate?.MyCell(self, didEnterText: textField.text! )
return true
}
}
Now again in your FirstViewController which has conformed to be its delegate do this
extension FirstViewController: MyCellDelegate {
func MyCell(_ cell: UITableViewCell, didEnterText text: String) {
if let indexPath = tableView.indexPathForCell(cell) {
// call whichever method you want to call based on index path
}
}

Swift - Calling a func of a UIViewController from a Custom UITableViewCell

Here is my problem:
I have a MainTableViewController with an outlet for a table that uses custom UITableViewCells. I also have an outlet for a UIView, called BlackView, in MainTableViewController.
What I want to do: Inside myCustomCell I would like to set "BlackView.hidden = false". I am trying to use "class func" in my MainTableViewController file, and call it from myCustomCell, but it is not working, because Xcode stops recognizing BlackView when I put the word "class" before "func".
So, I would like to call a function of a MainTableViewController or access its outlet from the .swift of my .xib file.
Does anybody knows how to do that?
Here is my .xib file:
My .xib file
Here is the .swift for my .xib file:
class myCustomCell: UITableViewCell {
#IBOutlet weak var commentTextView: UITextView!
override func awakeFromNib() {
commentTextView.delegate = self
super.awakeFromNib()
}
func textViewDidBeginEditing(textView: UITextView) {
MainTableViewController.hideBlackView(true)
}
func textViewDidEndEditing(textView: UITextView) {
var comment = commentTextView.text
}
}
Here is my MainTableViewController:
class MainTableViewController: UIViewController
#IBOutlet weak var MyTable: UITableView!
#IBOutlet weak var BlackView: UIView!
override func viewDidLoad() {
BlackView.hidden = true;
MyTable.registerNib(UINib(nibName: "myCustomCell", bundle: nil), forCellReuseIdentifier: "myCustomCellID")
}
class func hideBlackView(setToHidden: Bool) {
if setToHidden == true {
BlackView.hidden = true
} else {
BlackView.hidden = false
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCustomCellID") as! PublishHeaderTableViewCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
}
Here is my Main.storyboard:
My Main.storyboard
The Answer is Delegation
BlackView is an instance which will be created by the OS. The outlet is a special property (called an outlet) referencing that instance. When MainTableViewController is shown, an instance of it is created by the OS.
You probably want to use an instance method, not a class method to change the hidden property on instance of BlackView. To do that you will need to pass a reference of the MainTableViewController instance to myCustomCell. This is called delegation, which is how ios programming and most MVC models work.
To do this add define a delegate protocol (just above the definition for the custom cell would be normal) and add a weak var to the cell of this type:
// use a class protocol for delegates so weak properties can be used
protocol MyCustomCellDelegate: class {
func hideBlackView(setToHidden: Bool)
}
class MyCustomCell: UITableViewCell {
#IBOutlet weak var commentTextView: UITextView!
weak var delegate: MyCustomCellDelegate?
override func awakeFromNib() {
commentTextView.delegate = self
super.awakeFromNib()
}
func textViewDidBeginEditing(textView: UITextView) {
delegate?.hideBlackView(true)
}
func textViewDidEndEditing(textView: UITextView) {
var comment = commentTextView.text
}
}
Then when you are setting up the cells in cellForRowAtIndexPath, cast as the the proper cell type which should be MyCustomCell in the example you've given not PublishHeaderTableViewCell (also note that I've switched your custom cell class name to starting with a capital letter as is industry standard in ios development). Finally, set the delegate to the instance of the MainTableViewController (which is called "self" from within instance functions).
BTW, in your case, you are only using one cell, so you probably don't need to dequeue and reuse cells. You could just take all that out and return a simple instance you created of the cell in the cellForRowAtIndexPath method. Anyway, I will leave all that in place in case you have just simplified your code for Stack Overflow.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// you need to cast the cell to your custom class to use it
let cell = tableView.dequeueReusableCellWithIdentifier("myCustomCellID") as! MyCustomCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
// set the delegate
cell.delegate = self
return cell
}
Finally, and VERY IMPORTANTLY, you need to declare that MainTableViewController conforms to the the protocols that will use it so the functions (methods) that other objects want to delegate to it will succeed. In your case it needs to conform to both MyCustomCellDelegate which we wrote above, but also since you are using it for the for the tableView's data source (for cellForRowAtIndexPath and numberOfRowsInSection) you need to declare that it conforms to UITableViewDataSource (You may have done this already through Interface Builder (story board).. if not you can do it in the class definition).
// Declare objects conform to protocols by including protocol names separated by commas after the colon (or the class inherited from)
class MainTableViewController: UIViewController, MyCustomCellDelegate, UITableViewDataSource {
#IBOutlet weak var MyTable: UITableView!
#IBOutlet weak var BlackView: UIView!
override func viewDidLoad() {
BlackView.hidden = true
MyTable.registerNib(UINib(nibName: "myCustomCell", bundle: nil), forCellReuseIdentifier: "myCustomCellID")
}
func hideBlackView(setToHidden: Bool) {
// since they are both bools just set BlackView.hidden to the setToHidden parameter directly
BlackView.hidden = setToHidden
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCustomCellID") as! MyCustomCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
// set the delegate
cell.delegate = self
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
}
As a final note, I'm not sure is setting the delegate for the UITextView is a appropriate in the awakeFromNib method in your custom cell. I know that this method doesn't always fire.. In your case since it is on an outlet, I think it is OK, but I don't use XIB files very much my self, so you may want to print to the console to make sure it is being called each time or research the issue more.

UITableview with more than One Custom Cells with Swift

I want to use a UITableview with different custom tableViewCells. My 3 cells are as such:
Cell1: should have an image and a label.
Cell2: should have two labels.
Cell3: should have a dayPicker.
I don't want to code a tag for the cells. How can I manage this in Swift. Do I have to code my own class for every cell? Can I use one tableviewController? How can I populate data in different cells?
I would like to generate a tableView, like a contact app of an iOS device.
Let me start with answering your questions first.
Do I have to code an own class for every cell?=> Yes, I believe so. At least, I would do that way.
Can I use one tableviewController?=> Yes, you can. However, you can also have a table view inside your View Controller.
How can I populate data in different cells? => Depending on the conditions, you can populate data in different cells. For example, let's assume that you want your first two rows to be like the first type of cells. So, you just create/reuse first type of cells and set it's data. It will be more clear, when I show you the screen shots, I guess.
Let me give you an example with a TableView inside a ViewController. Once you understand the main concept, then you can try and modify anyway you want.
Step 1: Create 3 Custom TableViewCells. I named it, FirstCustomTableViewCell, SecondCustomTableViewCell, ThirdCustomTableViewCell. You should use more meaningful names.
Step 2: Go the Main.storyboard and drag and drop a TableView inside your View Controller. Now, select the table view and go to the identity inspector. Set the "Prototype Cells" to 3. Here, you just told your TableView that you may have 3 different kinds of cells.
Step 3:
Now, select the 1st cell in your TableView and in the identity inspector, put "FirstCustomTableViewCell" in the Custom class field and then set the identifier as "firstCustomCell" in the attribute inspector.
Do the same for all others- Set their Custom Classes as "SecondCustomTableViewCell" and "ThirdCustomTableViewCell" respectively. Also set the identifiers as secondCustomCell and thirdCustomCell consecutively.
Step 4: Edit the Custom Cell Classes and add outlets according to your need. I edited it based on your question.
P.S: You need to put the outlets under the class definition.
So, In the FirstCustomTableViewCell.swift, under the
class FirstCustomTableViewCell: UITableViewCell {
you would put your label and image view outlets.
#IBOutlet weak var myImageView: UIImageView!
#IBOutlet weak var myLabel: UILabel!
and in the SecondCustomTableViewCell.swift, add the two labels like-
import UIKit
class SecondCustomTableViewCell: UITableViewCell {
#IBOutlet weak var myLabel_1: UILabel!
#IBOutlet weak var myLabel_2: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
and the ThirdCustomTableViewCell.swift should look like-
import UIKit
class ThirdCustomTableViewCell: UITableViewCell {
#IBOutlet weak var dayPicker: UIDatePicker!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Step 5: In your ViewController, create an Outlet for your TableView and set the connection from storyboard. Also, you need to add the UITableViewDelegate and UITableViewDataSource in the class definition as the protocol list.
So, your class definition should look like-
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
After that attach the UITableViewDelegate and UITableViewDatasource of your table view to your controller. At This point your viewController.swift should look like-
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
P.S: If you were to use a TableViewController rather than a TableView inside a ViewController, you could have skipped this step.
Step 6: Drag and drop the image views and labels in your cell according to the Cell class. and then provide connection to their outlets from storyboard.
Step 7: Now, write the UITableViewDatasource's required methods in the view controller.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "firstCustomCell")
//set the data here
return cell
}
else if indexPath.row == 1 {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "secondCustomCell")
//set the data here
return cell
}
else {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "thirdCustomCell")
//set the data here
return cell
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Swift 3.0 + update with minimum code
Basic concept:
Create a table view with dynamic cell prototypes. Assign identifier and create custom table view cell class for each cell prototype. Initiate and show custom cells in table view's delegate method.
1. Create cells on storyboard
Drag a tableView to your view controller, add prototype cells to it, and then drop UI element to your table view cells, add constraint properly if needed.
2. Create custom UITableViewCell classes
Add the following code to your project. I am putting it right above the view controller class.
class FirstTableCell: UITableViewCell {
}
class SecondTableCell: UITableViewCell {
}
class ThirdTableCell: UITableViewCell {
}
3. Assign custom class and identifier to cell prototypes
For each of the cell prototypes in storyboard, assign the custom class created from step 2, and then enter an unique identifier.
4. Connect UI elements to swift code
Control drag the table view and connect to the view controller class. Control drag the UI elements that get added to cell prototypes on step 1, and connect to the corresponding table view cell class.
5. Add code to view controller and control the table view
Make your view controller conform to table view delegate
class YourViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
In viewDidLoad, set up table view's delegate and data source.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
}
Finally, add two delegate methods to control your table view, as per minimum requirement.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "firstTableCell") as! FirstTableCell
// Set up cell.label
return cell
} else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "secondTableCell") as! SecondTableCell
// Set up cell.button
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "thirdTableCell") as! ThirdTableCell
// Set up cell.textField
return cell
}
}
6. Give it a try :)
I recommend to use this simple and easy to use library, I made for Table and Collection views. You can add as many types of cells as you want and achieve more clean ViewControllers without boilerplate code.
https://github.com/deniskakacka/DKDataSources
For UI on first picture, all your code in ViewController is this:
lazy var dataSource = DKTableDataSource<CellType>(
models: [
DisclosureCellModel(title: "Disclosure 1", action: .action1),
TextFieldCellModel(title: "TextField 1", placeholder: "Placeholder 1"),
SwitchCellModel(title: "Switch 1", isOn: true),
BannerCellModel(imageName: "placeholder"),
SwitchCellModel(title: "Switch 2", isOn: false),
BannerCellModel(imageName: "placeholder"),
DisclosureCellModel(title: "Disclosure 2", action: .action2),
TextFieldCellModel(title: "TextField 2", placeholder: "Placeholder 2"),
BannerCellModel(imageName: "placeholder")
]
)
// in `viewDidLoad`
dataSource.registerCells(for: tableView)
tableView.dataSource = dataSource
Swift 5
Create 3 Custom TableViewCells. I named it,
FirstTableViewCell, SecondTableViewCell,
ThirdTableViewCell
Add All 3 Custom Cell Classes and add outlets according to your need.
I have added in below code.
class FirstTableViewCell: UITableViewCell {
#IBOutlet weak var myImageView: UIImageView!
#IBOutlet weak var myLabel: UILabel!
static let cellIdentifier = "FirstTableViewCell"
static let cellNib = UINib(nibName: "FirstTableViewCell", bundle: Bundle.main)
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
3: In your ViewController, create an Outlet for your TableView. Also, you need to add the UITableViewDelegate and UITableViewDataSource in the class definition.
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.delegate = self
tableView.dataSource = self
tableView.register(FirstTableViewCell.cellNib, forCellReuseIdentifier: FirstTableViewCell.cellIdentifier)
tableView.register(SecondTableViewCell.cellNib, forCellReuseIdentifier: SecondTableViewCell.cellIdentifier)
tableView.register(ThirdTableViewCell.cellNib, forCellReuseIdentifier: ThirdTableViewCell.cellIdentifier)
}
}
4.Now, write the UITableViewDatasource's required methods in the view controller.
extension ViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: FirstTableViewCell.cellIdentifier, for: indexPath) as? FirstTableViewCell else { return UITableViewCell() }
return cell
}else if indexPath.row == 1 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: SecondTableViewCell.cellIdentifier, for: indexPath) as? SecondTableViewCell else { return UITableViewCell() }
return cell
}else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ThirdTableViewCell.cellIdentifier, for: indexPath) as? ThirdTableViewCell else { return UITableViewCell() }
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50 //According requirement
}
}
Your code will look like below(View Controller Code)
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.delegate = self
tableView.dataSource = self
tableView.register(FirstTableViewCell.cellNib, forCellReuseIdentifier: FirstTableViewCell.cellIdentifier)
tableView.register(SecondTableViewCell.cellNib, forCellReuseIdentifier: SecondTableViewCell.cellIdentifier)
tableView.register(ThirdTableViewCell.cellNib, forCellReuseIdentifier: ThirdTableViewCell.cellIdentifier)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension ViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: FirstTableViewCell.cellIdentifier, for: indexPath) as? FirstTableViewCell else { return UITableViewCell() }
return cell
}else if indexPath.row == 1 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: SecondTableViewCell.cellIdentifier, for: indexPath) as? SecondTableViewCell else { return UITableViewCell() }
return cell
}else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ThirdTableViewCell.cellIdentifier, for: indexPath) as? ThirdTableViewCell else { return UITableViewCell() }
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50 //According requirement
}
}
The above answers are the best answers, but there are TONS of reasons to get this issue. Here is another potential solution for anyone with this problem:
My problem was that I was segueing to the ViewController class and not the storyboard view. So my reference to the storyboard cell was meaningless, since the storyboard wasn't being used.
I was doing this:
let viewControllerB = SubViewController()
viewControllerB.passedData = diseases[indexPath.row].name
navigationController?.pushViewController(viewControllerB, animated: true)
And I needed to do something like this:
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "SubViewStoryboardController") as! SubViewController
nextViewController.passedData = diseases[indexPath.row].name
self.present(nextViewController, animated:true, completion:nil)
Hope this helps someone.
If you're using custom XIBs as TableView Cells then follow the below code
//Write in viewDidLoad()
let nib = UINib(nibName: "PrinterTVC", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "CELL1")
let nib1 = UINib(nibName: "SelectAndEditTVC", bundle: nil)
tableView.register(nib1, forCellReuseIdentifier: "CELL2")
UITableViewController is inheriting UIViewController that already has UITableviewDataSource & UITableviewDelegate mapped on itself.
You might subclass UITableViewController or use a TableView inside your ViewController.
After that you must implement required methods(cellForRowAtIndexPath and numberOfRowsInSection) which are declared in the UITableviewDataSource.
Also in storyboard, you need to create cell prototypes with unique Id.
There are basic types of cell, with (title, subtitle for instance) - you can use them too if you don't need special configuration.
So, for picker, yes, you need to create your own custom cell. Create necessary custom UITableViewCell class holding date picker and make sure to use delegate to send back the desired result back to your ViewController.