0

I have a UIViewController that instantiates some UITableViewCell according to the desired option on the screen. One of theses options is "instructions", and I created a .XIB file to show the instructions on the screen. But, on this screen, I have a button to show more instruction (in another screen). So, I created another .XIB file with the instructions and I need to go there from the first instructions screen. How can I do that?

This is my code:

import UIKit

class FirstInstructionsCell: UITableViewCell {

    @IBOutlet weak var showSecondInstructions: UIButton!

    var secondInstructionsView: SecondInstructions!

    static let FIRST_INSTRUCTIONS_CELL_IDENTIFIER = "FirstInstructionsCell"

    @IBAction func showSecondInstructionsTapped(_ sender: UIButton) {
        print("Here I need to go to SecondInstructions screen")
    }
}

After changes, my UIViewController:

import UIKit

class FirstInstructionsViewController: UIViewController, FirstInstructionsCellDelegate {
    func goSecondScreen() {
        presentViewController(SecondInstructions, animated: true, completion: nil) 
// Here I have an error: Cannot convert value of type 'SecondInstructions.Type' to expected argument type 'UIViewController'
// 'SecondInstructions' is a UITableViewCell
    }

    @IBOutlet weak var confirmButton: UIButton!

    @IBOutlet weak var tableView: UITableView!

    var withdrawalCode: Int!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.register(UINib(nibName: FirstInstructionsCell.FIRST_INSTRUCTIONS_CELL_IDENTIFIER, bundle: nil), forCellReuseIdentifier: FirstInstructionsCell.FIRST_INSTRUCTIONS_CELL_IDENTIFIER)

        self.tableView.rowHeight = UITableView.automaticDimension
        self.tableView.estimatedRowHeight = 470.0
    }


    //MARK: - Actions

    @IBAction func onConfirmClicked(_ sender: UIButton) {
        self.navigationController?.popViewController(animated: true)
    }

}

// MARK: - UITableViewDelegate
extension FirstInstructionsViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return 13.0
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 142.0
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return PayPaxxHeaderView.loadFromNib(CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 142.0)) as! PayPaxxHeaderView
    }
}

// MARK: - UITableViewDataSource
extension FirstInstructionsViewController: UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCell(withIdentifier: FirstInstructionsCell.FIRST_INSTRUCTIONS_CELL_IDENTIFIER, for: indexPath) as! FirstInstructionsCell
        cell.fillCell(self.withdrawalCode)
        cell.delegate = self
        return cell
    }
}
Kim Ruan
  • 90
  • 1
  • 10

1 Answers1

3

As far as I understood, you need to go to another screen. Why not use protocol for that?

import UIKit

protocol FirstInstructionsCellDelegate {
    func goToSecondScreen()
}

class FirstInstructionsCell: UITableViewCell {

    @IBOutlet weak var showSecondInstructions: UIButton!

    var secondInstructionsView: SecondInstructions!
    var delegate : FirstInstructionsCellDelegate?

    static let FIRST_INSTRUCTIONS_CELL_IDENTIFIER = "FirstInstructionsCell"

    @IBAction func showSecondInstructionsTapped(_ sender: UIButton) {
        print("Here I need to go to SecondInstructions screen")
        delegate?.goToSecondScreen()
    }
}

Then, in your cellForRow(at:indexPath:) you can say (after dequeueing):

cell.delegate = self

Your viewController needs to implement it:

class FirstInstructionViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, FirstInstructionsCellDelegate {

and then, in that View Controller, implement said function:

func goToSecondScreen() {
    let story = UIStoryboard(named: "SecondScreen", bundle: nil)
    let viewController = story.instantiateViewController(withIdentifier: "SecondScreen")
    self.navigationController?.pushViewController(viewController, animated: true)
    // or 
    self.present(viewController, animated: true, completion: nil)
}

NOTE:

you need to set storyboard identifier in interface builder.

Miknash
  • 7,888
  • 3
  • 34
  • 46
  • I implemented the changes that you instructed, but I'm having some errors. Can you help me? – Kim Ruan Jul 22 '19 at 18:04
  • Sure thing - you cant just say class name. I will update answer – Miknash Jul 22 '19 at 18:05
  • Give it a try now – Miknash Jul 22 '19 at 18:07
  • Please, see the changes I made in the question, I put more codes so that you better understand my problem – Kim Ruan Jul 22 '19 at 18:08
  • you can use present with the code above - the trick is to call it from storyboard – Miknash Jul 22 '19 at 18:10
  • Any luck with presenting view controller? – Miknash Jul 22 '19 at 18:26
  • Unfortunately not because I do not have a UIViewController or another storyboard. I just have a .XIB file containing the second instructions and a .SWIFT file that is a UITableViewCell – Kim Ruan Jul 22 '19 at 18:43
  • here is how to get it from nib : https://stackoverflow.com/questions/37046573/loading-viewcontroller-from-xib-file – Miknash Jul 22 '19 at 18:45
  • Let me explain batter: I have a main.storyboard (MAIN) and a UIViewController (VC1), and two screens (First and Second, each one has a .XIB file and a swift file and each one are a UITableViewCell, bust just the First is instantiated from VC1). A need to instantiate (or call) a Second screen from First screen, tapping a button. – Kim Ruan Jul 22 '19 at 19:22
  • This code does exactly that - you will get the call from the button and then you can do whatever you want - in my example you get the view controller from storyboard, but instead of let story and let viewController you can use let myViewController = SecondInstructions(nibName: "SecondInstructions", bundle: nil) and then push/present as shown above – Miknash Jul 22 '19 at 19:24
  • Yes, but the SecondInstructions.swift is a UITableViewCell. So I'm having an error when I try to call it, because it is not a UIViewController. This code: `let myViewController = SecondInstructions(nibName: "SecondInstructions", bundle: nil)` do not works because I do not have another UIViewController to call. I'm doing something wrong? – Kim Ruan Jul 22 '19 at 19:28
  • I'm having this error: `Argument labels '(nibName:, bundle:)' do not match any available overloads` – Kim Ruan Jul 22 '19 at 19:29
  • to show any cell you will need to have controller for it - just create one similar to one that you already have just use another cell. – Miknash Jul 22 '19 at 20:02
  • Why this do not works: `func goSecondScreen() { self.tableView.register(UINib(nibName: SecondInstructions.SECOND_INSTRUCTIONS, bundle: nil), forCellReuseIdentifier: SecondInstructions.SECOND_INSTRUCTIONS) self.tableView.rowHeight = UITableView.automaticDimension self.tableView.estimatedRowHeight = 470.0 print("Go to another screen") }`. I'm doing this on my UIViewController (see the second code on my question). – Kim Ruan Jul 22 '19 at 20:09
  • because you will need additional condition in cellForRow to know what item to show. – Miknash Jul 22 '19 at 20:10
  • I make this changes: `if indexPath.row == 0 { let itemCell = self.tableView.dequeueReusableCell(withIdentifier: FirstInstructionsCell.FIRST_INSTRUCTIONS_CELL_IDENTIFIER, for: indexPath) as! FirstInstructionsCell itemCell.fillCell(self.withdrawalCode) cell = itemCell itemCell.delegate = self } else { let itemCell = self.tableView.dequeueReusableCell(withIdentifier: SecondInstructionsCell.SECOND_INSTRUCTIONS, for: indexPath) as! SecondInstructionsCell } return cell!` – Kim Ruan Jul 22 '19 at 20:24
  • But not work. I'm having this error: `Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier SecondInstructionsCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'` – Kim Ruan Jul 22 '19 at 20:25
  • 2
    register cells in viewDidLoad - but you will need flag to show other cell. below class FirstViewController add var shouldAddSecondCell : Bool = false. in goToSecondScreen, add self.shouldAddSecondCell = true and self.tableView.reloadData(). in numberOfCells, set return shouldAddSecondCell ? 2 : 1 – Miknash Jul 22 '19 at 20:31
  • No problem, glad I helped. Happy coding – Miknash Jul 22 '19 at 20:52