jRuMoL
jRuMoL

Reputation: 349

Perform Segue from a custom Button

I created a custom button to include in the button the information I need:

class CustomButton: UIButton {   
   var urlString: String?
   var documentType: String?
   var requestEntity: String?
   var requestSortBy: [NSSortDescriptor]?
   var requestPredicate: NSPredicate?
}

When I create the cells in the collectionView (embedded in a tableView) I include the information in my custom button, called documentTypeButton. And also the target for executing the function HomeTableViewController.documentTypeButtonTapped(_:) to perform the segue.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let languageSelected = UserDefaults().value(forKey: "language") as! String

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "allDocumentsItemCell", for: indexPath) as! AllDocumentsCollectionViewCell

    cell.documentTypeButton.documentType = Catalog.sharedInstance.allSections[allDocumentsSection!].singleItems[indexPath.row].name
    cell.documentTypeButton.requestEntity = Catalog.sharedInstance.allSections[allDocumentsSection!].singleItems[indexPath.row].entityName
    cell.documentTypeButton.requestSortBy = Catalog.sharedInstance.allSections[allDocumentsSection!].singleItems[indexPath.row].sortBy
    cell.documentTypeButton.requestPredicate = Catalog.sharedInstance.allSections[allDocumentsSection!].singleItems[indexPath.row].predicate
    cell.documentTypeButton.addTarget(nil, action: #selector(HomeTableViewController.documentTypeButtonTapped(_:)), for: .touchUpInside)


    return cell

}

And the, in the HomeTableViewController I have the function to execute when touching inside the button:

@objc func documentTypeButtonTapped(_ sender: CustomButton) {

    performSegue(withIdentifier: "goToDocumentList", sender: CustomButton())
    print(sender.documentType)
    print(sender.requestEntity)

}

func prepare(for segue: UIStoryboardSegue, sender: CustomButton) {

    let languageSelected = UserDefaults().value(forKey: "language") as! String

    if let destination = segue.destination as? DocumentListTableViewController {

        destination.titlePassed = sender.documentType!.localized(lang: languageSelected)
        destination.requestedEntity = sender.requestEntity
        destination.requestedSortBy = sender.requestSortBy
        destination.requestedPredicate = sender.requestPredicate

    }

}

The print statements above work well and print the information. And the segue goes to the DocumentListTableViewController.

In the viewDidLoad of the DocumentListTableViewController I added the following:

override func viewDidLoad() {
    super.viewDidLoad()

    print(titlePassed)
    print(requestedEntity)

}

And the two print statements print nil.

I guess the problem should be in the prepareForSegue, maybe with the sender... I don't know.

Any help would be appreciated.

Upvotes: 3

Views: 696

Answers (3)

vadian
vadian

Reputation: 285079

The issue is that you pass a new (empty) instance of CustomButton in the performSegue call rather than the sender of the action.

But your design to create a custom button as data container is still worse. Don't do that. It's horrible.

There is a much easier and more efficient solution:

In AllDocumentsCollectionViewCell create a callback closure

var callback : (()->Void)?

and an IBAction and connect it to the button, the callback is called when the button is tapped.

@IBAction func documentTypeButtonTapped(_ sender: UIButton) {
    callback?()
}

In HomeTableViewController assign a closure to the callback, perform the segue in there and pass the item of the data source array.

But the way calling UserDefaults().value( in each call of collectionView and getting the same item for index path 4 times is very very inefficient. And never use value(forKey. There is string(forKey in UserDefaults.

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let languageSelected = UserDefaults().string(forKey: "language")! 

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "allDocumentsItemCell", for: indexPath) as! AllDocumentsCollectionViewCell
    cell.callback {
         let item = Catalog.sharedInstance.allSections[allDocumentsSection!].singleItems[indexPath.row]
         self.performSegue(withIdentifier: "goToDocumentList", sender: item)
    }
    return cell

}

In prepare(for get the data source item (I have no idea what type it is) and retrieve the information from there. You can even pass the entire item to the destination view controller.

func prepare(for segue: UIStoryboardSegue, sender: CustomButton) {

    let languageSelected = UserDefaults().string(forKey: "language")! 

    if let destination = segue.destination as? DocumentListTableViewController {
        let item = sender as! WhatEverTheTypeIs // change this to the real type
        destination.titlePassed = item.name.localized(lang: languageSelected)
        destination.requestedEntity = item.entityName
        destination.requestedSortBy = item.sortBy
        destination.requestedPredicate = item.predicate

    }
}

Upvotes: 2

Arie Pinto
Arie Pinto

Reputation: 1292

In your performSegue call your are sending a new instance of CustomButton

performSegue(withIdentifier: "goToDocumentList", sender: CustomButton())

So its parameters are going to be nil in the next view controller, you need to send the sender instead.

performSegue(withIdentifier: "goToDocumentList", sender: sender)

You need to override prepareForSegue for it to be called, no need to change to its signature, you could write it like this instead:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let languageSelected = UserDefaults().value(forKey: "language") as! String

    if let destination = segue.destination as? DocumentListTableViewController {

        let myCustomButton = sender as! CustomButton

        destination.titlePassed = myCustomButton.documentType!.localized(lang: languageSelected)
        destination.requestedEntity = myCustomButton.requestEntity
        destination.requestedSortBy = myCustomButton.requestSortBy
        destination.requestedPredicate = myCustomButton.requestPredicate

    }
}

Upvotes: 2

bezoadam
bezoadam

Reputation: 587

Try to pass sender parameter in performSegue() method like this:

@objc func documentTypeButtonTapped(_ sender: CustomButton) {

    performSegue(withIdentifier: "goToDocumentList", sender: sender)
    print(sender.documentType)
    print(sender.requestEntity)

}

Problem is that you are instantiating new object of type CustomButton() instead passing sender.

Upvotes: 4

Related Questions