Jimmy
Jimmy

Reputation: 305

Swift 3: How do I tap a button inside cell and pass in that indexPath of that cell?

I am doing everything programatically, avoiding storyboards. I have a collectionView, a Post model object that holds a username and password and of course I have collectionView cells. I the issue I am having is that I have a button inside of my collectionView Cells. I want to be able to tap this button and segue into a new controller. This is the easy part. The problem is that I want to not only segue into the new controller but also pass in the specific username of that specific Post Cell of the button I tapped. I hope this makes sense. Basically I want to have a Post, just like any social media application and I want to segue to a "profile" and also pass in the username of that specific user to that profile controller too. I hope you can understand this. Any help will be highly, highly appreciated as I have been stuck on this for some time. I will of course mark as answer. Thank you guys...

class MyController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

var posts = [Post]()

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView?.backgroundColor = .white
    collectionView?.register(CustomCell.self, forCellWithReuseIdentifier: "cellId")
}

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return posts.count
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: view.frame.width, height: 50)
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CustomCell

    cell.post = posts[indexPath.item]
    cell.homeController = self

    return cell
}

class CustomCell: UICollectionViewCell, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

    var homeController: MyController?

    var post: Post? {
        didSet {
            if let username = post?.username {
                usernameLabel.text = username
            }
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .blue
        setupViews()
    }

    lazy var myButton: UIButton = {
        let btn = UIButton()
        btn.backgroundColor = .red
        btn.setTitle("Push", for: .normal)
        btn.isUserInteractionEnabled = false
        btn.addTarget(self, action: #selector(handleTapped), for: .touchUpInside)
        return btn
    }()

    let usernameLabel: UILabel = {
        let label = UILabel()
        label.textColor = .white
        return label
    }()

    func handleTapped() {
        let postController = PostController()
        homeController?.navigationController?.pushViewController(postController, animated: true)
    }

    func setupViews() {
        addSubview(myButton)

        myButton.frame = CGRect(x: (self.frame.width / 2) - 25, y: (self.frame.height / 2) - 25, width: 50, height: 50)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Upvotes: 2

Views: 2395

Answers (2)

Yannick
Yannick

Reputation: 3278

I suggest that you to tell your UIViewController that a button inside your CustomCell has been clicked. You can add a protocol, add a delegate property to your cell and then MyController has to conform to the protocol. The function should also pass in the cell that has been clicked and you can get the indexPath of that cell from your collectionView.

protocol CustomCellDelegate: class {
    func didSelectButton(in cell: CustomCell)
}

class CustomClass: UICollectionViewClass {
    weak var delegate: CustomCelLDelegate?
    func handleTapped() {
        delegate?.didSelectButton(in: self)
    }
}

Then in your MyController class, add the delegate and conform to the protocol.

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CustomCell

    cell.post = posts[indexPath.item]
    cell.delegate = self

    return cell
}

extension MyController: CustomCellDelegate {
    func didSelectButton(in cell: CustomCell) {
        if let indexPath = collectionView.indexPath(for: cell) {
            let postController = PostController()
            let post = posts[indexPath.row]
            postController.post = post //pass the data
            self.navigationController?.pushViewController(postController, animated: true)
        }
    }
}

You also need to add a post variable to your PostController.

class PostController {
    var post: Post!
}

Upvotes: 5

Nirav D
Nirav D

Reputation: 72460

You are not using the segue instead of that you have pushed the ViewController using the navigationController. So pass the value with the postController object.

func handleTapped() {
    let postController = PostController()
    //Pass the detail that you want
    postController.selectedPost = self.post
    homeController?.navigationController?.pushViewController(postController, animated: true)
}

Now in PostController declare one instance property of type Post with name selectedPost and then use that object in viewDidLoad to assign your Labels.

class PostController: UIViewController {
    var selectedPost: Post?

    override viewDidLoad() {
        super.viewDidLoad()
        self.yourLabel.text = self.selectedPost.username
    }

Upvotes: 2

Related Questions