I'm following this tutorial https://kylebashour.com/posts/context-menu-guide and am trying to repurpose this snippet which presents a context menu:
class TableViewController: UITableViewController {
let data: [MyModel] = []
override func viewDidLoad() {
super.viewDidLoad()
// Configure the table view
}
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
let item = data[indexPath.row]
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
// Create an action for sharing
let share = UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { action in
print("Sharing \(item)")
}
// Create other actions...
return UIMenu(title: "", children: [share, rename, delete])
}
}
}
5 seconds after the context menu is presented I would like to update the title of the menu
UIMenu(title: "expired", children: [share, rename, delete])
and ensure its children have the .disabled attributed set.
UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up", , attributes: .disabled)
Is there a way I can I update the already presented context menu's title, and the attributes of its children?
If you want dynamics table you should work with data models and update them every time you want to change the data showed on the table view.
What I mean?
A good approach is to have for every row in the table view a model with the data that you want to show in that row (CellModel).
So, in your table view controller you are going to have a list of CellModels:
private var cellModels = [ CellModel ]()
Create cell models on your view did load function and implemente UITableViewDatasource functions:
// MARK: - UITableViewDatasource implementation.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.cellModels.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellModel = self.cellModels[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: kTableViewCellIdentifier, for: indexPath)
cell.textLabel?.text = cellModel.cellData.menuDataTitle
return cell
}
Now It's time to work with the CellModel class: this class represents the data that you are going to show in one row.
In every row of this simple tutorial you are showing a string with a title, but imagine that you want to show a title, subtitle and an image, in this case It's convenient to create a class in order to represents the data that you are going to show on the row.
I named It as "CellData":
class CellData {
// MARK: - Vars.
private(set) var menuDataTitle: String
// MARK: - Initialization.
init(title: String) {
self.menuDataTitle = title
}
}
On the other hand for each row you have a menu to show, so It's convenient to have a model that represents that menu that you are going to show. I named It as "CellContextMenuData":
class CellContextMenuData {
// MARK: - Vars.
private(set) var contextMenuTitle: String
private(set) var contextMenuActions = [ UIAction ]()
// MARK: - Initialization.
init(title: String) {
self.contextMenuTitle = title
}
}
This model have a title and actions to show on the menus:
// MARK: - UITableViewDelegate.
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
let cellModel = self.cellModels[indexPath.row]
let cellContextMenuData = cellModel.cellContextMenuData
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
return UIMenu(title: cellContextMenuData.contextMenuTitle, children: cellContextMenuData.contextMenuActions)
}
}
So, every time that you want to change the menu title (or Actions):
1 - Retrieve the cell model that represents the row that you want to change.
2 - Change the title of the CellContextMenuData.
3 - Reload the tableview.
Example:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Timer to change our data.
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { [weak self] timer in
let cellModel = self?.cellModels[3]
let cellContextMenuData = cellModel?.cellContextMenuData
cellContextMenuData?.contextMenuUpdate(title: "Expired")
cellContextMenuData?.contextMenuSetActionsShareUpdateDelete()
self?.tableView.reloadData()
}
}
I created an example, feel free to use/see it: https://bitbucket.org/gastonmontes/contextmenuexample
Related
I'm facing performance problems using a SwiftUI List with lots of data. I've created a demo app just to showcase the problem with 500_000 Strings and to show a trailing action for one of them, the CPU hits 100% for a few seconds, which is totally unusable. I also went ahead and wrapped UITableView to use it on SwiftUI using the same dataset (the same half a million Strings) and the trailing action is displayed instantly.
Is there any way to speed things up on the SwiftUI List or is this just a limitation of the framework?
I've made it easy to test both implementations by just changing the var named listKind, here's the sample code:
import SwiftUI
#main
struct LargeListPerformanceProblemApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
ContentView().navigationBarTitleDisplayMode(.inline)
}
}
}
}
enum ListKind {
case slow
case fast
}
struct ContentView: View {
var listKind = ListKind.slow
var items: [String]
init() {
self.items = (0...500_000).map { "Item \($0)" }
}
var body: some View {
switch listKind {
case .slow:
List {
ForEach(items, id: \.self) { item in
Text(item).swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button("Print") {
let _ = print("Tapped")
}
}
}
}.navigationTitle("Slow (SwiftUI List)")
case .fast:
FastList(items: self.items)
.navigationTitle("Fast (UITableView Wrapper)")
}
}
}
// UITableView wrapper
struct FastList: UIViewRepresentable {
let items: [String]
init(items: [String]) {
self.items = items
}
func makeUIView(context: Context) -> UITableView {
let tableView = UITableView(frame: .zero, style: .insetGrouped)
tableView.dataSource = context.coordinator
tableView.delegate = context.coordinator
return tableView
}
func updateUIView(_ uiView: UITableView, context: Context) {
uiView.reloadData()
}
func makeCoordinator() -> Coordinator {
Coordinator(items: items)
}
class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {
var items: [String]
init(items: [String]) {
self.items = items
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let printAction = UIContextualAction(style: .normal, title: "Print") { _, _, block in
print("Tapped")
block(true)
}
return UISwipeActionsConfiguration(actions: [printAction])
}
}
}
Profiling in instruments it appears that List creates meta data for every item in order to track changes (for things like insert/delete animations).
So even though List is optimized to avoid creating invisible rows, it still has the meta data overhead that the direct UITableView implementation doesn't incur.
One other demonstration of the overhead of List is to instead use a ScrollView/LazyVStack combination. I don't recommend this as an alternative (in addition to the visual differences, it will blow up as you scroll down the list), but because it doesn't do change tracking, it too will have a fairly quick initial display.
I'm in trouble with passing array data from one vc (which is table view controller, where I have news with come categories). I put these categories in array, then sort this array for unique elements, and this sorted array (which contains about 7 categories) I want to pass to another view controller.
What I have: I've created bar button item "Filter" and create an action for this button, called "filterButtonTapped". Inside action I've called method sorting array and called pushViewController
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//this is sortedArray
var categories: [String]?
//this is sorting method
func sortingArray() {
var arrayOfAllCategories = [""]
if let rssItems = rssItems {
for item in rssItems {
//print(item.category)
arrayOfAllCategories.append(item.category)
}
arrayOfAllCategories.remove(at: 0)
categories = arrayOfAllCategories.unique
}
}
}
//this is IBAction
#IBAction func filterButtonTapped(_ sender: UIBarButtonItem) {
sortingArray()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondVC = storyboard.instantiateViewController(withIdentifier: "FilterViewController") as! FilterViewController
secondVC.arrayOfcategories = categories ?? [""]
self.navigationController?.pushViewController(secondVC, animated: true)
print("filter button tapped")
}
The problem is that
in the second view controller this array of categories is appearing (I print it and see the result), but cells apparently dont see this data, because this table view is empty after launching app and also nothing happens when I tap the button:
class FilterViewController: UITableViewController {
var arrayOfcategories: [String]?
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.reloadData()
print(arrayOfcategories) //this print prints array in the console
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return arrayOfcategories?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryCell", for: indexPath) as! FilterCell
let category = arrayOfcategories?[indexPath.row]
cell.categoryLabel.text = category
return cell
}
}
What am I doing wrong? How pass this array and use it to fill cells?
Update: Seems you're not registering your UITableViewCell here, add this line to viewDidLoad if you've created the cell programmatically (if you're using nib then register cell using nib parameter):
tableView.register(FilterCell.self, forCellReuseIdentifier: "CategoryCell")
Add an observer for arrayOfcategories for when it sets to reload your data to the UITableView like this:
var arrayOfcategories: [String]? {
didSet {
tableView.reloadData()
}
}
Try to add these methods: Register Nib (if you've created the cell programmatically) and try to pass number of section 1:
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(FilterCell.self, forCellReuseIdentifier: "CategoryCell")
self.tableView.reloadData()
print(arrayOfcategories) //this print prints array in the console
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
In ViewController A (ParameterViewController), I created a Parameter Model that takes in the following:
Parameter(name: "Alkalinity", symbol: "Kh", productName: ["item one", "item two", "item three"])
View Controller A displays a collection view cell and in the cell parameter name and symbol is used. When the user taps a cell (alkalinity), I want to pass the productName array to view controller B.
I think I may be faulty In my model, but here is what I did so far.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let productViewController = segue.destination as? ProductSelectionViewController else {
fatalError()
}
if segue.identifier == productSegueIdentifier {
if let indexPaths = collectionView.indexPathsForSelectedItems {
let indexPath = indexPaths[0]
let productList = parameters[indexPath.item] as Parameter
print("The Product List Is: \(productList)")
let productNames: [String] = productList.productName
print("The Product Names are \(productNames)")
productViewController.products = productNames
}
}
}
View Controller B (ProductViewController) looks like this.
class ProductSelectionViewController: UITableViewController {
var products = [String]()
let identifier = "ProductCell"
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.largeTitleDisplayMode = .never
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return products.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
let productItem = products[indexPath.row]
cell.textLabel?.text = productItem
return cell
}
I feel that since I declare product as [String] it is horrible architecture. When I declare it as Parameter I get the error cannot assign Parameter to type String
You can fix your issue by creating your model as follows:
struct Parameter {
var name : String
var symbol: String
var productsName: [ProductsName]
}
struct ProductsName {
var productName: String
}
Create two model where first one for your parameter model and second one for your product name list.
Then after pass your parameter as product, no need will not be horrible architecture.
In ViewController A, update following line of code:
let productNames: [ProductsName] = productList.productsName
In ViewController B, update following line of code:
var products = [ProductsName]()
then after after access name by:
products[indexPath.row].productName
I hope this will be solution what you are looking for.
We should avoid creating string arrays inside our model because they are not scalable in future. Instead create you model parameter like
struct Paramerter {
name: String?
symbol: String?
products: [Product]?
}
struct Product {
name: String?
}
Now initialize your Parameter model in View controller A as
let product1 = Product(name:"item one")
let product2 = Product(name:"item two")
let product3 = Product(name:"item three")
Parameter(name: "Alkalinity", symbol: "Kh", products: [product1, product2, product3])
Your segue method will remain same. View Controller B (ProductViewController) looks like
class ProductSelectionViewController: UITableViewController {
var products: [Product]?
let identifier = "ProductCell"
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.largeTitleDisplayMode = .never
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return products?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
let productItem = products[indexPath.row]
cell.textLabel?.text = productItem?.name
return cell
}
}
Now your Product model is fully scalable, If in future it need to add any more items like product description, product Id etc so you just need to update your Product model and you are ready to get set values from your Product model array.
Still very much a Swift noob, I have been looking around for a proper way/best practice to manage row deletions in my UITableView (which uses custom UserCells) based on tapping a UIButton inside the UserCell using delegation which seems to be the cleanest way to do it.
I followed this example: UITableViewCell Buttons with action
What I have
UserCell class
protocol UserCellDelegate {
func didPressButton(_ tag: Int)
}
class UserCell: UITableViewCell {
var delegate: UserCellDelegate?
let addButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Add +", for: .normal)
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
addSubview(addButton)
addButton.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -6).isActive = true
addButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
addButton.heightAnchor.constraint(equalToConstant: self.frame.height / 2).isActive = true
addButton.widthAnchor.constraint(equalToConstant: self.frame.width / 6).isActive = true
}
func buttonPressed(_ sender: UIButton) {
delegate?.didPressButton(sender.tag)
}
}
TableViewController class:
class AddFriendsScreenController: UITableViewController, UserCellDelegate {
let cellId = "cellId"
var users = [User]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! UserCell
cell.delegate = self
cell.tag = indexPath.row
return cell
}
func didPressButton(_ tag: Int) {
let indexPath = IndexPath(row: tag, section: 0)
users.remove(at: tag)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
where the Users in users are appended with a call to the database in the view controller.
My issues
The button in each row of the Table View is clickable but does not do anything
The button seems to be clickable only when doing a "long press", i.e. finger stays on it for a ~0.5s time
Will this method guarantee that the indexPath is updated and will not fall out of scope ? I.e. if a row is deleted at index 0, will deleting the "new" row at index 0 work correctly or will this delete the row at index 1 ?
What I want
Being able to click the button in each row of the table, which would remove it from the tableview.
I must be getting something rather basic wrong and would really appreciate if a Swift knight could enlighten me.
Many thanks in advance.
There are at least 3 issues in your code:
In UserCell you should call:
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
once your cell has been instantiated (say, from your implementation of init(style:reuseIdentifier:)) so that self refers to an actual instance of UserCell.
In AddFriendsScreenController's tableView(_:cellForRowAt:) you are setting the tag of the cell itself (cell.tag = indexPath.row) but in your UserCell's buttonPressed(_:) you are using the tag of the button. You should modify that function to be:
func buttonPressed(_ sender: UIButton) {
//delegate?.didPressButton(sender.tag)
delegate?.didPressButton(self.tag)
}
As you guessed and as per Prema Janoti's answer you ought to reload you table view once you deleted a row as your cells' tags will be out of sync with their referring indexPaths. Ideally you should avoid relying on index paths to identify cells but that's another subject.
EDIT:
A simple solution to avoid tags being out of sync with index paths is to associate each cell with the User object they are supposed to represent:
First add a user property to your UserCell class:
class UserCell: UITableViewCell {
var user = User() // default with a dummy user
/* (...) */
}
Set this property to the correct User object from within tableView(_:cellForRowAt:):
//cell.tag = indexPath.row
cell.user = self.users[indexPath.row]
Modify the signature of your UserCellDelegate protocol method to pass the user property stored against the cell instead of its tag:
protocol UserCellDelegate {
//func didPressButton(_ tag: Int)
func didPressButtonFor(_ user: User)
}
Amend UserCell's buttonPressed(_:) action accordingly:
func buttonPressed(_ sender: UIButton) {
//delegate?.didPressButton(sender.tag)
//delegate?.didPressButton(self.tag)
delegate?.didPressButtonFor(self.user)
}
Finally, in your AddFriendsScreenController, identify the right row to delete based on the User position in the data source:
//func didPressButton(_ tag: Int) { /* (...) */ } // Scrap this.
func didPressButtonFor(_ user: User) {
if let index = users.index(where: { $0 === user }) {
let indexPath = IndexPath(row: index, section: 0)
users.remove(at: index)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
Note the if let index = ... construct (optional binding) and the triple === (identity operator).
This downside of this approach is that it will create tight coupling between your User and UserCell classes. Best practice would dictate using a more complex MVVM pattern for example, but that really is another subject...
There is a lot of bad/old code on the web, even on SO. What you posted has "bad practice" written all over it. So first a few pointers:
Avoid an UITableViewController at all cost. Have a normal view controller with a table view on it
Delegates should always be weak unless you are 100% sure what you are doing
Be more specific when naming protocols and protocol methods
Keep everything private if possible, if not then use fileprivate. Only use the rest if you are 100% sure it is a value you want to expose.
Avoid using tags at all cost
The following is an example of responsible table view with a single cell type which has a button that removes the current cell when pressed. The whole code can be pasted into your initial ViewController file when creating a new project. In storyboard a table view is added constraint left, right, top, bottom and an outlet to the view controller. Also a cell is added in the table view with a button in it that has an outlet to the cell MyTableViewCell and its identifier is set to "MyTableViewCell".
The rest should be explained in the comments.
class ViewController: UIViewController {
#IBOutlet private weak var tableView: UITableView? // By default use private and optional. Always. For all outlets. Only expose it if you really need it outside
fileprivate var myItems: [String]? // Use any objects you need.
override func viewDidLoad() {
super.viewDidLoad()
// Attach table viw to self
tableView?.delegate = self
tableView?.dataSource = self
// First refresh and reload the data
refreshFromData() // This is to ensure no defaults are visible in the beginning
reloadData()
}
private func reloadData() {
myItems = nil
// Simulate a data fetch
let queue = DispatchQueue(label: "test") // Just for the async example
queue.async {
let items: [String] = (1...100).flatMap { "Item: \($0)" } // Just generate some string
Thread.sleep(forTimeInterval: 3.0) // Wait 3 seconds
DispatchQueue.main.async { // Go back to main thread
self.myItems = items // Assign data source to self
self.refreshFromData() // Now refresh the table view
}
}
}
private func refreshFromData() {
tableView?.reloadData()
tableView?.isHidden = myItems == nil
// Add other stuff that need updating here if needed
}
/// Will remove an item from the data source and update the array
///
/// - Parameter item: The item to remove
fileprivate func removeItem(item: String) {
if let index = myItems?.index(of: item) { // Get the index of the object
tableView?.beginUpdates() // Begin updates so the table view saves the current state
myItems = myItems?.filter { $0 != item } // Update our data source first
tableView?.deleteRows(at: [IndexPath(row: index, section: 0)], with: .fade) // Do the table view cell modifications
tableView?.endUpdates() // Commit the modifications
}
}
}
// MARK: - UITableViewDelegate, UITableViewDataSource
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myItems?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell", for: indexPath) as? MyTableViewCell {
cell.item = myItems?[indexPath.row]
cell.delegate = self
return cell
} else {
return UITableViewCell()
}
}
}
// MARK: - MyTableViewCellDelegate
extension ViewController: MyTableViewCellDelegate {
func myTableViewCell(pressedMainButton sender: MyTableViewCell) {
guard let item = sender.item else {
return
}
// Delete the item if main button is pressed
removeItem(item: item)
}
}
protocol MyTableViewCellDelegate: class { // We need ": class" so the delegate can be marked as weak
/// Called on main button pressed
///
/// - Parameter sender: The sender cell
func myTableViewCell(pressedMainButton sender: MyTableViewCell)
}
class MyTableViewCell: UITableViewCell {
#IBOutlet private weak var button: UIButton?
weak var delegate: MyTableViewCellDelegate? // Must be weak or we can have a retain cycle and create a memory leak
var item: String? {
didSet {
button?.setTitle(item, for: .normal)
}
}
#IBAction private func buttonPressed(_ sender: Any) {
delegate?.myTableViewCell(pressedMainButton: self)
}
}
In your case the String should be replaced by the User. Next to that you will have a few changes such as the didSet in the cell (button?.setTitle(item.name, for: .normal) for instance) and the filter method should use === or compare some id or something.
try this -
update didPressButton method like below -
func didPressButton(_ tag: Int) {
let indexPath = IndexPath(row: tag, section: 0)
users.remove(at: tag)
tableView.reloadData()
}
I currently have 2 table view controllers. I've added two disclosure indicators on two static cells for marital status and home state (canton). The user clicks on one of both and is taken to another view controller where he makes the appropriate selection.
The code is currently working for marital status. My question is if here I could reuse the second view controller (i.e. the one with the dynamic cells) for the same purpose but utilising a different array (in this case an array with states' names). For me it is clear that I could simply add a new view controller and implement the states' list there. Here is a screenshot of the storyboard:
First View Controller code:
import UIKit
class FirstTableViewController: UITableViewController, DataEnteredDelegate {
#IBOutlet var maritalStatusCell: UITableViewCell!
#IBOutlet var maritalStatusLabel: UILabel!
func userDidEnterInformation(info: String) {
maritalStatusLabel.text = "Marital Status: (\(info))"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "maritalStatusSegue" {
let sendingVC: SecondTableViewController = segue.destination as! SecondTableViewController
sendingVC.delegate = self
}
}
}
Second View Controller code:
import UIKit
protocol DataEnteredDelegate {
func userDidEnterInformation(info: String)
}
class SecondTableViewController: UITableViewController {
let maritalStatusArray: [String] = ["Single", "Married"]
let cantonArray: [String] = ["ZG", "ZH", "BE", "LU", "AG"]
var delegate: DataEnteredDelegate? = nil
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return maritalStatusArray.count
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if delegate != nil {
let information: String? = tableView.cellForRow(at: indexPath)?.textLabel?.text
delegate!.userDidEnterInformation(info: information!)
dismiss(animated: true, completion: nil)
self.navigationController?.popViewController(animated: true)
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MaritalStatusCell", for: indexPath)
cell.textLabel?.text = maritalStatusArray[indexPath.row]
return cell
}
}
Does is make sense here to use the second table view controller for the states' list as well ? If yes, how can I implement that ? Thanks.
Yes you can use the Same View controller for displaying the Array of your states' names which I think you have declared in cantonArray, what you need to do is declare a bool variable in Second View Controller (In case if you want to manage only two arrays, if you want to manage more arrays then declare an enum). Then in the segue get from which index that segue is fired, you can get the selected indexPath like this
if let indexPath = tableView.indexPathForSelectedRow{
}
Now check the indexPath.row, if it is 0 then you have selected Marital State so you need to show maritalStatusArray array so make the bool variable true if you get indexpath.row = 1 then make that variable false
Now in Second View Controller add a condition as per the bool variable and show the data from that array like this
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MaritalStatusCell", for: indexPath)
if showMaritalArray {
cell.textLabel?.text = maritalStatusArray[indexPath.row]
} else {
cell.textLabel?.text = cantonArray[indexPath.row]
}
return cell
}
This is how you can declare enum
enum SelectedRow {
case MaritalStatus
case States
case ThirdRow
}
var selectedRow = SelectedRow.MaritalStatus