Trying to update UI elements when switching focus between collectionview cells - swift3

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
}
}

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

Swift: Prepare for segue not passing data

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

Permanent storage To-Do App Swift

I've got a simple to do list app, and I would like the items in the list to appear after the app is closed. What I have so far does not do it, not sure if everything is in the right place?
Code in my first ViewController:
var list = [String]()
class FirstViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var myTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = list[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
list.remove(at: indexPath.row)
myTableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
myTableView.reloadData()
if let x = UserDefaults.standard.object(forKey: "cell") as? [String]! {
list = x
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Code in my second ViewController:
class SecondViewController: UIViewController {
#IBOutlet weak var input: UITextField!
#IBAction func addItemButton(_ sender: AnyObject) {
list.append(input.text!)
input.text = ""
UserDefaults.standard.set(input.text, forKey: "cell")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You are reloading the tableview at viewDidAppear before getting the data. Get your data from user defaults first (set list) and then reload the table view.
Hint:-
override func viewDidAppear(_ animated: Bool) {
if let x = UserDefaults.standard.object(forKey: "cell") as? [String]! {
list = x
myTableView.reloadData()
}
}

Swift 3 tableViewCell Button, strange behaviour, submit one button and others of the same Cell change as well?

Repeated on two projects and searched in vain so I hope someone can help...
Created a basic TableViewController and one TableViewCell. Run and press the first button, scroll down and other random buttons also show the same change?
TableViewController
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 99
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ButtonCell", for: indexPath) as! ButtonCell
cell.rowLabel.text = "\(indexPath.row)"
cell.tapAction = { (cell) in
self.showAlertForRow(tableView.indexPath(for: cell)!.row)
}
return cell
}
// MARK: - Extracted method
func showAlertForRow(_ row: Int) {
let alert = UIAlertController(
title: "BEHOLD",
message: "Cell at row \(row) was tapped!",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Gotcha!", style: UIAlertActionStyle.default, handler: { (test) -> Void in
self.dismiss(animated: true, completion: nil)
}))
self.present(
alert,
animated: true,
completion: nil)
}
}
Table View Cell
// ButtonCell.swift
import UIKit
class ButtonCell: UITableViewCell {
var tapAction: ((UITableViewCell) -> Void)?
#IBOutlet weak var rowLabel: UILabel!
#IBOutlet weak var button: UIButton!
#IBAction func buttonTap(_ sender: AnyObject) {
button.setTitle("Correct", for: .normal)
tapAction?(self)
}
}
Lets begin with the basics. You don't need this:
cell.tapAction = { (cell) in
self.showAlertForRow(tableView.indexPath(for: cell)!.row)
}
You better use the apple framework:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
showAlertForRow(indexPath.row)
}
Second create a method in you cell like
import UIKit
class ButtonCell: UITableViewCell {
var tapAction: ((UITableViewCell) -> Void)?
#IBOutlet weak var rowLabel: UILabel!
#IBOutlet weak var button: UIButton!
#IBAction func buttonTap(_ sender: AnyObject) {
button.setTitle("Correct", for: .normal)
tapAction?(self)
}
func createCell() {
button.setTitle("Your button title", for: normal)
}
}
and call the createCell method in your
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.createCell()
...
}
Hope I could help but try to start with the basics here: https://developer.apple.com/reference/uikit/uitableviewdelegate
This is happening because you are dequeuing your UITableViewCells. In order to solve the problem you should use the prepareForReuse() method in your ButtonCell. You can save the states of all your buttons and when a cell is being dequeued you can just set the title of the button in this cell in the prepareForReuse() method.