Apologies
You must be signed in to watch this lesson.
Handling User Selection
AppStore JSON APIs
Another rather tricky aspect of this app is to correctly capture what the user is doing within these nested UICollectionView components. In today's lesson, we'll implement a simple event that can be captured every time an app is selected. To make this process super simple, we can use NotificationCenter use post notifications from our views. Finally, our app needs to listen to these events and react appropriately by pushing a new view controller onto the screen.

Comments (7)
rmsdevios
5 years ago
Hey Brian, Maybe I already explained, but since English is not my first language, I did not quite understand it. If you can write here, I can translate rs rss I've done some different implementations in other projects, which worked well, but I'm not sure if it was the best practice, if there are imputed in memory, or another type of problem. Example: "Cell" gets the "delegate" from the "ViewController", inside the "Cell" class I use a "delegate.push ()" In this case, why the "Push" to the "AppDetailController" was not called from the "AppsHorizontalController"? Type: "AppsPageController" passes the "delegate" to "AppsHorizontalController". Push could be run from "AppsHorizontalController", or directly from "Cell", in a scenario that "AppsHorizontalController" passed the "delegate" to the "Cell" Are there any impurities in memory, or just a cleaner way to write? Taking advantage of the contact, just a boservation, you say goodbye to 12:13, but the video continues on black screen until 14:23. Thank you
rmsdevios
5 years ago
I did the tests here, in this case also not to pass the delegate on, I think it's because of the "addSubview (horizontalController.view)" Regardless of impacts, I found this form more clean, I will adopt it in the next.
Brian Voong
5 years ago
rmsdevios
5 years ago
I used to use "closures" only in the return of asynchronous functions to "API", I had never thought to use for cases like this. I liked the idea, I made a little adaptation: override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! AppsGroupCell let appGroup = groups[indexPath.item] cell.titleLabel.text = appGroup.feed.title cell.horizontalController.appGroup = appGroup cell.horizontalController.collectionView.reloadData() cell.horizontalController.didSelectHandler = self.didSelectHandler return cell } fileprivate func didSelectHandler(feedResult:FeedResult){ let controller = AppDetailController() controller.navigationItem.title = feedResult.name navigationController?.pushViewController(controller, animated: true) }
David Gonçaalves
4 years ago
Hi, direct assign "cell.horizontalController.didSelectHandler = self.didSelectHandler" will introduce a retain cycle Would suggest this, if you don't want your method cellForItemAt to be so much verbose cell.horizontalController.didSelectHandler = { [weak self] in result self?.didSelectHandler(feedResult: result){ }
hwatanabe9393
4 years ago
How come clicking on the cells on horizontal viewcontroller wont conflict with AppPageController's celldidselect and AppsHorizontalController's celldidselect? I've seen gesture recognizer conflicts each other when overlaps before...
hwatanabe9393
4 years ago
Sorry, I meant didSelectItemAt
Brian Voong
4 years ago
hwatanabe9393
4 years ago
Ah okay. thank!
Tokyojogo
4 years ago
Hi Brian, I have a question. The way I'm doing this is I separated the datasource into a different file so I have AppsPageController, AppsPageDataSource, AppsHorizontalController, & AppsHorizontalDataSource (plus the header files). For this video, I'm calling didSelectItemAt in AppsHorizontalController because the extension for the UICollectionViewDelegateFlowLayout is there. But following your example, the completion block is being handled inside AppsPageDataSource which only conforms to NSObject and UICollectionViewDataSource. I'm having an error here w/ the navigationContoller. I tried doing another completion block (which I feel is hacky) that runs on AppsPageController and did the push there but its not working either. Can you direct me on how to do it with this setup? Thank you!
Brian Voong
4 years ago
Tokyojogo
4 years ago
So in my AppsHorizontalController, that's where I have an ext for UIViewControllerDelegateFlowLayout because it didnt seem right putting this as an extension of the DataSource. So I have: var didSelectHandler: ((FeedResult) -> ())? override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if let app = dataSource.appGroup?.feed.results[indexPath.item] { didSelectHandler?(app) } } Then in my AppsPageDataSource, I have: var HandlerforNewVC: ((FeedResult) -> ())? func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! AppsGroupCell let appGroup = groups[indexPath.item] cell.titleLabel.text = appGroup.feed.title cell.horizontalController.dataSource.appGroup = appGroup cell.horizontalController.collectionView.reloadData() cell.horizontalController.didSelectHandler = { [weak self] feedResult in self?.HandlerforNewVC?(feedResult) } return cell } Then in AppsPageController, I have: let dataSource = AppsPageDataSource() override func viewDidLoad() { super.viewDidLoad() dataSource.HandlerforNewVC = { [weak self] feedResult in let controller = AppDetailController(appId: feedResult.id) controller.navigationItem.title = feedResult.name self?.navigationController?.pushViewController(controller, animated: true) } } I got it working but I don't know if this is the right way to do it. Would appreciate your advice on this. Thank you!
Brian Voong
4 years ago
donmcallister
4 years ago
I'm not following this too well because I was trying to duplicate and simply pass data from one tableview controller to another view controller and can't get it working. Is this callback closure way a good approach to do that instead of using storyboard segway option? Any other examples you have out there? thanks!
richardbranson
4 years ago
Hey Brian, Thank you for the amazing lessons. My question is "How do we know that there is a retain cycle happening in the app?".
Brian Voong
4 years ago
frankusu
4 years ago
Hey Brian didn't think you can use closures this way!! I'm still having a hard time wrapping my head around how AppsHorizontalController didSelectItemAt calls the closure in AppsController. Is the (app) passed into the closure so feedResult.name can be accessed? Thanks man!! //in appsHorizontalController override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if let app = appGroup?.feed.results[indexPath.item] { didSelectHandler?(app) } } //in apps controller cell.horizontalController.didSelectHandler = {[weak self] feedResult in let controller = AppDetailController() controller.navigationItem.title = feedResult.name self?.navigationController?.pushViewController(controller, animated: true) }
seventhaxis
3 years ago
What is the advantage to passing an app's info this way as opposed to using a protocol/delegate relationship?
HELP & SUPPORT