Expandable table menu to new View Controller

I have a realy big problem i cant solve. How can I link the menu name "Canada" in my expandable table menu to a new View Controller named "Canada", and then the menu name "Denmark" to a View Controller named "Denmark" and so on in my Xcode-project? Maybe by using segues?

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, ExpandableHeaderViewDelegate {
    @IBOutlet weak var tableView: UITableView!
  
  

var sections = [
    Section(genre: "North America",
            menytitel: ["USA", "Canada"],
            expanded: false),
    Section(genre: "Europe",
            menytitel: ["Sweden", "Finland", "Denmark", "Ireland"],
            expanded: false),
    Section(genre: "Africa",
            menytitel: ["Egypt", "Marocko"],
            expanded: false),
]

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    tableView.delegate = self
    tableView.dataSource = self
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func numberOfSections(in tableView: UITableView) -> Int {
    return sections.count
}


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return sections[section].menytitel.count
}

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

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if (sections[indexPath.section].expanded) {
        return 44
    } else {
        return 0
    }
}

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

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let header = ExpandableHeaderView()
    header.customInit(title: sections[section].genre, section: section, delegate: self)
    return header
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "labelCell")!
    cell.textLabel?.text = sections[indexPath.section].menytitel[indexPath.row]
    return cell
}

func toggleSection(header: ExpandableHeaderView, section: Int) {
    sections[section].expanded = !sections[section].expanded
    
    
    tableView.beginUpdates()
    for i in 0 ..< sections[section].menytitel.count {
        tableView.reloadRows(at: [IndexPath(row: i, section: section)], with: .automatic)
    }
    tableView.endUpdates()
}


    
}



This is my section.swift file

import Foundation

struct Section {
   var genre: String!
   var menytitel: [String]!
   var expanded: Bool!

init(genre: String, menytitel: [String], expanded: Bool) {
   self.genre = genre
   self.menytitel = menytitel
   self.expanded = expanded
    }
}

Accepted Reply

From Sweden, how cold today ?


What I mean:

In didSelectRowAt, you can get the identifier with


Then call

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let id = sections[indexPath.section].menytitel[indexPath.row]
        self.performSegue(withIdentifier: id, sender: self)
    }

Replies

Where is ExpandableHeaderViewDelegate defined ? I do not find it in documentation.


Is it a protocol you've added, from h ttps://github.com/AnonymousAllyy/OMA/blob/master/Patch/ExpandableHeaderView.swift

or from here

https://stackoverflow.com/questions/49024490/uitapgesturerecognizer-not-working-swift-2


Where do you call the menu action to move to the destination view ?


I would see several ways to do this (but I never used Expandable, so not sure):


1. solution A

- Complement section definition with the name of a segue to the right destination (and create all those segues with their identifier to link the original view to the country view

struct Section {
   var genre: String!
   var menytitel: [String]!
   var menuSegues: [String]
   var expanded: Bool!

- then, where you trigger the transition (button ? gesture ?), call performSegue withy the relevant identifier read from section, corresponding to menuTitle menytitel[x]

Note: you could also use the menuTitle as segue identifier,

or, change as

var menuTitleAndSegue: [(title: String, segue: String)]


2 remarks:

- no need to force unwrap with ! in the struct

- why do you call meny and not menu ?

- names should be camelCase as menuTitle


2. solution B

Do similar, but create segues in code when needed


3. solution C

include a closure to trigger directly

define type

typealias SegueClosure = (String) -> ()


struct Section {
   var genre: String!
   var menytitel: [String]!
   var segueClosure: SegueClosure
   var expanded: Bool!


define a var:

var mySegue = { (id: String) -> () in performSegue(withIdentifier: id, sender:nil) }

Then, when you need to activate:

let segueName = menytitel[x] // select the right one
mySegue(segueName)

Okey thank you.

I will test your first solution first, but I dont understand what you meen with this?

call performSegue withy the relevant identifier read from section, corresponding to menuTitle menytitel[x]


But i tried somtehing likte this after I google it.


var sections = [
    Section(genre: "North America",
            menytitel: ["USA", "Canada"],
            expanded: false),
    Section(genre: "Europe",
            menytitel: ["Sweden", "Finland", "Denmark", "Ireland"],
            expanded: false),
    Section(genre: "Africa",
            menytitel: ["Egypt", "Marocko"],
            expanded: false),
]


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.performSegue(withIdentifier: "USA", sender: self)
    }
       


Sorry for my name menytitel. Meny is menu in swedish.


Here is my ExpandableHeaderView


import UIKit

protocol ExpandableHeaderViewDelegate {
    func toggleSection(header: ExpandableHeaderView, section: Int)
}

class ExpandableHeaderView: UITableViewHeaderFooterView {
    var delegate: ExpandableHeaderViewDelegate?
    var section: Int!
    
    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(selectHeaderAction)))
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc func selectHeaderAction(gestureRecognizer: UITapGestureRecognizer) {
        let cell = gestureRecognizer.view as! ExpandableHeaderView
        delegate?.toggleSection(header: self, section: cell.section)
    }
    
    func customInit(title: String, section: Int, delegate: ExpandableHeaderViewDelegate) {
        self.textLabel?.text = title
        self.section = section
        self.delegate = delegate
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        self.textLabel?.textColor = UIColor.white
        self.contentView.backgroundColor = UIColor.darkGray
    }

From Sweden, how cold today ?


What I mean:

In didSelectRowAt, you can get the identifier with


Then call

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let id = sections[indexPath.section].menytitel[indexPath.row]
        self.performSegue(withIdentifier: id, sender: self)
    }

Its around 0 degrees celsius here in Sweden now. Where are you based?


Omg you solved my problem. I used your code below.


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {  
        let id = sections[indexPath.section].menytitel[indexPath.row]  
        self.performSegue(withIdentifier: id, sender: self)  
    } 


But I had to add a ! after that segue string, like this:


import Foundation

struct Section {
   var genre: String!
   var menytitel: [String]!
   var menuSegues: [String]!
   var expanded: Bool!

    
init(genre: String, menytitel: [String], expanded: Bool) {
   self.genre = genre
   self.menytitel = menytitel
   self.expanded = expanded
    
}
}



/ Fredrik

I'm based in Toulouse (visible on my forum identity card). About 10°C here.


Without the unwrap, you get the error: Return from initializer without initializing all stored properties


But you may simply initialize with

        self.menuSegues = []


I do not find good practice to have unwrapped optional everywhere. And not needed, just write:


struct Section {
    var genre: String
    var menytitel: [String]
    var menuSegues: [String]
    var expanded: Bool

    init(genre: String, menytitel: [String], expanded: Bool) {
        self.genre = genre
        self.menytitel = menytitel
        self.expanded = expanded
        self.menuSegues = []
    }
}

And you get the same.


unwrapped optional are used for IBOutlets, because IBOutlet are optional;

if you write

    @IBOutlet weak var aLabel: UILabel

you get compiler error: @IBOutlet property has non-optional type 'UILabel'

If you write

   @IBOutlet weak var aLabel: UILabel?


Then you have to unwrap aLabel! each time you use.

Hence the unwrapped optional.


have a good continuation.