0

In my app I have one screen divided between two ViewControllers - LadderViewController and GameHistoryTableViewController, which lies in a container. I want user to be able to filter the data in the table by tapping on something in the LadderView. I tried to solve this using delegates:

LadderViewController:

delegate = GameHistoryTableViewController()
func imageTapped(imageIndex: Int) {
    delegate?.selectedHeroNumber(imageIndex)
}

GameHistoryTableViewController: (conforms to the delegate protocol and implemets a function from it)

func selectedHeroNumber(heroNumber: Int) {
    let filteredGames = filterGamesFromHeroNumber(heroNumber)
    tableDataSource = filteredGames
    self.tableView.reloadData()
}

That doesn't work, though, because the delegate I declare in LadderViewConroller is another instance of GameHistoryTableViewController, not the (to the user) shown one. I don't know how to access the "visible" instance (table) of GameHistoryTableViewController though... So, how should be delegating used here? Or should I use another approach (and if so, what kind)? I basically need to change the table's data source according to on what the user taps, one can say "from outside" (dataSource is a property in my GameHistoryTableViewController class).

Eugleo
  • 418
  • 4
  • 11

2 Answers2

1

There are a few ways to achieve this, I have a similar setup for which I use a model class with a singleton to store the relevant data.

For instance you could have the following

class dataModel {

  static let sharedInstance = dataModel()

  private var _heroNumber = Int()

private init() {}

  var heroNumber: Int = {

    return _heroNumber

  }

  func setHero(hero: Int) -> Int {

    return _heroNumber

  }
 }
}

You can then can access this model from each of your controllers using dataModel.sharedInstance.heroNumber etc...

Ben Sullivan
  • 2,134
  • 1
  • 18
  • 32
  • This is the old way of making a singleton. There's a better way now: http://stackoverflow.com/a/36012158/2227743 – Eric Aya Apr 20 '16 at 14:01
  • I think I got the idea - making a "data storing" class, which I will access from different points in my program. I still won't be able to reload the table though, as I won't have the "shown" instance of the table... It seems I'll have to do it through notifications. – Eugleo Apr 20 '16 at 14:08
  • If you're familiar with NSNotifications then you can put one in the didSet of the heroNumber. This way every time it is modified you can then make a call to reload the table data. I'm not really sure of the difference Eric is referring do, perhaps he can elaborate? – Ben Sullivan Apr 20 '16 at 14:15
  • Yep, just didn't want to use them, as it seems somehow sketchy and not really elegant solution to me. I guess it's the only one, though. – Eugleo Apr 20 '16 at 14:17
  • 1
    I know what you mean! There are some good solutions, the second delegate pattern on this page might help you. https://davidnix.io/post/stop-using-nsnotificationcenter/ – Ben Sullivan Apr 20 '16 at 14:27
  • Oh man, I'll definitely read this when I find time! But according to the title, it is exactly what I need! Thanks a bunch :-) – Eugleo Apr 20 '16 at 14:32
1

Here is an example with delegation like you want to do. It's a better solution than singleton in this case ;)

declare a new protocol call HeroInfo:

protocol HeroInfo: class {
    func selectedHeroNumber(heroNumber: Int);
}

LadderViewController:

//create the delegation
weak var delegate:HeroInfo?

func imageTapped(imageIndex: Int) {
    //call the delegate method
    delegate?.selectedHeroNumber(imageIndex)
}

GameHistoryTableViewController:

 // Here get the protocol HeroInfo inheritance
class userTableViewController: UITableViewController, HeroInfo {

  override func viewDidLoad() {
      super.viewDidLoad()
      //Here get your Ladder view in a splitView
      if let split = self.splitViewController {
            let controllers = split.viewControllers
            self.ladderViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? ladderViewController
            //register it to delegate
            self.ladderViewController?.delegate = self
        }
  }
  ...
  // Here is your method of your protocol that you must conform to
  func selectedHeroNumber(heroNumber: Int) {
    let filteredGames = filterGamesFromHeroNumber(heroNumber)
    tableDataSource = filteredGames
    self.tableView.reloadData()
  }
  ...

}
  • I don't know how to get my ladderView, though... It's the same problem as with getting the shown table, just from other side. So, I'd need to get help with the _var ladderView:ladderViewController = ..._ dotdotdotty part. – Eugleo Apr 20 '16 at 14:40
  • I'd be interested to know how this would work also, perhaps he means - let ladderView = LadderViewController() – Ben Sullivan Apr 20 '16 at 14:51
  • Yeah something like that. But I must have some information from your architecture in the storyboard to know how you design that. What are you using to have two view controller in your divided screen? – Stéphane Garnier Apr 20 '16 at 14:54
  • That would cause the same problem as I had, the ladderView I'd create wouldn't be _the_ ladderView the user taps. – Eugleo Apr 20 '16 at 14:54
  • Would it be horrific practice to just have a singleton in the LadderViewController? Then ladderView can equal the singleton and away you go... – Ben Sullivan Apr 20 '16 at 14:57
  • You will use a singleton as a global var so it could be horrific in the future but in a simple code it could do the job ;). You must know that a singleton can be accessed anywhere from your application ;) – Stéphane Garnier Apr 20 '16 at 14:58
  • I just had how to get you're view in a split view from the TableViewController ;) – Stéphane Garnier Apr 20 '16 at 15:30