What is the correct way to pass data from my ViewController to my CollectionViewItem?
ViewContoller
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = CollectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CollectionViewItem"), for: indexPath) as! CollectionViewItem
item.themeName = getDirectoryContentByIndex(index: indexPath)
return item
}
CollectionViewItem
class CollectionViewItem: NSCollectionViewItem {
var themeName: String!
#IBOutlet weak var NameOfTheme: NSTextFieldCell!
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
}
Edit:
I want to assign the themeNameas stringValue of NameOfTheme. But if I set the value, the IBOutlet is nil.
I recommend you to make fields of your cell private and make configure(YOUR_PARAMS) (in our case configure(themeName: String). So in cellForRow you will call this function cell.configure(themeName: "")
Related
I am using collectionview inside tableview cell. so when collectionview cell button is clicked then present viewcontroller i am using protocol..
code for tableviewcell and delegate:
protocol CustomCellDelegate: class {
func sharePressed(cell: ProposalTableVIewCell)
}
class ProposalTableVIewCell: UITableViewCell, UICollectionViewDelegate,UICollectionViewDataSource {
#IBOutlet weak var attetchmentsCollectionview: UICollectionView!
var delegate: CustomCellDelegate?
public var bidAttatchment: Array<Get_attachments>?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return bidAttatchment?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AttatchmentCollectionViewCell", for: indexPath) as! AttatchmentCollectionViewCell
let attatchBid = bidAttatchment?[indexPath.item]
cell.attatchmentLbl.text = attatchBid?.filename
cell.openBtn.tag = indexPath.item
cell.openBtn.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
return cell
}
#objc func connected(sender: UIButton){
delegate?.sharePressed(cell: self)
}
code for viewcontroller: when i press sharePressed getting only collectionview's first cell value.. how to get all cells value.. please do let me know
class ViewMyAppliedReqVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, CustomCellDelegate{
func sharePressed(cell: ProposalTableVIewCell) {
guard let index = tableView.indexPath(for: cell)?.row else { return }
let name = getBitDetails?.result?.bid?.get_attachments?[index].filename// always getting only first cell value
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewProposalTableVIewCell1", for: indexPath) as! ViewProposalTableVIewCell1
cell.bidAttatchment = getBitDetails?.result?.bid?.get_attachments
cell.delegate = self
cell.attetchmentsCollectionview.reloadData()
return cell
}
You are always getting same index because you are taking out index of "ProposalTableVIewCell" and this is tableView cell. And collectionView cells are in the same tableView cell.
Solution:
Take another parameter in protocol function like below for storing index of collection cell
protocol CustomCellDelegate: class {
func sharePressed(cell: ProposalTableVIewCell,collectionCellIndex:Int)
}
func sharePressed(cell: ProposalTableVIewCell, collectionCellIndex:Int) {
guard let index = tableView.indexPath(for: cell)?.row else { return }
let name = getBitDetails?.result?.bid?.get_attachments?[collectionCellIndex].filename// This will return index of collection cell
}
#objc func connected(sender: UIButton){
delegate?.sharePressed(cell: self,collectionCellIndex: sender.tag )
}
I've created an collection view with carName's there are 5 of them, after clicking for example Mercedes(one of the collection view's cell) I want to set label text its own values:carModel ( carName and carModel are both same struct properties ) in Table view which is already created by me, but I cant access carModel array
I tried for loop but it returns an error
for i in cars.carModel {
lbl.text = cars.carModel[i]
}
any solution will be appericated
// data source file
struct Cars {
let carName:String
let carModel:[String]
}
let cars:[Cars] = [
Cars(carName: "Mercedes", carModel: ["S Class","A Class", "B Class"]),
Cars(carName: "BMW", carModel: ["X5","X6","X7"]),
Cars(carName: "Ford", carModel: ["Fuison","Focus","Mustang"]),
Cars(carName: "Toyota", carModel: ["Camry", "Corolla"]),
Cars(carName: "Hyundai", carModel: ["Elantra"])
]
// table view cell file
class TableViewCell: UITableViewCell {
#IBOutlet var lbl: UILabel!
func configure(with cars:Cars){
lbl.text = cars.carName
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
// mainviewcontroller
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
//#IBOutlet weak var tableView
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension ViewController: UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cars.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell
cell?.setup(with: cars[indexPath.row])
return cell!
}
}
extension ViewController:UICollectionViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "TableViewController") as? TableViewController
self.navigationController?.pushViewController(vc!, animated: true)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.configure(with: cars[indexPath.row])
return cell
}
}
Here's my simulator:
img1
img2
what I want:expected result img
When you defined your UITableViewDataSource you set the number of rows in function method to return a constant (5). In cellForRowAt function you setup the cell with a Cars object.
I think you are mixing what data should be use for table view's datasource.
For collection view data source you have to:
Number of rows in section function should return: cars.count
Cell for item at function must configure CollectionViewCell with a Cars instance
I think all you are missing is saving which cell is being selected in collection view to use this to supply correct data to table view.
In collection view delegate when user taps on a cell you can either save the selected indexPath or Cars instance, this will be use by table view delegate. Let's call it selectedCar.
Then for table view data source you have to:
Number of rows in section function should return: selectedCar.carModel.count
Cell for item at function must configure TableViewCell with a CarModel instance (which is a String)
I created a small project in Github as a sample, is working as expected. Check it out. Hope this helps you. Sample video in Youtube.
Note: Setting datasource and delegate for tableView or collectionView in ViewController is not the best way to organize code but for sample code is ok.
In my iOS app, I have a UICollectionView controller with the following code inside:
var images = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
loadImages()
// Register cell classes
}
func loadImages() {
images.append(UIImage(named: "ActivityTab")!)
self.collectionView?.reloadData()
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return images.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! EventCollectionViewCell
let image = images[indexPath.row]
cell.imageView.image = image
return cell
}
And I have a UICollectionViewCell (EventCollectionViewCell) with the following code:
#IBOutlet weak var imageView: UIImageView!
override func prepareForReuse() {
super.prepareForReuse()
self.imageView.image = nil
}
I have linked everything up with the storyboard too. This looks like it should work but I get the following error:
Could not cast value of type 'UICollectionViewCell' (0x1092c04a8) to EventCollectionViewCell'
Does anyone know what the error is and how to fix it. It only shows when I return images.count in the number of sections.
I am using xcode 9 but have tried with xcode 8 and the error still persists. Any help would be greatly appreciated. :)
Let change this line of code:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! EventCollectionViewCell
to:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? EventCollectionViewCell else { return UICollectionViewCell() }
Check for the following things:
EventCollectionViewCell is a subclass of UICollectionViewCell.
Have you registered EventCollectionViewCell with the collectionView?
EventCollectionViewCell has the same Reusable Identifier that you are using while calling dequeueReusableCell with "reuseIdentifier".
What is the value of reuseIdentifier here?
I have a custom UITableViewCell with 2 labels and a button. The cell has it's own class:
class personTableCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBAction func inviteButtonPressed(_ sender: Any) {
self.accessoryType = .checkmark
}
}
Inside the view controller that contains the table view, I add the cells to the table in this method:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "person", for: indexPath) as? personTableCell
cell?.nameLabel.text = results[indexPath.row].name
cell?.emailLabel.text = results[indexPath.row].email
return cell!
}
When a user presses the button inside the cell that calls the #IBAction func inviteButtonPressed, I want to add the cell's labels' text into an array that's initialized in the same view controller as the table.
How can I achieve such a thing if the #IBAction func inviteButtonPressed is in a seperate file as the table's view controller?
I think using delegate is one of solutions.
In TableViewCell class
#objc protocol PersonTableViewCellDelegate {
func personTableViewCellInviteButtonPressed(cell: PersonTableViewCell)
}
class PersonTableViewCell: UITableViewCell {
weak var delegate: PersonTableViewCellDelegate?
#IBAction func inviteButtonPressed(_ sender: Any) {
delegate?.personTableViewCellInviteButtonPressed(cell: self)
}
}
In ViewController class
class TableViewController: UITableViewController, PersonTableViewCellDelegate {
var results: [Person] = []
var invited: [Person] = []
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "person", for: indexPath) as! PersonTableViewCell
cell.nameLabel.text = results[indexPath.row].name
cell.emailLabel.text = results[indexPath.row].email
cell.delegate = self
return cell
}
func personTableViewCellInviteButtonPressed(cell: PersonTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else {
return
}
let person = results[indexPath.row]
invited.append(person)
}
}
I have 2 files.
myTableViewController.swift
myTableCell.swift
Can I get the indexPath.row in myTabelCell.swift function?
Here is myTableCell.swift
import UIKit
import Parse
import ActiveLabel
class myTableCell : UITableViewCell {
//Button
#IBOutlet weak var commentBtn: UIButton!
#IBOutlet weak var likeBtn: UIButton!
#IBOutlet weak var moreBtn: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func likeBtnTapped(_ sender: AnyObject) {
//declare title of button
let title = sender.title(for: UIControlState())
//I want get indexPath.row in here!
}
Here is myTableViewController.swift
class myTableViewController: UITableViewController {
//Default func
override func viewDidLoad() {
super.viewDidLoad()
//automatic row height
tableView.estimatedRowHeight = 450
tableView.rowHeight = UITableViewAutomaticDimension
}
// cell config
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//define cell
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexPath) as! myTableCell
}
As you can see... I'm trying to get indexPath.row in myTableCell, liktBtnTapped function.
Could you let me know how can I access or get IndexPath.row?
I have created a UIResponder extension with a recursive method that you can use in any UIView (which inherits from UIResponder) to find a parent view of a specific type.
import UIKit
extension UIResponder {
/**
* Returns the next responder in the responder chain cast to the given type, or
* if nil, recurses the chain until the next responder is nil or castable.
*/
func next<U: UIResponder>(of type: U.Type = U.self) -> U? {
return self.next.flatMap({ $0 as? U ?? $0.next() })
}
}
Using this, we can extend UITableViewCell with some convenient read-only computed properties for the table view and index path of the cell.
extension UITableViewCell {
var tableView: UITableView? {
return self.next(of: UITableView.self)
}
var indexPath: IndexPath? {
return self.tableView?.indexPath(for: self)
}
}
Here is how you could use it in your example:
#IBAction func likeBtnTapped(_ sender: AnyObject) {
//declare title of button
let title = sender.title(for: UIControlState())
//I want get indexPath.row in here!
self.indexPath.flatMap { print($0) }
}
Swift 4+
Try this inside your cell.
func getIndexPath() -> IndexPath? {
guard let superView = self.superview as? UITableView else {
print("superview is not a UITableView - getIndexPath")
return nil
}
indexPath = superView.indexPath(for: self)
return indexPath
}
Easy.. You can do like this inside button action:
let section = 0
let row = sender.tag
let indexPath = IndexPath(row: row, section: section)
let cell: myTableCell = self.feedTableView.cellForRow(at: indexPath) as! myTableCell
And afterwards in cellForRowAtIndexPath:
// add the row as the tag
cell.button.tag = indexPath.row
Another Approach for Swift 4.2 and not assuming Superview will be always a tableview
extension UITableViewCell{
var tableView:UITableView?{
return superview as? UITableView
}
var indexPath:IndexPath?{
return tableView?.indexPath(for: self)
}
}
Usage example
#IBAction func checkBoxAction(_ sender: UIButton) {
guard let indexPath = indexPath else { return }
sender.isSelected = !sender.isSelected
myCustomCellDelegate?.checkBoxTableViewCell(didSelectCheckBox: sender.isSelected, for: indexPath)
}
Swift 4.1. Here I created function to get IndexPath. Just pass your UIView(UIButton,UITextField etc) and UITableView object to get IndexPath.
func getIndexPathFor(view: UIView, tableView: UITableView) -> IndexPath? {
let point = tableView.convert(view.bounds.origin, from: view)
let indexPath = tableView.indexPathForRow(at: point)
return indexPath
}
Create a property indexPath in the cell class and set it in cellForRowAtIndexPath when the cell is reused.
But there is a caveat: Some table view methods to rearrange the cells don't call cellForRowAtIndexPath. You have to consider this case.
But if you use always only reloadData() it's safe and pretty easy.
Another way is to put the code regarding controlling things back in the controller class and run it via callback closures capturing the index path.
Heres another way of doing it
import UIKit
import Parse
import ActiveLabel
class myTableCell : UITableViewCell {
//Button
#IBOutlet weak var commentBtn: UIButton!
#IBOutlet weak var likeBtn: UIButton!
#IBOutlet weak var moreBtn: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
}
class myTableViewController: UITableViewController {
//Default func
//assuming you have an array for your table data source
var arrayOfTitles = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//automatic row height
tableView.estimatedRowHeight = 450
tableView.rowHeight = UITableViewAutomaticDimension
}
// cell config
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//define cell
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexPath) as! myTableCell
cell.commentBtn.tag = indexPath.row
cell.commentBtn.addTarget(self, action: #selector(likeBtnTapped(_:), forControlEvents:.TouchUpInside)
//cell config end
#IBAction func likeBtnTapped(sender: UIButton) {
let btn = sender
let indexP = NSIndexPath(forItem: btn.tag, inSection: 0)
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexP) as! myTableCell
//I want get indexPath.row in here!
let title = arrayOfTitles[indexP.row]
//declare title of button
cell.commentBtn.setTitle(title, forState: UIControlState.Normal)
}
}
My solution was subclassing UITableViewCell, so can add IndexPath property. assign custom class for table view cell in storyboard. assign IndexPath value when rowAtIndexPath called.
class MyTableViewCell: UITableViewCell {
var indexPath: IndexPath?
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cellid1", for: indexPath)
(cell as? MyTableViewCell)?.indexPath = indexPath
return cell
}
Swift 5:
if
let collectionView = superview as? UICollectionView,
let index = collectionView.indexPath(for: self)
{
// stuff
}