Swift: Prepare for segue not passing data - swift3

I have got a collection view inside a table view (Netflix App Style). I followed this tutorial: https://www.thorntech.com/2016/01/scrolling-in-two-directions-like-netflix-part-2-making-api-calls/
Now I want to implement a function that whenever I click on a collection view, it will pass the employee's name to the next view controller.
Here is my code:
import UIKit
class CategoryRow: UITableViewCell, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
var jobpost:JobPosition?
var employee: Employee?
var myname = ""
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return jobpost!.Employees.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for: indexPath) as! EmployeeCell
cell.employeephoto.image = UIImage(named: "spanner")
cell.employeename.text = jobpost?.Employees[indexPath.row].name
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let myname = jobpost?.Employees[indexPath.row].name
//print(myname)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let EmployeeDetailPage = segue.destination as? EmployeeDetailViewController
EmployeeDetailPage?.employeename = myname
print(myname)
}
Now my problem is, the whole PrepareForSegue function didn't actually run. The app runs normally without any error but the data just didn't pass through? (i.e. the print(myname) didn't actually print anything).
Is there something I missed out? Very much appreciated.

Step 1
Create a global variable in your CategoryRow
var myname: String!
Step 2
Change the didSelectItemAt func like below
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
myname = jobpost?.Employees[indexPath.row].name
performSegue(withIdentifier: "EmployeeDetailViewController", sender: myname)
}
Step 3
Create a variable in EmployeeDetailViewController
var employeename: String!
Step 4
Give a identifier to your segue. Here I give "EmployeeDetailViewController"
Then change the prepareForSegue func like below
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EmployeeDetailViewController" {
if let EmployeeDetailVC = segue.destination as? EmployeeDetailViewController {
EmployeeDetailVC.employeename = myname
}
}
}

It looks like your problem is that you are setting a local let constant called myname in your collectionView(_:didSelectItemAt:) method instead of setting your class's myname property.
let myname = jobpost?.Employees[indexPath.row].name
should be:
myname = jobpost?.Employees[indexPath.row].name

Related

How to present view controller from UICollectiveViewCell Class - SWIFT

I want to know how I can present a new view controller from "didSelectItemAt - Method "
Please note that UIViewController is not a subclass so I don't have access to the navigationController to present the view controller.
class FeedCell: UICollectionViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
cv.alwaysBounceVertical = true // Making the collection view scrollable vertically
cv.dataSource = self
cv.delegate = self
return cv
}()
override func setupViews(){
super.setupViews()
addSubview(collectionView)
addConstraintWithFormat(format: "H:|[v0]|", views: collectionView)
addConstraintWithFormat(format: "V:|[v0]|", views: collectionView)
collectionView.contentInset = UIEdgeInsetsMake(0, 0, 50, 0)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
You have to implement custom delegate for this. When user select item from collection view, you have to call parent view method using delegate.
Here is reference for this : Access a UICollectionView's parent UIViewController
First give a storyboard id for the viewcontroller then
let destination = UIStoryboard(name: "Storyboard",bundle:nil).instantiateViewController(withIdentifier: "your_viewcontroller_id")
present(destination, animated: false, completion: nil)

Collectionview in tableview cell

I have taken collection view in tableview cell now when I click the collection view cell it will go to the next view controller,is it possible? I have tried did select item method in that how to use perform segue method
I am showing u example
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return false
}
}
This ViewController contain Tableview datasource n delegates. Its cell of type DemoTableViewCell showing below
class DemoTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {
var parentVC: UIViewController?
#IBOutlet weak var collectionView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
let demoCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "DemoCollectionViewCell", for: indexPath) as! DemoCollectionViewCell
return demoCollectionViewCell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
(self.parentVC as! ViewController).performSegue(withIdentifier: "YourSegueName", sender: self)
}
}
Again DemoTableViewCell contain datasouce and delegate of collection view as showing above
Also didSelectItemAt contains performSegue, which is using parent refrence for Segue.
Now in collection view cell, there is 1 variable named parentVC, U have to set this value in cellForRowatIndexpath defined in ViewController class like that
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let demoTableViewCell = tableView.dequeueReusableCell(withIdentifier: "DemoTableViewCell", for: indexPath) as! DemoTableViewCell
demoTableViewCell.parentVC = self
demoTableViewCell.collectionView.reloadData()
return demoTableViewCell
}
and final make a segue from collectionViewCell on storyboard to YourController

Trying to update UI elements when switching focus between collectionview cells

I hope someone can help me with this problem. Below is my tvOS app screenshot:
(source: mtiaz.com)
.
I am trying to do two things:
When the user scrolls down on their remote, the second item in the collectionView becomes automatically focused. I need to have it so that the first item is focused instead.
When the user manually shifts focus between items in the collectionView, I want the UI elements in the mid-section of the screen to get updated instead of having to press or select them individually.
Here is my code so far for this screen:
import UIKit
// Global variable
internal let g_CR1 = "channel_cell_01"
class DashboardVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
// outlets
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var lblChannelName: UILabel!
#IBOutlet weak var imgChannelLogo: UIImageView!
#IBOutlet weak var txtChannelDescription: UITextView!
// variables
var staticData: [Channel] = []
override func viewDidLoad() {
super.viewDidLoad()
// sample channels
initializeSampleData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - CollectionView Callbacks
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return staticData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// Get or make a cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: g_CR1, for: indexPath) as! CollectionViewCell
// Configure
cell.imgView.backgroundColor = UIColor.black
cell.imgView.image = staticData[indexPath.row].chLogo
// Return.
return cell
}
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let currentChannel = staticData[indexPath.row]
DispatchQueue.main.async {
self.lblChannelName.text = currentChannel.chName
self.imgChannelLogo.image = currentChannel.chLogo
self.txtChannelDescription.text = currentChannel.chDescription
}
}
}
I was able to answer point #2. Hopefully someone may benefit from this. However I am still stomped at #1.
func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
// test
print("Previous Focused Path: \(context.previouslyFocusedIndexPath)")
print("Next Focused Path: \(context.nextFocusedIndexPath)")
let currentChannel = staticData[(context.nextFocusedIndexPath?[1])!]
DispatchQueue.main.async {
self.lblChannelName.text = currentChannel.chName
self.imgChannelLogo.image = currentChannel.chLogo
self.txtChannelDescription.text = currentChannel.chDescription
}
}

Issue with "didSelectRowAt indexPath" in Swift 3

I'm pretty new to swift but I've managed to follow along for the most part. However, there's an issue with my code that the code itself can't even identity apparently(I'm not receiving any error signs). I'm trying to click on a row from the table view to make it go to the following page but I don't think the code is recognizing the didselectrow method. Maybe one of you more experienced persons can help me out.
Here's the code:
import UIKit
import Foundation
class OnePercentersViewController: UIViewController, UITableViewDataSource
{
var copiedExecutiveArray : [String] = NSArray() as! [String]
var identities : [String] = NSArray() as! [String]
override func viewDidLoad()
{
super.viewDidLoad()
let copyExecutiveNames : ExecutiveArray = ExecutiveArray()
copiedExecutiveArray = copyExecutiveNames.executiveNames
identities = copyExecutiveNames.executiveNames
}
//how many sections in table
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
//returns int (how many rows)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return copiedExecutiveArray.count
}
//contents of each cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")//UITableViewCell()
let personName = copiedExecutiveArray[indexPath.row]
cell?.textLabel?.text = personName
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let execName = identities[indexPath.row]
let viewController = storyboard?.instantiateViewController(withIdentifier: execName)
self.navigationController?.pushViewController(viewController!, animated: true)
print("button clicked")
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
}
}
I haven't checked other parts of your code, but to make tableView(_:didSelectRowAt:) work, your ViewController needs to conform to UITableViewDelegate:
class OnePercentersViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
Also you need to connect the delegate of the UITableView to your ViewController in your storyboard. Or you can do it in code, if you have an #IBOutlet to the UITableView. Anyway, not only dataSource, but also delegate.
And this is not a critical issue, but you can write the first two lines of the class as:
var copiedExecutiveArray : [String] = []
var identities : [String] = []
After spending a day and a half watching Youtube videos and reading comments to similar problems, tt took trial and error but I finally got it thanks to you guys!It finally clicked when I read that I had to connect the delegate to the ViewController through the storyboard, so thanks for that!
Here's the final code for those with similar issue:
import UIKit
import Foundation
class OnePercentersViewController: UIViewController,UITableViewDataSource, UITableViewDelegate
{
var copiedExecutiveArray : [String] = []
var identities : [String] = []
override func viewDidLoad()
{
super.viewDidLoad()
let copyExecutiveNames : ExecutiveArray = ExecutiveArray()
copiedExecutiveArray = copyExecutiveNames.executiveNames
identities = copyExecutiveNames.executiveNames
}
//how many sections in table
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
//returns int (how many rows)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return copiedExecutiveArray.count
}
//contents of each cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let personName = copiedExecutiveArray[indexPath.row]
cell.textLabel?.text = personName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let execName = identities[indexPath.row]
let viewController = storyboard?.instantiateViewController(withIdentifier: execName)
self.navigationController?.pushViewController(viewController!, animated: true)
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
}
}

"Missing argument for parameter 'for' in call"

Whilst coding in swift3 I wanted to use custom protocols and generics for reusing collection view cells. I know that this is the standard way of reusing cells:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TacoCell", for: indexPath) as? TacoCell {
cell.configureCell(taco: ds.tacoArray[indexPath.row])
return cell
}
return UICollectionViewCell()
}
But everytime I try to do this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(forIndexPath: indexPath) as TacoCell
cell.configureCell(taco: ds.tacoArray[indexPath.row])
return cell
}
The compiler complains I have a "Missing argument for parameter 'for' in call"... The parameter in this case is "forIndexPath"
FYI...
I have custom extensions for reusing cells and loading nibs. Code is as follows:
ReusableView class
import UIKit
protocol ReusableView: class { }
extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String.init(describing: self)
}
}
NibLoadableView class
import UIKit
protocol NibLoadableView: class { }
extension NibLoadableView where Self: UIView {
static var nibName: String {
return String.init(describing: self)
}
}
This is my extension for the UICollectionView
import UIKit
extension UICollectionView {
func register<T: UICollectionViewCell>(_: T.Type) where T: ReusableView, T: NibLoadableView {
let nib = UINib(nibName: T.nibName, bundle: nil)
register(nib, forCellWithReuseIdentifier: T.reuseIdentifier)
}
func dequeueReusableCell<T: UICollectionViewCell>(forIndexPath indexPath: NSIndexPath) -> T where T: ReusableView {
guard let cell = dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath as IndexPath) as? T else {
fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)")
}
return cell
}
}
extension UICollectionViewCell: ReusableView { }
The problem is that you've got a little bit of Swift 2.2 code alongside your Swift 3.0 code, and the compiler is getting confused when it tries to pick a method to call because nothing quite matches.
Your cellForItemAt method calls your own dequeueReusableCell() extension method with the IndexPath from the collection view. However, the extension method you wrote expects to receive an NSIndexPath, which is a subtly different thing.
Amend your extension method to this and the problem should clear up:
func dequeueReusableCell<T: UICollectionViewCell>(forIndexPath indexPath: IndexPath) -> T where T: ReusableView {