Louis Sankey
Louis Sankey

Reputation: 492

Swift - Delegate through Nav Controller

I'm going to include my full code in this but I will try to give pointers to where the relevant bits are. Basically I am returning to a view controller from an Unwind Segue with some new data. I am using that data successfully in the 'NBARotoHome' VC but I additionally need to pass some of that data through an embedded Nav controller to 'NBARotoTabPager' vc.

I am trying to do this using the 'UpdateChildView' delegate (at the top of the first block of code) and calling its method in 'loadViewData() (in the 'if statement' near the bottom of the first block).

protocol UpdateChildView : class {
 func updateView()
 func test()
 var playerSelected: Player? { get set }
}



class RotoViewRoundCell: UITableViewCell{

@IBOutlet weak var categoryLabel: UILabel!

}

class RotoViewRoundHeader: UITableViewCell{

}


class NBARotoHome: UIViewController{


@IBOutlet weak var posPaidLabel: UILabel!
@IBOutlet weak var progressIndicator: UIProgressView!
@IBOutlet weak var vsLabel: UILabel!
@IBOutlet weak var fantasyFundsAmountLabel: UILabel!
@IBOutlet weak var fantasyFundsLabel: UILabel!
@IBOutlet weak var playerName: UILabel!
@IBOutlet weak var roundIndicator: UILabel!


var selectedPlayer: Player!

var firstNavController: UINavigationController!

weak var updateChildView : UpdateChildView?

override func viewDidLoad() {
    super.viewDidLoad()

    loadViewData()

    firstNavController = self.navigationController



    let rightBarButton = UIBarButtonItem(title: "Select", style: UIBarButtonItemStyle.plain, target: self, action: #selector(myRightSideBarButtonItemTapped(_:)))
    self.navigationItem.rightBarButtonItem = rightBarButton
    self.title = "NBA Roto"


}


func myRightSideBarButtonItemTapped(_ sender:UIBarButtonItem!){

    performSegue(withIdentifier: "ShowDraft", sender: nil)
}


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "ShowDraft" {
        let navVC = segue.destination as? UINavigationController
        let nbaDraftList = navVC?.viewControllers.first as! NBADraftList
        nbaDraftList.mainNavController = firstNavController
    }


    if (segue.identifier == "buyNavControllerChild"){

//            let navVC = segue.destination as? UINavigationController
//            let buyVC = navVC?.viewControllers.first as! NBARotoTabPager
//            buyVC.delegate = self

    }


}


override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)


}

@IBAction func prepareForUnwind(segue: UIStoryboardSegue) {



}

func loadViewData(){

    if((selectedPlayer) != nil){
        roundIndicator.text = "Upcoming: " + selectedPlayer.game_time
        playerName.text = selectedPlayer.Name
        vsLabel.text = selectedPlayer.visiting + " @ " + selectedPlayer.home
        fantasyFundsLabel.text = ""
        fantasyFundsAmountLabel.text = ""

          updateChildView?.test()
     //   updateChildView?.playerSelected = selectedPlayer
    //    updateChildView?.updateView()

    }else{
        roundIndicator.text = "Select a Player"
        playerName.text = "No Player Selected"
        vsLabel.text = "--"
        fantasyFundsLabel.text = "Fantasy Funds"
        fantasyFundsAmountLabel.text = "$10,000"

    }

}

}

Because I haven't been able to get the delegate to work, I have been playing around with setting its delegate property in the above 'prepare' method -'buyVC.delegate = self' - but I'm getting 'buyVC has no member delegate' so that has been a dead end.

The next bit of code is the NBARotoTabPager vc which is embedded in the navigation controller. For reasons I'm no longer sure about I decided to make it a subclass of NBARotoHome, but its basically a custom tab pager that uses a segmented control to switch between two additional vcs.

The most important step at this point is just getting the 'test' function to work (which just prints 'test'. Its implemented in the below block of code second from the bottom above updateView).

class NBARotoTabPager: NBARotoHome, UpdateChildView{

@IBOutlet weak var segmentedControl: UISegmentedControl!
@IBOutlet weak var scoreKey: UIBarButtonItem!
@IBOutlet weak var standings: UIBarButtonItem!

 var playerSelected: Player?


override func viewDidLoad() {



    navigationController?.navigationBar.barTintColor = UIColor(red: 27/255, green: 27/255, blue: 27/255, alpha: 1)


    scoreKey.setTitleTextAttributes([NSFontAttributeName: UIFont(name: "Helvetica", size: 13.0)!], for: UIControlState.normal)
    scoreKey.tintColor = UIColor.blue

    standings.setTitleTextAttributes([NSFontAttributeName: UIFont(name: "Helvetica", size: 13.0)!], for: UIControlState.normal)
    standings.tintColor = UIColor.blue

    setupView()
}

private func setupView() {
    setupSegmentedControl()
    updateView()
}

private func setupSegmentedControl() {
    // Configure Segmented Control
    segmentedControl.removeAllSegments()
    segmentedControl.insertSegment(withTitle: "Live", at: 0, animated: false)
    segmentedControl.insertSegment(withTitle: "Avg / +", at: 1, animated: false)
    segmentedControl.addTarget(self, action: #selector(selectionDidChange(_:)), for: .valueChanged)
    segmentedControl.selectedSegmentIndex = 0
}

func selectionDidChange(_ sender: UISegmentedControl) {
    updateView()
}


private lazy var viewLiveTab: NBARotoLive = {
    // Load Storyboard
    let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

    // Instantiate View Controller
    var viewController = storyboard.instantiateViewController(withIdentifier: "NBARotoLive") as! NBARotoLive


    if((self.playerSelected) != nil){
        viewController.selectedPlayer = self.playerSelected
    }


    // Add View Controller as Child View Controller
    self.add(asChildViewController: viewController)

    return viewController
}()

private lazy var viewAvgsTab: NBARotoAvgs = {
    // Load Storyboard
    let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

    // Instantiate View Controller
    var viewController = storyboard.instantiateViewController(withIdentifier: "NBARotoAvgs") as! NBARotoAvgs

    if((self.playerSelected) != nil){
        viewController.selectedPlayer = self.playerSelected
    }


    // Add View Controller as Child View Controller
    self.add(asChildViewController: viewController)

    return viewController
}()

private func add(asChildViewController viewController: UIViewController) {
    // Add Child View Controller
    addChildViewController(viewController)

    // Add Child View as Subview
    view.addSubview(viewController.view)

    // Configure Child View
    viewController.view.frame = view.bounds
    viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

    // Notify Child View Controller
    viewController.didMove(toParentViewController: self)
}

private func remove(asChildViewController viewController: UIViewController) {
    // Notify Child View Controller
    viewController.willMove(toParentViewController: nil)

    // Remove Child View From Superview
    viewController.view.removeFromSuperview()

    // Notify Child View Controller
    viewController.removeFromParentViewController()
}

internal func test(){
    print("test")
}

internal func updateView() {

    if segmentedControl.selectedSegmentIndex == 0 {



        let position = viewAvgsTab.tableView.contentOffset.y;
        viewLiveTab.tableView.contentOffset = CGPoint(x:0, y:position);


        remove(asChildViewController: viewAvgsTab)
        add(asChildViewController: viewLiveTab)

    } else {

        let position = viewLiveTab.tableView.contentOffset.y;
        viewAvgsTab.tableView.contentOffset = CGPoint(x:0, y:position);

        remove(asChildViewController: viewLiveTab)
        add(asChildViewController: viewAvgsTab)
    }
}

}

I've looked at a lot of examples but I don't understand the whole 'setting the delegate' thing i.e. theSecondViewController.delegate = self. Sometimes I see examples where you don't need to do this. And other times it seems like my VCs don't even have a delegate property. So I'm not sure if that's my specific problem or not but any direction would be greatly appreciated. Thanks

Upvotes: 2

Views: 2784

Answers (1)

the_legend_27
the_legend_27

Reputation: 571

There are three steps to implement a delegate.

  1. create a protocol.. (you've already done this by creating a updateChildView protocol)
  2. you need to implement this protocol in the class you wish to receive and process this data.. (you've not done this step and thats why you cant set buyVC.delegate = self)
  3. you need to add a property in ViewController2 called "delegate" and make it as a type of your protocol in step 1 (you've not done this step and there is no property called "delegate" in vc2 .. that's why you get this error 'buyVC has no member delegate')

Here's a quick example:

Protocol:

protocol UpdateChildView{ //removed :class
  func updateView()
  func test()
  var playerSelected: Player? { get set }
}

Viewcontroller A:

class NBARotoHome: UIViewController, UpdateChildView { //added conformance to the protocol

  //add the methods for conforming to protocol and add your implementation

   func updateView() {
        //add your implementation
   }
   func test(){
        //add your implementation
   }
   var playerSelected: Player? {
        //add your implementation
   }

  prepare(for: Segue) {
    /** code for passing data **/
    let navVC = segue.destination as? UINavigationController
    let buyVC = navVC?.viewControllers.first as! NBARotoTabPager
    buyVC.delegate = self
            //sets the delegate in the new viewcontroller 
            //remember.. delegate is a property in the next vc
           // and the property only accepts any viewcontroller that is implements updatechildview protocol
    present(vc2)
    }
  } 

viewcontroller2 :

class viewControllerB: UIViewController {

  var delegate: UpdateChildView? //add this

  viewdidload {
      delegate?.test() //call the func in the previous vc
  }
}

Upvotes: 3

Related Questions