I want my collectionView that's housed in AllWorkoutsVC to pass videoCode string data to the next viewController that's named VideoViewVC.
My collection view presents data correctly, however is the didSelectItemAt func i am having trouble with... this code runs fine and no warnings or errors are thrown, just that the variable myPassVideoCode not being passed to the target view controller
(i have removed some code for clarity..please ask for more if you feel is needed.)
collectionView class
class AllWorkoutsVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var myPassVideoCode = String()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WorkoutsCell", for: indexPath) as? WorkoutsCell {
let workout = workouts[indexPath.row]
cell.updateViews(workout: workout)
print("this is my workouts: " + workout.videoCode)
return cell
} else {
return WorkoutsCell()
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let chosenWorkout = DataService.instance.getAllWorkouts()[indexPath.row]
myPassVideoCode = String(chosenWorkout.videoCode)
print("this is my: " + myPassVideoCode)//Note: this whole print does not appear in the console either
performSegue(withIdentifier: "onPlayPressed2", sender: chosenWorkout)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "onPlayPressed2" {
let toNextVC = segue.destination as! VideoViewVC
toNextVC.myPassVideoCode = myPassVideoCode
}
}
}
Target Class...
class VideoViewVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
var myPassVideoCode = String()
#IBOutlet var videoPlayer: YouTubePlayerView!
override func viewDidLoad() {
super.viewDidLoad()
videoPlayer.loadVideoID(myPassVideoCode)
}
The issue is that you're declaring a new local constant with the same name myPassVideoCode in:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt...
You only need to set myPassVideoCode. So change this line:
let myPassVideoCode = String(chosenWorkout.videoCode)
to:
myPassVideoCode = String(chosenWorkout.videoCode)
You are making a new constant inside didSelectItemAt:
let myPassVideoCode = ...
So you are not storing the value to the variable myPassVideoCode that you declared in the beginning of the class definition.
Related
This question already has answers here:
passing tapped cell data to another view via segue in Swift
(2 answers)
Pass data through segue
(3 answers)
Closed 8 months ago.
Hi dear professionals.
I have main ViewController, where I put Three horizontal CollectionView with cells into (but I hope at least solve problem with 1 of these).
One of this named - FirstPlaylistCollectionView
Cells also custom - FirstPlaylistCollectionViewCell
On tap on cell with specific video it needed pass Video object to the Player (PlayerViewController).
I cant figure it out how, in my case, make this Segue (pass Video object with necessary data) from CollectionView by code !
I almost don't use Storyboard in this project.
Maybe with help of Delegate, but I'm also couldn't understand how to use them for my case.
Method didSelectItemAt - works and get Video object, but i don't understand how to pass it correctly.
Will be very grateful for answer. I couldn't apply for now any solution from Stack, help please.
FirstPlaylistCollectionView code
import UIKit
protocol FirstPlaylistCollectionViewDelegate: AnyObject {
func playVideo()
}
class FirstPlaylistCollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource, ModelDelegate {
var playlistsModel = PlaylistsModel()
private var firstPlaylist: [Video] = []
weak var delegate2: FirstPlaylistCollectionViewDelegate?
// MARK: - Data Source
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return firstPlaylist.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dequeueReusableCell(withReuseIdentifier: FirstPlaylistCollectionViewCell.reuseId, for: indexPath) as! FirstPlaylistCollectionViewCell
let video = self.firstPlaylist[indexPath.row]
cell.setCell(video)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.delegate2?.playVideo()
print("selected video \(firstPlaylist[indexPath.row]) with \(collectionView)! DONE!")
}
FirstPlaylistCollectionViewCell code
class FirstPlaylistCollectionViewCell: UICollectionViewCell {
static let reuseId = "FirstPlaylistCollectionViewCell"
var video: Video?
PlayerViewController code
import UIKit
import WebKit
class PlayerViewController: UIViewController {
#IBOutlet weak var handleArea: UIView!
#IBOutlet weak var openCloseArrow: UIImageView!
var video: Video?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("I'm here!!!")
let vc2 = segue.destination as! PlayerViewController
if let cell = sender as? Video {
self.video = cell
vc2.titleOfVideoLabel.text = video?.title
}
}
}
extension PlayerViewController: FirstPlaylistCollectionViewDelegate {
func playVideo() {
performSegue(withIdentifier: "homeToPlayer", sender: self)
}
}
Answering this by assuming some of the things, I hope you want to navigate to PlayerViewController from ViewController through a segue. Keeping that in my mind, I have assumed your FirstPlaylistCollectionView is in your ViewController class as mentioned below.
class ViewController: UIViewController {
var firstPlaylistCollectionView: FirstPlaylistCollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// First try to get notified from your collection list to here
// and then from here to your player
firstPlaylistCollectionView.listDelegate = self
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
if let id = segue.identifier, id == "playerSegue",
let lVideo = sender as? Video,
let destination = segue.destination as? PlayerViewController{
destination.video = lVideo
}
}
}
extension ViewController: FirstPlaylistCollectionViewDelegate {
func firstPlaylistCollectionView(_ listView: FirstPlaylistCollectionView, didSlect video: Video) {
self.performSegue(withIdentifier: "playerSegue", sender: video)
}
}
And below is the update for the collection view
class FirstPlaylistCollectionView: UICollectionView {
var playlistsModel = PlaylistsModel()
private var firstPlaylist: [Video] = []
weak var listDelegate: FirstPlaylistCollectionViewDelegate?
}
extension FirstPlaylistCollectionView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return firstPlaylist.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dequeueReusableCell(withReuseIdentifier: FirstPlaylistCollectionViewCell.reuseId, for: indexPath) as! FirstPlaylistCollectionViewCell
/* Here it goes your cell configuration
.
.
*/
return cell
}
}
extension FirstPlaylistCollectionView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
listDelegate?.firstPlaylistCollectionView(self, didSlect: firstPlaylist[indexPath.row])
}
}
And finally verify that the playerViewController has received the data or not
class PlayerViewController: UIViewController {
#IBOutlet weak var handleArea: UIView!
#IBOutlet weak var openCloseArrow: UIImageView!
var video: Video?
override func viewDidLoad() {
super.viewDidLoad()
print("Video object from player vc :: \(video)")
}
}
Added protocol is
protocol FirstPlaylistCollectionViewDelegate: AnyObject {
func firstPlaylistCollectionView(_ listView: FirstPlaylistCollectionView, didSlect video: Video) ->Void
}
you can use Prepare for segue or Did Select Row method try these out.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedProgram = programy[indexPath.row]
let destinationVC = PlayerTableViewController()
destinationVC.programVar = selectedProgram
destinationVC.performSegueWithIdentifier("playerSegue", sender: self)
}
Trying to pass a value selected in a collectionView into a new View Controller to determine which local json file to decode. Get the error "Cannot use instance member 'selectedCategory' within property initializer; property initializers run before 'self' is available"
Initial VC:
extension MenuViewController: UICollectionViewDataSource {
...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedVC = storyboard?.instantiateViewController(withIdentifier: "SelectImageViewController") as! SelectImageViewController
selectedVC.selectedCategory = categories[indexPath.row].category
navigationController?.pushViewController(selectedVC, animated: true)
}
}
Second VC:
class SelectImageViewController: UIViewController {
lazy var selectedCategory: String = ""
var selectedArray = Bundle.main.decode([Images].self, from: "\(selectedCategory).json")
override func viewDidLoad() {
super.viewDidLoad()
...
}
I tried the lazy var and init methods suggested in other posts with this error, but no luck.
You cannot run code which refers to self outside of a method (unless it's declared as lazy)
It's simpler to decode the array before presenting the controller
extension MenuViewController: UICollectionViewDataSource {
...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedVC = storyboard?.instantiateViewController(withIdentifier: "SelectImageViewController") as! SelectImageViewController
let selectedCategory = categories[indexPath.row].category
selectedVC.selectedArray = Bundle.main.decode([Images].self, from: "\(selectedCategory).json")
navigationController?.pushViewController(selectedVC, animated: true)
}
}
...
class SelectImageViewController: UIViewController {
var selectedArray = [Images]()
override func viewDidLoad() {
super.viewDidLoad()
...
}
Or if you really want to decode the array in SelectImageViewController
extension MenuViewController: UICollectionViewDataSource {
...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedVC = storyboard?.instantiateViewController(withIdentifier: "SelectImageViewController") as! SelectImageViewController
selectedVC.selectedCategory = categories[indexPath.row].category
navigationController?.pushViewController(selectedVC, animated: true)
}
}
...
class SelectImageViewController: UIViewController {
var selectedCategory = ""
var selectedArray = [Images]()
override func viewDidLoad() {
super.viewDidLoad()
selectedArray = Bundle.main.decode([Images].self, from: "\(selectedCategory).json")
...
}
I have a tableView, with a prototype cell containing a UICollectionView. I’ve setup the tableView according to this tutorial (https://medium.com/#stasost/ios-how-to-build-a-table-view-with-multiple-cell-types-2df91a206429), and the UI is working. I can pass data through my tableView and into the collectionView.
View Layout
When a collectionViewCell is selected it segues to another view.
I haven’t figured out how to access the data from the collectionViewCell and pass it to the new view.
The collectionView is initialized within the tableView prototype cell. I've tried didSelectRow -> prepareForSegue (code below), but the commands do not autocomplete, and are not working.
Here's the code for the tableViewCell, where the collectionView is setup.
EDIT: Removed commented code for clarity
import UIKit
class homeFeedTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var feedCollectionView: UICollectionView!
var selectedEvent : Event?
var collectionItems = [CollectionViewModelItem]()
var collectionItem : CollectionViewModelItem?
#IBOutlet weak var sectionHeadingLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
feedCollectionView.delegate = self
feedCollectionView.dataSource = self
print("collection items \(collectionItems.count)")
for item in collectionItems{print("type: \(item.type), title: \(item.eventTitle)")}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
// setup view model
var item: TableViewModelItem? {
didSet {
// if not right class, skip
guard let item = item as? TableViewModelFeed else {
return
}
sectionHeadingLabel.text = item.sectionTitle
}
}
// create reuse identifier property
static var identifier: String {
return String(describing: self)
}
}
import Foundation
import UIKit
extension homeFeedTableViewCell {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// print("dataCount3: \(collectionItems.count) \(collectionItems[collectionItems.count-1].type)")
return collectionItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// let cell = UICollectionViewCell()
// return cell
self.collectionItem = collectionItems[indexPath.row]
switch collectionItem!.type {
case .yourEvents:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier:YourEventsCollectionViewCell.identifier, for: indexPath) as? YourEventsCollectionViewCell{
cell.item = collectionItem
print(cell.item?.type)
print(".yourEvents")
return cell
}
case .feed:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: mainFeedCollectionViewCell.identifier, for: indexPath) as? mainFeedCollectionViewCell{
cell.item = collectionItem
print(".feed")
return cell
}
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("\(collectionItems[indexPath.row].eventTitle) tapped")
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourEventsToEventViewController" || segue.identifier == "feedToEventViewController"{
print("prepare for segue1")
let destinationVC = segue.destination as! EventViewController
if collectionItem != nil{
print("prepare for segue2")
destinationVC.backgroundImageUrl = collectionItem!.backgroundImageUrl
}
}
}
}
}
A UICollectionView keeps track of its selected indexPaths with the property indexPathsForSelectedItems. Since you trigger your segue in collectionView(didSelectItem: atIndexPath:), your selected indexPath is available during prepare(forSegue:). You could try the following:
class MyViewController: UIViewController, UICollectionViewDelegate {
...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "mySegue", sender: self)
}
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard let destinationVC = segue.destination as! EventViewController,
segue.identifier == "mySegue" else { return }
// In this context, your selected cell is the one who fired the segue
if let selectedIndexPaths = collectionView.indexPathsForSelectedItems,
let firstSelectedIndexPath = selectedIndexPaths.first {
let selectedObject = collectionItems[firstSelectedIndexPath.row]
destinationVC?.backgroundUrl = selectedObject.backgroundUrl
}
}
}
The sequence is:
You select a cell (through user interaction, ie tapping).
didSelect performs a segue named "mySegue" (in this example).
In prepareForSegue, you look for your selected index paths. Assuming you aren't using multi-selection, you want your first and only indexPath. Using that index path, you can retrieve your data in your collectionItems array.
I've been trying for hours but I do not understand how I hope someone can help me!
In practice I have an initial VC I click on an image that takes me to a collectionView through a follow and the 2 elements are also joined by a NC.
In the CollectionView are inserted images, which are contained in an array, I would like to touch on an image to return to the initial VC and that the image displayed is the one selected in the CollectionView.
I tried with UnWind but I can not carry the information of the image index that I try to recover in the didselct.
Viewcontroller
class ViewController: UIViewController {
#IBOutlet weak var immagine: UIImageView!
#IBAction func actionTap(_ sender: UITapGestureRecognizer) {
print("tap")
performSegue(withIdentifier: "selezione", sender: nil)
}
#IBAction func indietro(segue: UIStoryboardSegue){
let recupero = segue.source as! CollectionViewController
print(recupero.indice)
immagine.image = UIImage(named: recupero.arrayImmagini[recupero.indice])
}
private let reuseIdentifier = "Cell"
...
}
CollectionViewController
class CollectionViewController: UICollectionViewController {
var arrayImmagini = ["globe","bed","home","toolbox"]
var indice = 0
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayImmagini.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell
cell.imgCell.image = UIImage(named: arrayImmagini[indexPath.row])
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// print(arrayImmagini[indexPath.row])
let indice = indexPath.row
// idImg = indexPath.row
}
...
}
even if set index = indexPath.row it is never recovered in the unwind
You have wired the unwind segue to your collection view cell. The unwind happens before didSelectItemAt runs.
You can fix this in one of two ways:
Remove the segue from the cell, and wire it from the viewController icon (top left icon) to the exit icon (top right icon). Find this exit segue in the Document Outline and give it an identifier such as "returnToMain". In didSelectItemAt, call self.performSegue(withIdentifier: "returnToMain", sender: self) after setting indice.
OR
Don't use didSelectItemAt at all. Instead, implement prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "returnToMain" {
if let indexPath = self.collectionView.indexPathsForSelectedItems?.first {
indice = indexPath.row
}
}
}
You should implement some handler for this purpose or delegate method. For example:
class ViewController: UIViewController {
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourCollectionViewController" {
if let vc = segue.destination as? CollectionViewController {
vc.imageHandler = { imageIndex in
...
}
}
}
}
}
class CollectionViewController: UICollectionViewController {
var imageHandler : ((_ imageId: Int) -> Void)?
...
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// print(arrayImmagini[indexPath.row])
let indice = indexPath.row
// idImg = indexPath.row
imageHandler?(indice)
dismiss(animated: true, completion: nil)
}
}
I have a button inside a collection view cell and when pressed I want to go to another view controller and pass a string to that view controller. The only problem I'm having is with passing the data, I don't know how to check from which cell the button was clicked.
extension UserViewController: UICollectionViewDataSource{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return self.posts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PostCell", for: indexPath) as! UsersCollectionViewCell
//cell.post = posts[indexPath.item]
cell.User_Name.text = "\(self.posts[indexPath.item].firstname!) \(self.posts[indexPath.item].lastname!)"
cell.Country.text = self.posts[indexPath.item].Country
//user id is in the posts().uid
return cell
}
//the segue is already made in the storyboard, i am trying to pass the user id in the function below
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Testing1"{
var view = segue.destination as! ViewTheAccount
//user_ID = self.posts[indexPath.item].firstname
}
}
}
Add a string variable to you cell class and give it strValue that you want in cellForRow
// btn action in cell
#IBAction func btnClicked(_ sender: Any)
{
/// access cell and get it's string var , self = cell
/// here use delegate to push another view controller with self.strValue
}
Try this (I assume here that only single selection is allowed here, plus I am assuming that the segues are started by selecting a cell):
if segue.identifier == "Testing1" {
var view = segue.destination as! ViewTheAccount
if let itemIndex = collectionView.indexPathsForSelectedItems?.first?.item {
let selectedItem = self.posts[itemIndex]
// do here what you need
}
}
So one way to do this is to send the cell in the delegate call or callback that you are using.
class SomeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var collectionView:UICollectionView!
collectionView(method that dequeues the cell){
let yourcell = collection view.dequeue(...) as! SomeCell
yourcell.somecallback = callback
}
func callback(cell: UICollectionViewCell){
//To find out which cell it is just
let indexPath = collection view.indexPathForCell(cell)
//YOU NOW know which cell this was sent from.
}
}
class SomeCell: UICollectionViewCell{
var somecallback:((UICollectionViewCell)->())?
func didPress(sender: UIButton){
somecallback(self)
}
}