aslanyanhaik / youtube-iOS

youtube iOS app template written in swift 5

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Programmatically Changing Tab

surayashivji opened this issue · comments

I'm trying to change the tab programmatically when a table view cell is being tapped

This is my current code:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let vc = MainViewController()
vc.didSelectItem(atIndex: 2)
}

When I do this, the app crashes in the MainViewController in the customization() method on the View Controller init. (crashes when setting the storyboard.

If I set self.viewsAreInitialized to true before I programmatically select the Item, the app crashes in TabBar.swift on the highlightItem function.

Is there a better way I can programmatically change the tab from one of the view controllers?

Probably the easiest way to do is to use NotificationCenter.
Add observer to MainViewController with implementation and post to that Notification from didSelectRowAt method.

P.S. That delegate method vc.didSelectItem(atIndex:) will work if your ViewController has TabBarDelegate delegate object and MainViewController is confirming. However I think it will scroll to given page but tabBar will not be updated.

Hi! Thanks for your response.

I'm trying to implement the notifications but I'm getting unrecognized selector sent to instance error on the tableView:didSelectRowAtIndexPath method.

This is my didSelectItem method in MainViewController:

    func didSelectItem(atIndex: Int) 
{
        self.collectionView.scrollRectToVisible(CGRect.init(origin: CGPoint.init(x: (self.view.bounds.width * CGFloat(atIndex)), y: 0), size: self.view.bounds.size), animated: true)
        
        let notificationName = Notification.Name("switchT")
        NotificationCenter.default.addObserver(self, selector: #selector(SearchViewController.tableView(_:didSelectRowAt:)), name: notificationName, object: nil)
        }

This is my didSelectRow method in my SearchViewController (view controller with the table view)

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        print("item selected: \(indexPath.row)")
        
        let notificationName = Notification.Name("switchT")
        
        NotificationCenter.default.post(name: notificationName, object: nil)
        
    }

Do you see anything wrong with this implementation?

My errors:
-[MainViewController tableView:didSelectRowAtIndexPath:]: unrecognized selector sent to instance
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:[.MainViewController tableView:didSelectRowAtIndexPath:]: unrecognized selector sent to instance

Appreciate your assistance! :)

You should put "addObserver" when ViewController is loaded (ViewDidLoad, ViewDidAppear). So iOS will add observer and start listening. Also instead of putting "SearchViewController.tableView(_:didSelectRowAt:)" method just create a new method with implementation and add as a selector. Something like this

override viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(SearchViewController. doSomething()), name: NSNotification.Name("switchT"), object: nil)
}

func doSomething() {
//do something
}

P.S. Don't forget to remove observer once you finish. Otherwise you will get memory leak.

Hi-- thanks so much for the info! Sorry, but one last question of confusion. I implemented your notes and have the addObserver in the viewDidLoad() of MainViewController:

    override func viewDidLoad() {
        self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
        super.viewDidLoad()
        print("mainv iew controller loading in")
        customization()
        didSelectItem(atIndex: 0)
        NotificationCenter.default.addObserver(self, selector: #selector(MainViewController.hideBar(notification:)), name: NSNotification.Name("hide"), object: nil)
        
        let notificationName = Notification.Name("switchT")
        NotificationCenter.default.addObserver(self, selector: #selector(SearchViewController.hello()), name: notificationName, object: nil)
    }

I'm getting an error on the addObserver line since I'm using "instance member hello on type SearchViewController."

In my SearchViewController, I have

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("item selected: \(indexPath.row)")
        hello()
    }
    
    func hello() {
        let notificationName = Notification.Name("switchT")
        NotificationCenter.default.post(name: notificationName, object: nil)
    }

How will the notification know to ever call didSelectItem?

Your help is much much appreciated!!

Hi Suraya, sorry for delay. Here is the problem:
NotificationCenter.default.addObserver(self, selector: #selector(SearchViewController.hello()), name: notificationName, object: nil)
You are adding observer you self (MainViewController) but selector is in SearchViewController. If SearchVC is not initialized posting a notification will crash. Here is how you should do

  1. in MainVC viewDidLoad() add notificationCetner
    let notificationName = Notification.Name("switchT")
    NotificationCenter.default.addObserver(self, selector: #selector(MainViewController. switchT(notification:)), name: notificationName, object: nil)

  2. You should have "switchT" method in MainViewController
    func switchT(notificaion: Notification) {
    if let index = notification.userInfo?["index"] as? Int {
    //call method to change tab with index (I guess didSelectItem(atIndex: index) method)
    }
    }

  3. From any ViewController do this to change tab programmatically:
    let data:[String: Int] = ["index": 2]
    let notificationName = Notification.Name("switchT")
    NotificationCenter.default.post(name: notificationName, object: data)

You need "data" dictionary to pass information.
Make sure MainViewController is initialized before posting the notification otherwise app will crash.
Also once you done with notification or MainViewController is deinitialized you should remove notification. Best place to do this is in ViewDidDisappear() in MainViewController.

viewDidDisappear() {
NotificationCenter.default.removeObserver(self)
}

P.S. if still you are getting errors, send me the source codes to check. 😉

Thank you so much! I haven't used Notifications before and this helped a ton. The tab switching is now working 😄