I have TableView and inside TableViewCell I have added UICollectionView. Now when user taps on any UICollectionView, I want to pass the data to next viewController but unfortunately I cannot use perform segue inside UITableViewCell class where I have didselect item. Is there any way to perform segue over here ?
Whole Code
class UserLibraryViewController: UIViewController {
extension UserLibraryViewController : UITableViewDelegate { }
extension UserLibraryViewController : UITableViewDataSource {
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return myBooksGenre[section]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return myBooksGenre.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("userBooksTableViewCell") as! UserLibraryTableViewCell
cell.tableIndexPath = indexPath
return cell
}
}
TableViewCell
class UserLibraryTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
var tableIndexPath: NSIndexPath?
}
extension UserLibraryTableViewCell : UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("userBooksCollectionViewCell", forIndexPath: indexPath) as! UserLibraryCollectionViewCell
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("\(self.tableIndexPath!.section) ---- \(indexPath.item) \(tableSectionHeader[self.tableIndexPath!.section]) ")
}
}
extension UserLibraryTableViewCell : UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let itemsPerRow:CGFloat = 4
let hardCodedPadding:CGFloat = 5
let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
return CGSize(width: itemWidth, height: itemHeight)
}
}
Create one protocol, after that create its instance inside TableViewCell and implement the protocol in the UserLibraryViewController, after that in the didSelectItemAtIndexPath of CollectionView use that delegate instance to call the method like this.
protocol CustomCollectionDelegate {
func selectedCollectionCell(indexPath: NSIndexPath)
}
class UserLibraryTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
var tableIndexPath: NSIndexPath?
var delegate: CustomCollectionDelegate?
}
Now call this method inside your didSelectItemAtIndexPath of CollectionView.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("\(self.tableIndexPath!.section) ---- \(indexPath.item) \(tableSectionHeader[self.tableIndexPath!.section]) ")
self.delegate?.selectedCollectionCell(indexPath)
}
Now implement this protocol in UserLibraryViewController like this and set delegate in the cellForRowAtIndexPath.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("userBooksTableViewCell") as! UserLibraryTableViewCell
cell.tableIndexPath = indexPath
cell.delegate = self
return cell
}
extension UserLibraryViewController : CustomCollectionDelegate {
func selectedCollectionCell(indexPath: NSIndexPath) {
self.performSegueWithIdentifier("Identifier", sender: indexPath)
}
}
Note: I have added protocol method with NSIndexPath as parameter you can change it with your custom object or with Dictionary/Array.
Related
I am created UICollectionView inside of UITableView ,populating array values in UItableview is working perfect, when trying to populate a array values into collectionView getting same values in collectionView of all the rows of tableView, I want to populate a zeroth index array in collectionview of zeroth row UItableview and so on
below I tried a sample,
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,UICollectionViewDelegate,UICollectionViewDataSource{
let array = ["product 1","product 2","product 3","product4"]
let array1 = ["product id 1","product id 2","product id 3","product id 4"]
let array2 = [["product id 1","product id 2"],["product id 3","product id 4"],["product id 5","product id 6"],["product id 7","product id 8","product id 9","product id 10","product id 11"]] as [NSArray]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cells") as! TableViewCell
cell.tableviewlabel1.text = array[indexPath.row]
cell.tableviewlabel2.text = array1[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array2.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection", for: indexPath) as! CollectionViewCell
let rowValue : NSArray = array2[indexPath.row]
for i in 0..<rowValue.count {
cell.collectionviewlabel1.text = rowValue[i] as? String
}
return cell
}
For example ,I want a zeroth index of array2 values in collectionview of zeroth row of tableview, and first index values in collectionview of first row of tableview and so on.
First your array2 has to be like this :
let array2 = [["product id 1","product id 2"],["product id 3","product id 4"],["product id 5","product id 6"],["product id 7","product id 8","product id 9","product id 10","product id 11"]]
Don't use NSArray in Swift. you can write it as [String], [[String]].
Your ViewController will have only UITableViewDelegate and UITableViewDatasource methods:
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cells") as! TableViewCell
cell.tableviewlabel1.text = array[indexPath.row]
cell.tableviewlabel2.text = array1[indexPath.row]
cell.arrayForCollectionView = array2
return cell
}
}
Then in your TableViewCell:
class TableViewCell: UITableViewCell {
var arrayForCollectionView : [[String]]! {
didSet {
self.collectionView.reloadData
}
}
#IBOutlet weak var collectionView: UICollectionView!
}
extension TableViewCell : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (arrayForCollectionView != nil) {
return arrayForCollectionView.count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection", for: indexPath) as! CollectionViewCell
let rowValue = arrayForCollectionView[indexPath.row]
for i in 0..<rowValue.count {
cell.collectionviewlabel1.text = rowValue[i] as? String
}
return cell
}
}
Let me know if you are getting any issue.
Giving an example for that:
first tableView is inside ViewController
class ViewController: UIViewController {
//MARK:- IBOUTELTS
//MARK:-
#IBOutlet weak var tableView: UITableView!
//MARK:- VARIABLES
//MARK:-
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension YourViewConroller: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
}
}
And Your CollectionView is inside TableViewCell
extension YourTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
And make different class for TableView and CollectionView:
class YourTableViewCell: UITableViewCell { // tableview cell
}
classYourCollectionViewCell: UICollectionViewCell { // collectionviewcell
}
I want to show image using sdWebImage to an image inside collectionView Cell which is inside tableView cell.
How to do it ? and where i should write the sdWebImage api ? in CategogyRow.swift ?
I have 3 different cell where i will be doing the same thing.
Link i reffered
Code :
ViewController.swift :
class ViewController: UIViewController {
var categories = ["Action", "Drama", "Science Fiction", "Kids", "Horror"]
}
extension ViewController : UITableViewDelegate { }
extension ViewController : UITableViewDataSource {
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CategoryRow
return cell
}
}
CategoryRow.swift
class CategoryRow : UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
}
extension CategoryRow : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for: indexPath) as! VideoCell
cell.imageView.image.sd_setImage(with: URL(string: "url"), placeholderImage: UIImage(named: "placeholder.png"))`
return cell
}
}
extension CategoryRow : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemsPerRow:CGFloat = 4
let hardCodedPadding:CGFloat = 5
let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
return CGSize(width: itemWidth, height: itemHeight)
}
}
VideoCell.swift
class VideoCell : UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
}
I have a tableview cell inside which i have added collectionview cell ( for horizontal scrolling).
Now i want to push to other navigation controller on button of any cell of horizontal collectionview. How to do it ? O
Code :
ViewController.swift :
class ViewController: UIViewController {
var categories = ["Action", "Drama", "Science Fiction", "Kids", "Horror"]
}
extension ViewController : UITableViewDelegate { }
extension ViewController : UITableViewDataSource {
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CategoryRow
return cell
}
}
CategoryRow.swift
class CategoryRow : UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
}
extension CategoryRow : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for: indexPath) as! VideoCell
cell.button.addTarget(self, action:#selector(ViewController.goToLookAtPage(_:)), for: .touchUpInside)
return cell
}
}
extension CategoryRow : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemsPerRow:CGFloat = 4
let hardCodedPadding:CGFloat = 5
let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
return CGSize(width: itemWidth, height: itemHeight)
}
}
where do i declare goToLookAtPage function ?
VideoCell.swift
class VideoCell : UICollectionViewCell {
#IBOutlet weak var button: UIButton!
}
You need to delclare it
on CategoryRow Class also declare global closure
like
var buttonTapped:((CategoryRow?) -> Void)? = nil
Now we have closure to call
Implement goToLookAtPage as below
func goToLookAtPage(_ sender:UIButton) {
if let btnAction = self.buttonTapped {
btnAction(self)
}
}
Now in your ViewController add following in cellForRowAtIndexPath
cell.buttonTapped = {(cell) -> Void in
//You Got your response , do push in main Thread
}
Hope it helps to you
In your button handler function get the button's position where sender is your button.
let position = sender.convert(CGPoint.zero, to: self.collectionView)
Get the index path for the given position.
let indexPath = self.collectionView?.indexPathForItem(at: position)
Get the model from your data source with the index path.
let model = self.data[indexPath.row]
Now that you have your data model, pass it to your destination and push your view controller or whatever.
I have a tableview cell inside which i have added collectionview cell ( for horizontal scrolling).
Now i want to push to other navigation controller on pressing any cell of horizontal collectionview. How to do it ? Or how can i define delegate methods for cell press.
Code :
ViewController.swift :
class ViewController: UIViewController {
var categories = ["Action", "Drama", "Science Fiction", "Kids", "Horror"]
}
extension ViewController : UITableViewDelegate { }
extension ViewController : UITableViewDataSource {
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CategoryRow
return cell
}
}
CategoryRow.swift
class CategoryRow : UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
}
extension CategoryRow : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for: indexPath) as! VideoCell
return cell
}
}
extension CategoryRow : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemsPerRow:CGFloat = 4
let hardCodedPadding:CGFloat = 5
let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
return CGSize(width: itemWidth, height: itemHeight)
}
}
VideoCell.swift
class VideoCell : UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
}
Here you will get the cell click at the delegate method didSelectItemAtIndexPath on CategoryRow class and from there you can fire a delegate to get call inside ViewController
ViewController.swift :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CategoryRow
cell.delegate = self
return cell
}
VideoCell.swift :
protocol CategoryRowDelegate:class {
func cellTapped()
}
CategoryRow.swift :
class CategoryRow : UITableViewCell {
weak var delegate:CategoryRowDelegate?
#IBOutlet weak var collectionView: UICollectionView!
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if delegate!= nil {
delegate?.cellTapped()
}
}
Add the delegate function inside ViewController
func cellTapped(){
//code for navigation
}
First create protocol for delegation from CategoryRow.swift like below code
protocol CollectionViewSelectionDelegate: class {
func didSelectedCollectionViewItem(selectedObject: AnyObject)
}
Now create delegate object on VideoCell.swift like below
weak var delegate:CollectionViewSelectionDelegate?
Change ViewController.swift code before return cell
cell?.delegate = self
Override method of delegate in ViewController.swift and call similar method from VideoCell.swift from UICollectionView Delegate method.
I think its better to use Notification in this case.
post a notification in didSelectItem of collection view
NSNotificationCenter.defaultCenter().postNotificationName(notificationIdentifier, object: nil)
and add an observer in viewController viewDidLoad as follows
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(pushToNew(_:)), name: notificationIdentifier, object: nil)
in the new pushToNew function, perform your segue
func pushToNew(notification: Notification) {
// perform your segue here. Navigate to next view controller
}
Make a protocol
protocol collectionViewCellClicked{
func cellClicked()
}
Implement this protocol in main View Controller Your View Controller look like this
class ViewController: UITableViewController, collectionViewCellClicked{ func cellClicked(){ // Your Code }}
Your cellForRowAt delegate look like this
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: <#T##String#>, for: <#T##IndexPath#>)
cell.delegate = self
return cell
}
In your Table View Cell Make a variable of type collectionViewCellClicked
var delegate: collectionViewCellClicked?
and in your didSelectItemAt delegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate.cellClicked()
}
I have added UICollectionView in UITableView. Now, I want to identify on which cell the user taps and the indexPath of that UICollectionView.
I'm getting correct indexPath but could not get the tag of on which the UICollectionView he tapped. I'm doing something like this to get tap
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("\(collectionView.tag) ---- \(indexPath.item) ")
}
Whole Code
class UserLibraryViewController: UIViewController {
extension UserLibraryViewController : UITableViewDelegate { }
extension UserLibraryViewController : UITableViewDataSource {
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return myBooksGenre[section]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return myBooksGenre.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("userBooksTableViewCell") as! UserLibraryTableViewCell
return cell
}
}
TableViewCell
class UserLibraryTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
}
extension UserLibraryTableViewCell : UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("userBooksCollectionViewCell", forIndexPath: indexPath) as! UserLibraryCollectionViewCell
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("\(collectionView.tag) ---- \(indexPath.item) ")
}
}
extension UserLibraryTableViewCell : UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let itemsPerRow:CGFloat = 4
let hardCodedPadding:CGFloat = 5
let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
let itemHeight = collectionView.bounds.height - (2 * hardCodedPadding)
return CGSize(width: itemWidth, height: itemHeight)
}
}
You are having multiple section in your tableView, so you can create one NSIndexPath instance in your UserLibraryTableViewCell and initialize it in the cellForRowAtIndexPath like this.
class UserLibraryTableViewCell: UITableViewCell {
var tableIndexPath: NSIndexPath?
}
Now set this tableIndexPath in the cellForRowAtIndexPath.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("userBooksTableViewCell") as! UserLibraryTableViewCell
cell.tableIndexPath = indexPath
return cell
}
Now in didSelectItemAtIndexPath of collectionView access tableIndexPath like this.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("\(self.tableIndexPath) ---- \(indexPath.item) ")
}
Try this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("userBooksTableViewCell") as! UserLibraryTableViewCell
cell.collectionView.tag = indexpath.row
return cell
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myBooksGenre.count
}
You are not setting the Tag for the Collection View for the particular cell in the cellForRowAtIndexPath of Table View.
Update the code.
Also conform UICollectionViewDelegate protocol:
extension UserLibraryTableViewCell : UICollectionViewDataSource, UICollectionViewDelegate