I'm stuck with the following, any inputs would be highly appreciated.
My app has a UITableViewController with custom cells. Let's name its UITableView as TABLE VIEW-1. I am using XIB as its custom cell. Inside that xib, there is another UITableView, (TABLE VIEW-2), with another XIB as its custom cell.
My question is, How can I reload cells of (TABLE VIEW-2) from (TABLE VIEW-1) once I get data from an API in (TABLE VIEW-1). I want to use delegate and protocols to do this.
OR, What would be the correct way of performing this and how would I go about it?
Create a reference of TableView1 in the TableView2 ViewController. You can do this by
-- Open the Assistant Editor
-- Right-click and drag from your UI element (i.e. label) to the code file
-- Xcode auto inserts code for you to create the name and connection
Then call tableView2.reloadData(), where tableView2 is the name of the outlet you just created.
Create a reference of the TABLE VIEW-2 in the TABLE VIEW-1 custom cell (TABLE VIEW-1 CELL)
Create a method inside TABLE VIEW-1 CELL to reload its data alone with the TABLE VIEW-2 (Optional but useful to organize your code)
Inside the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell of the TABLE VIEW-1 call reload function
class ViewController: UIViewController {
#IBOutlet weak var mainTable: UITableView!
let sampleData = [
(section: "News", SubSections: [
"Breaking news",
"Current Affairs",
"Local"
]) ]
override func viewDidLoad() {
super.viewDidLoad()
self.mainTable.estimatedRowHeight = 200
self.mainTable.rowHeight = UITableView.automaticDimension
let nib: UINib = UINib(nibName: String(describing: MainCell.self), bundle: Bundle.main)
self.mainTable.register(nib, forCellReuseIdentifier: String(describing: MainCell.self))
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.sampleData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: MainCell = tableView.dequeueReusableCell(withIdentifier: String(describing: MainCell.self), for: indexPath) as! MainCell
let sectionData = self.sampleData[indexPath.row]
cell.setUpData(title: sectionData.section, data: sectionData.SubSections)
return cell
}
}
class MainCell: UITableViewCell {
#IBOutlet weak var sectionTitle: UILabel!
#IBOutlet weak var internalTable: UITableView!
var tableData:[String]?
override func awakeFromNib() {
super.awakeFromNib()
self.internalTable.estimatedRowHeight = 200
self.internalTable.rowHeight = UITableView.automaticDimension
let nib: UINib = UINib(nibName: String(describing: InsideCell.self), bundle: Bundle.main)
self.internalTable.register(nib, forCellReuseIdentifier: String(describing: InsideCell.self))
}
func setUpData(title: String, data:[String]) {
self.sectionTitle.text = title
self.tableData = data
self.internalTable.reloadData()
}
}
extension MainCell: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.tableData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: InsideCell = tableView.dequeueReusableCell(withIdentifier: String(describing: InsideCell.self), for: indexPath) as! InsideCell
if let subSectionData = self.tableData?[indexPath.row] {
cell.subSectionTitle.text = subSectionData
}
return cell
}
}
class InsideCell: UITableViewCell {
#IBOutlet weak var subSectionTitle: UILabel!
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
}
}
Related
In this image I have two section in this table view . In the second section there is a tableview inside the cell. i want to fetch the textfield data of the “inner table view” in the current class. How to do this? followings are the code for my tableview cell .I want to fetch "InerTableViewCell1" data in "outerTableView". How to do this?
//MARK- UITableViewDataSource
extension outerTableView: UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0{
let cell1 = tableView.dequeueReusableCell(withIdentifier: "TableViewCell1", for: indexPath) as? TableViewCell1
return cell1!
}
else{
let cell2 = tableView.dequeueReusableCell(withIdentifier: "TableViewCell2", for: indexPath) as? TableViewCell2
cell2!.innerTableView.reloadData()
return cell2!
}
}
class TableViewCell2: UITableViewCell {
//MARK- IBOutlet
#IBOutlet weak var innerTableView: UITableView!
//MARK- Properties
var rowNumber = 1
//MARK- Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
innerTableView.dataSource = self
innerTableView.delegate = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
//MARK- UITableViewDataSource
extension TableViewCell2: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("row number is \(rowNumber)")
return rowNumber
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "InerTableViewCell1", for: indexPath) as? InerTableViewCell1
return cell!
}
}
}
class InerTableViewCell1: UITableViewCell {
//MARK- IBOutlet
#IBOutlet weak var firstUserNameTextField: UITextField!
#IBOutlet weak var firstAdressTextField: UITextField!
#IBOutlet weak var secondUserNameTextField: UITextField!
#IBOutlet weak var secondAdressTextField: UITextField!
//MARK- Lifecycle
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
}
}
I have 2 View Controllers: In the first ViewController that has a TableView, I have a button in each TableViewCell to select a shipping option. When clicking the button, another TableViewController will pop up with a list of shipping options. Upon choosing a shipping option, I need to pass this data back to TableViewCell in the first ViewController. I wrote below code but the shipping option selected in the second TableViewController still didn't pass to the first controller. Other things work fine. Could anyone help to let me know how to improve this code? Thanks a million!
//First ViewController:
class PaymentMethodViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//MARK: - IBOutlets
#IBOutlet weak var PurchasedReviewItemsTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PurchasedReviewItemsTableViewCell") as! PurchasedReviewItemsTableViewCell
cell.delegate = self
}
}
extension PaymentMethodViewController: PurchasedReviewItemsTableViewCellDelegate {
func chooseShippingOptionButtonPressed() {
let chooseShippingOptionVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(identifier: "ShippingOptionsSelectionPopUpViewController") as! ShippingOptionsSelectionPopUpViewController
chooseShippingOptionVC.modalPresentationStyle = .overCurrentContext
self.present(chooseShippingOptionVC, animated: true, completion: nil)
}
//MARK: Pass data from popUpView
func popUpShippingOptionsSelected(shippingOption: String) {
let cell = PurchasedReviewItemsTableView.dequeueReusableCell(withIdentifier: "PurchasedReviewItemsTableViewCell") as! PurchasedReviewItemsTableViewCell
cell.shippingOptionsLabel.text = shippingOption
}
}
//TableViewCell of the first ViewController:
protocol PurchasedReviewItemsTableViewCellDelegate {
func chooseShippingOptionButtonPressed()
func popUpShippingOptionsSelected(shippingOption: String)
}
class PurchasedReviewItemsTableViewCell: UITableViewCell {
#IBOutlet weak var shippingOptionsLabel: UILabel!
var delegate: PurchasedReviewItemsTableViewCellDelegate?
#IBAction func changeShippingOptionButtonPressed(_ sender: Any) {
delegate?.chooseShippingOptionButtonPressed()
}
}
//Second TableViewController:
class ShippingOptionsSelectionPopUpViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var selectedShippingOption : String?
var shippingOption = ["X", "Y"]
var delegate: ShippingOptionsSelectionPopUpDelegate?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShippingOptionsSelectionPopUpTableViewCell", for: indexPath) as! ShippingOptionsSelectionPopUpTableViewCell
cell.selectShippingOption(shippingOption: shippingOption[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedShippingOption = shippingOption[indexPath.row]
delegate?.popUpShippingOptionsSelected(shippingOption: selectedShippingOption!)
dismiss(animated: true, completion: nil)
}
}
Define separate protocol for popUpShippingOptionsSelected
protocol ShippingOptionsDelegate {
func popUpShippingOptionsSelected(shippingOption: String)
}
class ShippingOptionsSelectionPopUpViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var selectedShippingOption : String?
var shippingOption = ["X", "Y"]
var shippingOptiondelegate: ShippingOptionsSelectionPopUpDelegate?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShippingOptionsSelectionPopUpTableViewCell", for: indexPath) as! ShippingOptionsSelectionPopUpTableViewCell
cell.selectShippingOption(shippingOption: shippingOption[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedShippingOption = shippingOption[indexPath.row]
shippingOptiondelegate?.popUpShippingOptionsSelected(shippingOption: selectedShippingOption!)
dismiss(animated: true, completion: nil)
}
}
Add tag to cell
class PaymentMethodViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//MARK: - IBOutlets
#IBOutlet weak var PurchasedReviewItemsTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PurchasedReviewItemsTableViewCell") as! PurchasedReviewItemsTableViewCell
cell.delegate = self
cell.tag = 100 // Set tag
}
}
Update extension with ShippingOptionsDelegate
extension PaymentMethodViewController: PurchasedReviewItemsTableViewCellDelegate, ShippingOptionsDelegate {
func chooseShippingOptionButtonPressed() {
let chooseShippingOptionVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(identifier: "ShippingOptionsSelectionPopUpViewController") as! ShippingOptionsSelectionPopUpViewController
chooseShippingOptionVC.shippingOptiondelegate = self
chooseShippingOptionVC.modalPresentationStyle = .overCurrentContext
self.present(chooseShippingOptionVC, animated: true, completion: nil)
}
//MARK: Pass data from popUpView
func popUpShippingOptionsSelected(shippingOption: String) {
let cell = PurchasedReviewItemsTableView.viewWithTag(100) as! PurchasedReviewItemsTableViewCell // use tag to get cell
cell.shippingOptionsLabel.text = shippingOption
}
}
I have a UITableViewCell that contains a bunch of UISwitches. I want the switch to toggle on/off based on what row the user selects, the data is passing to my cell but the switch state is not updating.
I am using a basic MVC, the Storyboard has a TableView > TableViewCell > Label | UISwitch
Controller:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var testList = [1,2,3,4,5]
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table.tableFooterView = UIView()
table.delegate = self
table.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
//Turn them all on at start
cell.setSwitch(rowSelected: true)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
cell.setSwitch(rowSelected: false)
}
}
SwitchCell
class SwitchCell: UITableViewCell {
#IBOutlet weak var uiswitch: UISwitch!
#IBOutlet weak var label: UILabel!
func setCell(number: Int){
label.text = String(number)
}
func setSwitch(rowSelected:Bool) {
uiswitch.setOn(rowSelected, animated: true)
}
}
I know I can just make the UISwitch intractable, but I am looking at changing it's state when the user selects the row.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
cell.setSwitch(rowSelected: false)
}
is incorrect. Using this code you don't pick the cell you want.
You should do simething like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.cellForRow(at: indexPath) as! SwitchCell
cell.setSwitch(rowSelected: false)
}
First of all, we don't dequeue the cell in didSelectRowAt method. Never do that. This will dequeue a brand new cell and won't reflect any changes that you do in it.
So remove the code for tableView(_: didSelectRowAt:) method.
Secondly, you can simply handle the UISwitch state based on cell selection in SwitchCell's definition using setSelected(_:animate:) method like so,
class SwitchCell: UITableViewCell {
#IBOutlet weak var uiswitch: UISwitch!
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
uiswitch.setOn(selected, animated: true)
}
//rest of the code...
}
To avoid bulky code, do all the cell layout in custom cell itself instead of doing it in the delegate or dataSource methods. That will only make you ViewController heavier and non-modular.
I m creating a UITableViewCell which can display n numbers of UITextfields.Please suggest me how to declare these textfields . And also mention how to implement the cellForRowAt function for it.
Heres my UITableViewCell file
import UIKit
class CustomTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var textlab: UITextField!
func configure(text: String?, placeholder: String) {
textlab.text = text
textlab.placeholder = placeholder
textlab.accessibilityValue = text
textlab.accessibilityLabel = placeholder
}
#IBOutlet weak var underlineLabel: UILabel!
#IBOutlet weak var AlertMessage: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
AND THE VIEW CONTROLLER FOR IT IS AS FOLLOWS:
import UIKit
class CustomViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var blanklabel = ["","","",""]
var textfields = ["First Name","Last Name","Email","Contact Number"]
var alertmsg = ["Please enter your First Name","Please enter your Last Name","Please enter your Email","Please enter your Contact number",]
var textfield = UITextField()
var AlertMessage = UILabel()
#IBAction func nextLabel(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomTableViewCell
return cell
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
return nil
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
It's not good idea to do it in UITableView with dynamic cells. Try to use custom view (change your cell class to subclass of UIView) in UIStackView or UITableView with static cells. And set uitextfields as properties in your viewcontroller.
I am trying to build a times tables app with a slider and a UITableView, but it gives me the SIGABRT error. I have tried relinking every outlet and action as well as redoing it from scratch, but it still won't seem to work.
Since I cannot paste it in here correctly formatted, I put it on pastebin.com
Debug code: http://pastebin.com/cBaALXWq
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var sliderValue: UISlider!
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50
}
#IBAction func sliderMoved(sender: AnyObject) {
var timesTableIndex: Int = Int(sender.value)!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel?.text = String(timesTableIndex*indexPath.row+1)
return cell
}
}
}
AppDelegate.swift; gives error in line 4
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
EDIT
#IBOutlet weak var sliderValue: UISlider!
var timesTableIndex: Int?
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50
}
#IBAction func sliderMoved(sender: AnyObject) {
timesTableIndex = Int(sliderValue.value * 50)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel.text = String(timesTableIndex*(1+indexPath.row))
return cell
}
I looks like you've added cellForRowAtIndex: inside #IBAction method. What you need to do is to take it out from the method. In addition you need set the timesTableIndex variable as global so that it can be accessed in cellForRowAtIndexPath: and call the reloadData method on tableView when you call sliderMoved: method.
var timesTableIndex: Int = 0 // Now global variable
#IBAction func sliderMoved(sender: AnyObject) {
timesTableIndex = Int(sender.value)!
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = "\(timeTableIndex * indexPath.row + 1)"
return cell
}
Also it seems that you are not using dequeueReusableCellWithIdentifier method on your table view, so I've updated the code to use it. Not that you need to set the Cell reuse identifier in your storyboard.
This should be your tableview code if you have created tableview in the storyboard
#IBOutlet weak var sliderValue: UISlider!
#IBOutlet weak var tableview: UITableView!
var timesTableIndex: Int = 0
#IBAction func sliderMoved(sender: AnyObject) {
timesTableIndex = Int(sliderValue.value * 50)
tableview.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return timesTableIndex
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel!.text = String(1*(1+indexPath.row))
return cell
}
If you have created your cell in the storyboard set the CellReuseIdentifier as 'cell' in the storyboard else you can register the class in the like
override func viewDidLoad() {
super.viewDidLoad()
//tableView is IBOutlet instance of UITableview
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "test")
}
Also please check you have set the tableview delegate/datasource in the storyboard tableview object
I've managed to solve the problem. I accidentally put the function which sets up the prototype cell outside the ViewController class, that explains everything. Thank you for helping me!