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.
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 have two View controllers(VC-1, VC-2).
View controller 1 is having a table view and further that table view is consists of 4 collection views. I want to navigate to new VC(VC-2). But unable to get "self.storyboard.instantiateViewController" in didSelect method of collection View.
So now how I can navigate to VC-2
You should use delegate. you can create a protocol and define a method for routing and implement it into VC1
You can use the below sample code.
This code is using closure syntax
class FirstVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell_Identifier") as! TableCell
cell.configureCell(data: ["Your", "Data"])
cell.didSelectRow = { data in
// Goto second view controller
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC")
self.navigationController?.pushViewController(vc!, animated: true)
}
return cell
}
}
class TableCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var colView: UICollectionView!
var didSelectRow: ((_ data: String) -> Void)? = nil // Closure
var arrData = [String]()
func configureCell(data: [String]) {
// Setup CollectionView data
self.arrData = data
self.colView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.arrData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CELL_IDENTIFIER", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let data = self.arrData[indexPath.row]
didSelectRow?(data)
}
}
I have a tableView with several cells (created by MVVM-architecture).
In ViewController I fill my tableView like this:
tbv.registerCells(
withModels:
FirstViewModel.self,
SecondViewModel.self,
ThirdViewModel.self)
My task is to put my TableView in one cell of CollectionView. I thought l have to create CollectionView in my ViewController, after it create CollectionViewCell and CollectionCellViewModel, but how to do it exactly I don't understand.
If you know, how to make it, help.
How I have several tableviews in collection views in one of my apps. First I have a view controller in which I build my collection view. As usually proposed in the new design guidelines, I have the Collection View delegate and data source in an extension of this view controller.
In your view controller you define a delegate and data source for your table view. Preferably, this is a different class. I would not have the tableview data source and delegate also in the same view controller as your collection view.
class WorkoutSettingsViewController: UIViewController, LoadWorkoutSettings {
//MARK: - Properties
//Used variables
//Used constants
private let settingsDelegate = SettingsTableViewDelegate()
The extension would then look like this.
extension WorkoutSettingsViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func numberOfSections(in collectionView: UICollectionView) -> Int {
//Whatever sets your sections
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//Whatever sets your rows per section
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Workout Settings", for: indexPath) as! SettingsCollectionViewCell
settingsDelegate.workoutTitleLabel = [countdown, mainView, spokenMessage]
settingsDelegate.mainContentLabel = getSettingsContent()
cell.settingsTableView.delegate = settingsDelegate
cell.settingsTableView.dataSource = settingsDelegate
cell.settingsTableView.reloadData()
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
//Whatever you need as header or footer
}
The delegate does exactly what you would like the table view data source and delegate to do.
class SettingsTableViewDelegate: NSObject, UITableViewDataSource, UITableViewDelegate {
//MARK: - Properties
//Used variables
var workoutTitleLabel = [String]()
var mainContentLabel = [String]()
var selectedSetting: ((Int) -> ())? = .none
private var secondaryContentLabel = [String]()
//Used constants
private let onTitle = NSLocalizedString("ON", comment: "Localized on title")
private let offTitle = NSLocalizedString("OFF", comment: "Localized off title")
private let fontColorBlack = UIColor(red: 20.0/255.0, green: 20.0/255.0, blue: 19.0/255.0, alpha: 1.0)
private let fontColorRed = UIColor(red: 255.0/255.0, green: 96.0/255.0, blue: 89.0/255.0, alpha: 1.0)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
workoutTitleLabel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Settings Cell") as! WorkoutTableViewCell
cell.workoutTitle.text = workoutTitleLabel[indexPath.row]
cell.mainContent.text = mainContentLabel[indexPath.row]
cell.secondaryContent.text = ""
(mainContentLabel[indexPath.row] == offTitle) ? (cell.mainContent.textColor = fontColorRed) : (cell.mainContent.textColor = fontColorBlack)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedSetting?(indexPath.row)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
61
}
}
Your collection view cell should look like this.
class SettingsCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var settingsTableView: UITableView!
}
This should then work. If you need to have a callback from the table view delegate / data source to your view controller managing your collection view, you can use a closure. In the example table view delegate the closure is called selectedSettings. In your view controller in viewDidLoad you define the call back for instance like this:
override func viewDidLoad() {
super.viewDidLoad()
settingsDelegate.selectedSetting = { [unowned self] selection in
startSettingsMenu(for: selection)
}
}
The result looks like this.
Kind regards,
MacUserT
In Tableview each row you can load UITableViewCell with pass collectionviewdata
//View Controller
var collectionView1Data = ["cell1", "cell2"]
var collectionView2Data = ["cell1", "cell2"]
//UITableviewDelegate Datasource
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//.....
if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellID") as? TableviewCell
cell.collectionData = collectionView1Data /// Collectionviewdata
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellID") as? TableviewCell
cell.collectionData = collectionView2Data
return cell
}
}
==============================
Each Tableviewcell contains CollectionView
//UITableViewCell
class TableviewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
var collectionData: [String]? {
didSet {
guard collectionData != nil else {
return
}
collectionView.reloadData()
}
}
override func awakeFromNib() {
super.awakeFromNib()
collectionView.register(UINib(nibName: "collectionViewCell", bundle: nil), forCellWithReuseIdentifier: "collectionViewCell")
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension TableviewCell: UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
collectionData.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? collectionViewCell
cell...
return cell
}
}
I don't understand why my delegate doesn't 'receive' anything. Later down the road, I want to hand over a UIImage from the ViewController into a CollectionViewCell inside a TableViewCell by using the delegate. The ViewController contains a tableView and I created a custom cell with a Nib (Xib) file. Each cell contains a collectionView. The tableview's cell class is my delegate, but when the ViewController 'sends' (is that the correct term?) anything to the delegate, nothing happens. Here is the code for my viewController:
protocol ViewControllerDelegate {
func delegateMethod(with string: String)
}
class ViewController: UIViewController {
var viewControllerdelegate: ViewControllerDelegate?
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet var suggestionTableView: UITableView!
var image = UIImage()
override func viewDidLoad() {
super.viewDidLoad() suggestionTableView.delegate = self
suggestionTableView.dataSource = self
suggestionTableView.register(UINib(nibName: "SuggestionTableViewCell", bundle: nil), forCellReuseIdentifier: "ID")
searchBar.delegate = self
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ID", for: indexPath) as! SuggestionTableViewCell
cell.currentImage = image
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
extension ViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print(searchText)
viewControllerdelegate?.delegateMethod(with: "Henlo")
}
}
And here is my TableViewCell class:
class SuggestionTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
var currentCollectionIndex = IndexPath()
var currentCell = UICollectionViewCell()
var currentImage = UIImage()
let controller = ViewController()
override func awakeFromNib() {
super.awakeFromNib()
controller.viewControllerdelegate = self
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CollectionCell")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension SuggestionTableViewCell: ViewControllerDelegate {
func delegateMethod(with string: String) {
print(string)
}
}
extension SuggestionTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CollectionViewCell
cell.userImage.image = currentImage
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
Why is my delegate not working? I am so sorry for the code mess, but stackoverflow just doesn't let me organize it properly this time...
In the TableViewCell class you create an instance of ViewController class
let controller = ViewController()
this would be a different instance than the one that created the TableViewCell hence the delegate won't be called as you are setting the delegate of the wrong instance.
Also why do you need a delegate for this? I think creating a function in the TableViewCell class should do.
I've been having this problem recently, and I couldn't seem to find a fix on any other similar questions. I have a collectionView which just display's colours (see below image) and I would like to store the cell's background colour in a variable to pass through an unwind segue, in order to set a background colour of another element. However, I can't seem to get this function to work as it has on previous projects and versions of this application and I was wondering if anyone knows what the problem might be ?
After pressing the 'add' button at the bottom, it unwinds and appends a new item to another collectionView, seen here:
I am able to get the name that the user has put in, however I have to use a default colour as I can't seem to get the didSelectItemAtIndexPath function to work.
Here's my code for the addViewController (first image)
import UIKit
import ChameleonFramework
class mondayViewController: UIViewController {
// collectionView
#IBOutlet var dayView: UICollectionView!
// data to display
var data = Day.mondayData()
// data from segues
var receivedName: String?
var receivedColour: UIColor?
override func viewDidLoad() {
super.viewDidLoad()
// set title
self.navigationController!.navigationBar.topItem!.title = "Monday"
// set datasource for collectionView
// datasource is self as the collectionViewDataSource methods are in the extension class below
dayView.dataSource = self
// make the navigationBar flat to flow onto the options tab
self.navigationController?.hidesNavigationBarHairline = true
}
#IBAction func unwindToMonday(segue: UIStoryboardSegue) {
if segue.identifier == "mondayUnwind" {
let subjectAdd = segue.sourceViewController as! mondayAddSubjectView
receivedName = subjectAdd.nameField.text
appendStuff()
}
}
func appendStuff() {
data.append(Day(name: receivedName, color: UIColor.flatRedColor()))
dayView.reloadData()
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}
// data.append(Day(name: receivedName, color: receivedColour))
// dayView.reloadData()
// currently used for testing
#IBAction func mondayAddButton(sender: AnyObject) {}
}
// mandatory functions for UICollectionView to work
// this includes assigning the number of sections, how manycells are meant to be in the section(s) and what cell to use (mondayCell in this case)
extension mondayViewController: UICollectionViewDataSource {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! mondayCell
cell.data = self.data[indexPath.item]
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath)
}
// custom style the size of the collectionView. Changes the width to fill the screen and the height to 70
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let kWhateverHeightYouWant = 80
return CGSizeMake(collectionView.bounds.size.width, CGFloat(kWhateverHeightYouWant))
}
}
if anyone could let me know if I am doing something wrong, or if there is a solution to this issue, that would be awesome.
Cheers,
Rowan
UPDATE:
I have added the collectionViewDataSource and Delegate Methods to the main class (code below):
import UIKit
import ChameleonFramework
class mondayAddSubjectView: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var pageView: PageViewController = PageViewController()
#IBOutlet var selectColourLabel: UILabel!
// would be a public variable, however it is an internal class
internal var cellColour: UIColor!
// collectionView
#IBOutlet var colourPicker: UICollectionView!
// textfield for the user to input their subject name
#IBOutlet var nameField: UITextField!
// source of colours to use in the colour collectionView
private var data = collectionViewColors.createColor()
override func viewDidLoad() {
super.viewDidLoad()
self.colourPicker.delegate = self
colourPicker.dataSource = self
// gesture recognizer to determine when the user has tapped anywhere but the text field, which cancells the keyboard
let tap: UIGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(mondayAddSubjectView.dismissKeyboard))
view.addGestureRecognizer(tap)
}
// dismiss keyboard when the user taps away from the text field (called above with #selector)
func dismissKeyboard() {
view.endEditing(true)
}
// MARK: - CollectionView Delegate and Data source methods
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath)
print("it works")
}
// choose how many sections are in the collectionView. as no content break is required, this will be 1
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
// choose how many items are in each section. as there is only 1 section, it will be the number of stored items
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
// set re-use identifier and the data that will go in each cell
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! colourCell
cell.data = self.data[indexPath.item]
return cell
}
}