How to implementing two collection view inside of view controller? - iphone

Hi I am beginner in Swift programming language and I have problem to implementing two collection view inside of view controller, this is the link of my picture that I want to achieve:
The picture I want to achieve
is it possible? so far I have the codes that doesn't seems work:
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDataSource, UICollectionViewDelegate{
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewTwo: UICollectionView!
var optionOne = ["Gulsah", "Hurrem", "Mihrimah", "Nilufer"]
override func viewDidLoad() {
super.viewDidLoad()
self.resetFilterThumbnails()
self.collectionView.delegate = self
}
//For the collectionView, number of filters in the section
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (filters == true){
//do all the stuff here for FILTER_CELL
return self.filters.count}
else{
return self.optionOne.count}
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("FILTER_CELL", forIndexPath: indexPath) as FilterThumbnailCell
let cellTwo = collectionView.dequeueReusableCellWithReuseIdentifier("FILTER_CELL_TWO", forIndexPath: indexPath) as FilterThumbnailCell
var filterThumbnail = self.filterThumbnails[indexPath.row]
println("filter cell two")
if (indexPath.item == 0){
//do all the stuff here for FILTER_CELL
if filterThumbnail.filteredThumbnail != nil {
cell.imageView.image = filterThumbnail.filteredThumbnail
} else {
cell.imageView.image = filterThumbnail.originalThumbnail
//filterThumbnail is a class instance
filterThumbnail.generateThumbnail({ (image) -> Void in
if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? FilterThumbnailCell {
cell.imageView.image = image
}
})
}
return cell
}else{
//FILTER_CELL_TWO
var button = cellTwo.viewWithTag(1) as UILabel
button.text = optionOne[indexPath.row]
cellTwo.backgroundColor = UIColor.redColor()
return cellTwo
}
}
}

This is very Possible. You could do this in several ways.
1:
You could implement two contained view controllers. This might be the cleanest since you could handle interaction in separate controllers. In this case you may need to also implement some delegate/protocols to pass information back to the parentViewController.
2:
You could create a separate datasource/delegate class to handle the associated functions for each view controller and assign them in viewDidLoad. This case may also need to have delegates/protocols implemented to pass information from the collectionView dataSource/Delegate class.
Here is an example, taken from the Master-Detail starting class Create your class like this (add conform to the UITableViewDelegate if needed):
import UIKit
class MyDataSource: NSObject, UITableViewDataSource {
var objects = [AnyObject]()
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let object = objects[indexPath.row] as NSDate
cell.textLabel!.text = object.description
return cell
}
}
Then in your UIViewController you create a variable of type MyDataSource:
var dataSource1 = MyDataSource()
var dataSource2 = MyDataSource()
Inside of viewDidLoad you can assign the dataSource its objects and assign the datasource to the tableviews:
dataSource1.objects = objects1;
dataSource2.objects = objects2;
tableView1.dataSource = dataSource1
tableView2.dataSource = dataSource2
Now each of the tableviews will use a different instance of this DataSource, with its own set of model objects. If you need to further customize you datasource (as it looks like you do) Simply create another data source class and assign it to the appropriate object. If you conform to the delegate of the tableview inside these data source objects, you should implement a standard protocol for the data source, and conform to that protocol inside of your view controller:
Above the datasource object:
protocol DataSourceDelegate{
func didSelectCellAtIndexPath(indexPath:NSIndexPath)
}
Inside of the data source object:
var dataSourceDelegate:DataSourceDelegate?
Inside of the view controller:
dataSource.dataSourceDelegate = self
Make sure to conform to the delegate in the class definition. Then instantiate the method in the view controller:
func didSelectCellAtIndexPath(indexPath:NSIndexPath){
// Your code here
}
Make sure to call the delegate inside the datasource
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
dataSourceDelegate?.didSelectCellAtIndexPath(indexPath)
}
To register cells, if you are using storyboard, just make sure that the cell identifiers are the correct ones, and you will not need to register anything else. If not, you could register the cell inside the dataSource (probably a good idea, since it would keep all the info contained)
3:
You could use one viewController and differentiate between the cases by comparing the collectionView passed into the dataSource/Delegate functions to an outlet connected to the corresponding collectionViews in the view. While this case will not need any delegates implemented, it will fill the viewController up with a lot of if/else statements, perhaps making the code more difficult to maintain/read.

Related

Send data from CustomTableView Class to its parent View Controller

I have a View Controller "VCInicio", that has a TableView in his View, the Cells for that TableView are in a .xib format, the cells has a custom class called "CustomiseTableViewCell", inside there I have logic that gets and print a String (Phone Number) every time I click on a RadioButton, it prints its Phone Number, I'm able to print the value (From CustomiseTableViewCell Class) and see the value on console, but I need to send that value back to "VCInicio" so I can manipulate it from that Controller. I've seen lots of examples that suggest to use Protocols but I haven't been able to make them work.
EDIT: Because of the structure I'm using, I can't work with didSelectRowAt, hence I'm working with the Selection of the Radio Button instead of the selection of the cell.
What Fixed the Issue:
"CustomiseTableViewCellDelegate" TableView Custom Class (Child Class)
//Protocol Implementation
protocol CustomiseTableViewCellDelegate {
func onPhoneNumberClicked(_ cell: CustomiseTableViewCell, phoneNumber: String?)
}
class CustomiseTableViewCell: UITableViewCell {
var delegate: CustomiseTableViewCellDelegate?
var phone: String?
override func awakeFromNib() {
super.awakeFromNib()
...
}
//Here I get and send the phoneNumber
#objc func radioButtonTapped(_ radioButton: UIButton) {
...
phone = itemLabel.text!
self.delegate?.onPhoneNumberClicked(self, phoneNumber: phone!)
...
}
}
"VCInicio" View Controller
class VCInicio: UIViewController, UITableViewDelegate, UITableViewDataSource, CustomiseTableViewCellDelegate {
func onPhoneNumberClicked(_ cell: CustomiseTableViewCell, phoneNumber: String?) {
//Here I print the phoneNumber
print("From VCInicio", phoneNumber)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "phoneCell", for: indexPath) as! CustomiseTableViewCell
cell.delegate = self
...
//cell data config
...
return cell
}
}
If you don't want to use custom protocols:
Assuming you have the numbers stored in some model in variable phoneNumbers: [String]
Make your VC table view delegate:
self.tableView.delegate = self
Extend ViewController to conform UITableViewDelegate protocol
class VCInicio: UIViewController, UITableViewDelegate {
//...
}
Implement func tableView(UITableView, didSelectRowAt: IndexPath) and use selected cell phone value to manipulate it later
class VCInicio: UIViewController, UITableViewDelegate {
//...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedPhoneNumber = self.model.phoneNumbers[indexPath.row]
// manipulate selectedPhoneNumber
}
}
With protocols:
Define cell delegate and use it in the method for click
protocol CustomiseTableViewCellDelegate: class {
func onPhoneNumberClicked(_ cell: CustomiseTableViewCell, phoneNumber: String?)
}
class CustomiseTableViewCell: UITableViewCell {
weak var delegate: CustomiseTableViewCellDelegate?
// ... rest of your cell class
func onPhoneClicked() {
// this is the function where you have code to print the number to console
print(self.phoneNumberLabel.text)
self.delegate?.onPhoneNumberClicked(self, self.phoneNumberLabel.text)
}
}
In your UITableViewDataSource method for creating cells make VCInicio as cell's delegate
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// assuming you have your cell here
cell.delegate = self
return cell
}
Make your VC conform to CustomiseTableViewCellDelegate protocol
class VCInicio: UIViewController, CustomiseTableViewCellDelegate {
//...
override func onPhoneNumberClicked(_ cell: CustomiseTableViewCell, phoneNumber: String?) {
// manipulate phoneNumber
}
}
This code below is right now for an ImageView with a gesture recognizer. Which is targeted to the handleAvatarTap. Which is posting a global notification. Any ViewController or View can listen to notifications, and once the chosen one is posted the controller can act on this.
In the example below is an example, passing the ImageView as an object for the notification.
// this is how I am creating a new notification name type
extension Notification.Name {
static let didTapAvatar = Notification.Name("didTapAvatar")
}
// this would go within the cell where you are printing the number
#objc func handleAvatarTap() {
NotificationCenter.default.post(name: .didTapAvatar, object: self)
}
// This would go in to the view controller viewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(handleAvatarTap), name: .didTapAvatar, object: nil)
// This is the objc func within the view controller
#objc fileprivate func handleAvatarTap(notification: NSNotification) {
// checking the type of the posted notification object
guard let avatarView = notification.object as? AvatarView else { return }
}

Seguing from uicollectionview that is inside of a tableview

I've put a uicollectionview inside of a uitableview. I'm having trouble seguing to another viewcontroller after selecting a collectionview cell that is inside of the table view cell.
// if the user selects a cell, navigate to the viewcontroller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// we check did cell exists or did we pressed a cell
if let cell = sender as? UICollectionViewCell {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "cell") as! TestingTableView
// define index to later on pass exact guest user related info
let index = cell2.collectionView?.indexPath(for: cell)!.row
print(index as Any)
// if segue is guest...
if segue.identifier == "guest" {
// call guestvc to access guest var
let guestvc = segue.destination as! GuestCommunityViewVC
// assign guest user inf to guest var
guestvc.guest = communities[index!] as! NSDictionary
}
}
}
}
I'm getting an error at the line:
let index = cell2.collectionView?.indexPath(for: cell)!.row
because it is saying the value is nil. Does anyone know a better method to do this?
Here is an example of how to use a delegate:
1) Create a protocol outside of a class declaration:
protocol customProtocolName:class {
func pushToNewView(withData:[DataType])
}
note: use class in order to prevent a reference cycle
2) Create a delegate inside of the UITableViewCell that holds the reference to the UICollectionView:
class customUITableViewCell {
weak var delegate:customProtocolName? = nil
}
3) Inside the UIViewController that holds the reference to the UITableView, make sure you add the protocol besides the class declaration and add the function we created to ensure that the protocol specifications are satisfied:
class customViewController: customProtocolName {
func pushToNewView(withData:[DataType]) {
//inside here is where you will write the code to trigger the segue to the desired new UIViewController
//You can take this new data and store it in this ViewController and then during the segue pass it along
}
}
4) In the UITableViewDelegate function, "cellForRowAt", set the delegate inside the customUITableViewCell to self:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! customUITableViewCell
cell.delegate = self
return cell
}
5) Inside the customUITableViewCell, where the UICollectionView delegate function handles "didSelectItemAt" delegate function, you trigger the protocol function there like so:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.pushToNewView(withData:[DataType])
}
This is a very simplified example, if you wanted to pass an IndexPath, then you can modify the function to do so. you can also pass back anything you want as well, it isn't limited.

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, does not conform to protocol - how to declare several?

I understand that I need to implement required methods in the controller and establish a relationship between view and controller.. but where do the identifier "tableView" in
func tableView(mintabell: UITableView, numberOfRowsInSection section: Int) -> Int
{
return items.count
}
come from and what if I want several tableviews on same view? How to declare them separately?
These are the protocols that you need to declare for a TableView
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return createCellAndReturnItHere
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfCellsInSection
}
In terms of having more than one tableview in one ViewController.. If you have an outlet for each tableview then you can check which one you need inside the protocol functions:
#IBOutlet weak var tableViewOne: UITableView!
#IBOutlet weak var tableViewTwo: UITableView!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView == self.tableViewOne {
return createCellForTableViewOneAndReturnItHere
} else {
return createCellForTableViewOneAndReturnItHere
}
}
Think of it like a box. Every tableView that is subscribed to the protocol grabs in that box leaves its fingerprint and takes what it gets. So if you have multiple tableViews in one controller you can distinguish them by checking for equality.
Example:
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let firstTableView = UITableView()
let secondTableView = UITableView()
viewDidLoad() {
firstTableView.delegate = self
secondTableView.delegate = self
firstTableView.dataSource = self
secondTableView.dataSource = self
}
// ... some othe methods...
func tableView(mintabell: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView == firstTableView {
return 10
}
if tableView == secondTableView {
return 20
}
return 0
}
}
Delegates are implemented by the developer and called by iOS not the developer. The tableView is passed by iOS and points to the particular tableview.
If you have several tableviews with the same delegate you can compare the passed tableview parameter to the tableviews you have implemented to determine which one.
Alternatively, create a tableView delegate and datasource per tableView. This will eliminate testing which tableView removing a lot of conditional logic from the code.
First of all, you don't necessarily always have to implement a protocol for every view. Only when you have a view which has delegate that needs implementing, you will have to conform that protocol responsible for that delegate implementation.
So for tableView, first you drag a UITableView in your Controller from the Object Library and then a UITableViewCell under the Table View.
Now go to the ViewController.swift file and add
#IBOutlet var myFirstTableView: UITableView!
right after the line.
class ViewController: UIViewController
P.S: If you have more than one table then you could just declare the extra table's here., like-
#IBOutlet var mySecondTableView: UITableView!
Now, let's assume, you have one table. Now, you need to add the list of protocols. So, just add UITableViewDelegate, UITableViewDataSource with
class ViewController: UIViewController
appended by comma.
Adding this should give you an error but that's okay. This is because you have not added the required method listed under that UITableViewDatasource protocol.
So, just add those required methods and implement it accordingly.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("myCell") as! UITableViewCell
cell.textLabel?.text = "test"
return cell
}
Here, I said, I will have 3 cells whose identifier is "myCell" and the cell's textLabel will have a text, "test".
Now, we forgot a very important step and that is to assign the cell identifier to our TableView Cell. So, go to the storyboard and select the TableView Cell and insert "myCell" as an identifier for your cell.
If you had more than one table, then you would check for which TableView, you are loading your data. So, you could assign a unique tag to each table explicitly(you can do that either from storyboard or from code), and based on that tag, you would implement your methods. Let's say you have 3 tables and the assigned tag is 1,2 and 3. So, you could do something like,
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1{
return 3
}
else if tableView.tag == 2{
return 4
}
else{
return 1
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
if tableView.tag == 1{
cell.textLabel?.text = "test1"
}
else if tableView.tag == 2{
cell.textLabel?.text = "test2"
}
else{
cell.textLabel?.text = "test"
}
return cell
}

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.