3

I am a new iOS programming. Now i am creating a sample app by not using storyboard which i implemented UICollectionView for the main class which scroll direction is vertical. A particular cell i added another UICollectionView for horizontal direction. The problem is that cell which horizontal scroll direction is not pushing to another view controller which i click on it.

Here is rootViewController that i setup in AppDelegate

window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let navigation = UINavigationController(rootViewController: CategoryController())
window?.rootViewController = navigation

Here is CategoryController which i implement tableView

tableView.register(TableCell.self, forCellReuseIdentifier: cellId)

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

    return cell
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 120
}

For tableView when i click on particular cell is working fine when i want to navigate to a particular controller

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    print(indexPath.item)
    let view = DetailEachCellController()
    navigationController?.pushViewController(view, animated: true)
}

The problem is that in DetailEachCellController i use UICollectionView for list down some data, and when i click on particular cell is not navigate to new controller

Here DetailEachCellController as UIViewController

var collectionDetailView: UICollectionView!
collectionDetailView.register(DetailContactCell.self, forCellWithReuseIdentifier: cellIdContact)
collectionDetailView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellIdContent)

func setupCollectionView() {
    collectionDetailView.topAnchor.constraint(equalTo: detailContent.topAnchor).isActive = true
    collectionDetailView.widthAnchor.constraint(equalTo: detailContent.widthAnchor).isActive = true
    collectionDetailView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    collectionDetailView.backgroundColor = .white 
}

func callAction() {
    print("pushing view")
    let view = UIViewController()
    self.navigationController?.pushViewController(view, animated: true)
}

Here the delegate for UICollectionViewFlowLayout

extension DetailEachCellController : UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 2
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if indexPath.item == 0 {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdContact, for: indexPath)

            return cell
        }
        else {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdContent, for: indexPath)
            cell.backgroundColor = .blue
            return cell
        }
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if indexPath.item == 1 {
          return CGSize(width: view.frame.width, height: 250)
        }
        return CGSize(width: view.frame.width, height: 120)
    }
}

Here is DetailContactCell which i added some buttons for user click and more to next view, but it is not moving but the function is called.

class DetailContactCell: UICollectionViewCell {

var eachCellController: DetailEachCellController?

lazy var callButton: UIButton = {

    let button = UIButton()
    button.translatesAutoresizingMaskIntoConstraints = false
    button.layer.shadowOpacity = 0.25
    button.backgroundColor = .white
    button.setTitle("CALL", for: .normal)
    button.layer.cornerRadius = 10
    button.layer.shadowOffset = CGSize(width: 0, height: 10)
    button.layer.shadowRadius = 10
    return button
}()
lazy var bookButton: UIButton = {

    let button = UIButton()
    button.translatesAutoresizingMaskIntoConstraints = false
    button.layer.shadowOpacity = 0.25
    button.backgroundColor = .white
    button.layer.cornerRadius = 10
    button.layer.shadowOffset = CGSize(width: 0, height: 10)
    button.layer.shadowRadius = 10
    return button
}()
override init(frame: CGRect) {
    super.init(frame: frame)
    addSubview(callButton)
    addSubview(bookButton)
    callButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
    callButton.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 15).isActive = true
    callButton.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1/2.5).isActive = true
    callButton.heightAnchor.constraint(equalToConstant: 60).isActive = true

    bookButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
    bookButton.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -15).isActive = true
    bookButton.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1/2.5).isActive = true
    bookButton.heightAnchor.constraint(equalToConstant: 60).isActive = true

    self.actionButton()
}

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

func actionButton() {
    callButton.addTarget(self, action: #selector(callBtn), for: .touchUpInside)
}

@objc func callBtn() {
    print("calling")
    eachCellController = DetailEachCellController()
    eachCellController?.callAction()
}

}
Kuldeep
  • 4,466
  • 8
  • 32
  • 59
I Love Coding
  • 580
  • 5
  • 13

4 Answers4

3

Because this

// eachCellController = DetailEachCellController() // comment it

is another VC not the presented one

eachCellController?.callAction()

you need to send self in cellForItemAt

cell.eachCellController = self

//

@objc func callBtn() {
   print("calling")
   eachCellController?.callAction()
}
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
  • Passing a strong instance of the `UIViewController` handling the `UICollectionView` to the `UICollectionViewCell` object is not good. This is vulnerable to retain cycle. Delegation pattern and handlers are made to fit in such cases. – Malloc Jul 23 '18 at 14:48
  • @Malloc he can declare it weak – Shehata Gamal Jul 23 '18 at 14:57
3
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    if indexPath.item == 0 {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdContact, for: indexPath) as! DetailContactCell
        // Here is line add to `UICollectionViewFlowLayout`
        cell.eachCellController = self

        return cell
    }else {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdContent, for: indexPath)
        cell.backgroundColor = .blue
        return cell
    }

}
Visal Sambo
  • 1,270
  • 1
  • 19
  • 33
2

You can't push a new view controller to the navigation controller stack from inside the UICollectionViewCell class.

The way to do so is to tell the DetailEachCellController(through a delegate method or a callback handler) that the button was clicked, then ONLY FROM DetailEachCellController you can push the view controller.

Here is a similar thread

Malloc
  • 15,434
  • 34
  • 105
  • 192
1

It is not convenient way to pass the strong object of UIViewController to cell. You may create retain cycle and your view controller will never been released as well as memory so it may cause a memory issue in future

To Fix this you may have following options

  1. Use Delegate
  2. Use Closure
  3. Implement Button action in viewcontroller itself with addTarget

As closure option is easy I am giving you a example to implement it.

In your collection view cell add following Property

var callBtnTapped : (() -> ())?

Now in .

@objc func callBtn() {
    print("calling") 
     // Check that target has implemented closure ? 
     if let clouser = self.callBtnTapped {
            clouser(screenShotID)
      }
}

In your view controller cellForItem add body to closure

     cell.callBtnTapped = {[weak self]  in
        guard let strongSelf = self   else  {return}
        strongSelf. callAction()
        // It will be called when button tapped 
    }

Hope it is helpful

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98