How to get data from a selected UICollectionView cell? - ios

My ViewController consists of a UIButton and a UICollectionView which has 4 cells. I will select any cell and when I tap the button I want to get the data from only the selected UICollectionViewCell.
The UIButton is outside the UICollectionView and UICollectionViewCell.

You can use indexPathsForSelectedItems to get the indexPaths for all selected Items. After you requested all the IndexPath you can simply ask the collectionView for the corresponding cell to get your Data.
import UIKit
class TestCell: UICollectionViewCell {
var data : String?
}
class ViewController: UIViewController {
var model = [["1","2","3","4"]]
#IBOutlet weak var collectionView: UICollectionView?
#IBAction func buttonTapped(sender: AnyObject) {
if let collectionView = self.collectionView,
let indexPath = collectionView.indexPathsForSelectedItems?.first,
let cell = collectionView.cellForItem(at: indexPath) as? TestCell,
let data = cell.data {
print(data)
}
}
}
extension ViewController : UICollectionViewDataSource {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return model.count
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return model[section].count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("test", forIndexPath: indexPath) as! TestCell
cell.data = self.model[indexPath.section][indexPath.row]
return cell
}
}

Related

Hint on how to create & pass data via segue when collectionview cell is pressed

I have a UICollectionView embedded in a UITableViewCell & I want to perform a segue when a UICollectionViewCell is pressed & pass the data represented by that cell to the destination ViewController for detailed information.
Here is the code for the embedded UICollectionView inside the UITableViewCell
#IBOutlet weak var EventCollection: UICollectionView!
var events = [Events]()
override func awakeFromNib() {
super.awakeFromNib()
EventCollection.delegate = self
EventCollection.dataSource = self
}
extension PopularCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return events.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = EventCollection.dequeueReusableCell(withReuseIdentifier: "EventCell", for: indexPath) as! EventCell
let event = events[indexPath.row]
print("Event Name:\(event.event_name)")
cell.event = event
cell.tag = indexPath.row
return cell
}
How do I perform & prepare a segue in the main ViewController when a UICollectionViewCell is pressed so as to pass the data contained by that cell to the destination ViewController
Here are the steps that you need to do.
As you said, your CollectionView is inside TableView. So your TableView delegates/DataSources binded with the MainViewController. CollectionView delegates/DataSources binded with the TableViewCell.
Now create a protocol to know user has clicked on collectionView.
protocol MyProtocol : class {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
}
In TableViewCell, you need to call delegate like this,
class MyTableCell : UITableViewCell, UICollectionViewDelegate {
weak var delegate : MyProtocol?
:
:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let delegate = delegate {
delegate.collectionView(collectionView, didSelectItemAt: indexPath)
}
}
}
Now your MainViewController must have to conform this protocol,
class MainViewController :UIViewController, MyProtocol {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Do segue here ....
}
}
Note : Make sure to bind delegate with your MainViewController i.e. in TableviewCellForRow have cell.delegate = self
Add the following code with in a new file named UIView_Ext
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
In func didSelectItem(At indexPath: IndexPath) method , write the following code
self.parentViewController?.performSegue(withIdentifier: "Identifer", sender: "Your Data in place of this string")

Make sure only 1 cell has an active state in a UICollectionView

I have an UICollectionView in which I want only want 1 cell to be active. With active I mean: the last cell that has been clicked (or the very first cell when to collection view lays out). When a user clicks a non-active cell, I want to reset the old active cell to a non-active state. I am having trouble doing this. This is because visibleCells, a property of collection view, only returns the cells on screen but not the cells in memory. This is my current way to locate an active cell and reset the state to non active.
This scenario can happen, causing multiple active cells: A user scroll slightly down so that the current active cell is not visible anymore, taps on a random cell and scroll up. The problem is that the old active cell stays in memory, although it is not visible: cellForItemAt(_:) does not gets called for that cell. Bad news is that visibleCells also do not find the old active cell. How can I find it? The function willDisplay cell also does not work.
An example project can be cloned directly into xCode: https://github.com/Jasperav/CollectionViewActiveIndex.
This is the code in the example project:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var collectionView: CollectionView!
static var activeIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
collectionView.go()
}
}
class Cell: UICollectionViewCell {
#IBOutlet weak var button: MyButton!
}
class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
func go() {
delegate = self
dataSource = self
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 500
}
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
if indexPath.row == ViewController.activeIndex {
cell.button.setTitle("active", for: .normal)
} else {
cell.button.setTitle("not active", for: .normal)
}
cell.button.addTarget(self, action: #selector(touchUpInside(_:)), for: .touchUpInside)
return cell
}
#objc private func touchUpInside(_ sender: UIButton){
let hitPoint = sender.convert(CGPoint.zero, to: self)
guard let indexPath = indexPathForItem(at: hitPoint), let cell = cellForItem(at: indexPath) as? Cell else { return }
// This is the problem. It does not finds the current active cell
// if it is just out of bounds. Because it is in memory, cellForItemAt: does not gets called
if let oldCell = (visibleCells as! [Cell]).first(where: { $0.button.titleLabel!.text == "active" }) {
oldCell.button.setTitle("not active", for: .normal)
}
cell.button.setTitle("active", for: .normal)
ViewController.activeIndex = indexPath.row
}
}
To recover from this glitch you can try in cellForRowAt
cell.button.tag = indexPath.row
when the button is clicked set
ViewController.activeIndex = sender.tag
self.reloadData()
You can use the isSelected property of the UIColectionViewCell. You can set an active layout to your cell if it is selected. The selection mechanism is implemented by default in the UIColectionViewCell. If you want to select/activate more than one cell you can set the property allowsMultipleSelection to true.
Basically this approach will look like this:
class ViewController: UIViewController {
#IBOutlet weak var collectionView: CollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.go()
}
func activeIndex()->Int?{
if let selectedItems = self.collectionView.indexPathsForSelectedItems {
if selectedItems.count > 0{
return selectedItems[0].row
}
}
return nil
}
}
class Cell: UICollectionViewCell {
#IBOutlet weak var myLabel: UILabel!
override var isSelected: Bool{
didSet{
if self.isSelected
{
myLabel.text = "active"
}
else
{
myLabel.text = "not active"
}
}
}
}
class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
func go() {
delegate = self
dataSource = self
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 500
}
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
return cell
}
}

How to add cell to dynamic collection view using swift

In top of that image you can see some images and plus button I want to do like this in my app so i used collection view to get this view and everything works fine but i cant add cell to my dynamic collection view can some one help me to do this process
code in my view controller:
import UIKit
import Alamofire
import SwiftyJSON
import SDWebImage
import SwiftElegantDropdownMenu
class EditPendingViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UITextViewDelegate,UICollectionViewDelegate,UICollectionViewDataSource,UIImagePickerControllerDelegate,UINavigationControllerDelegate{
var pendingImageString:[String]!
#IBOutlet var editPendingCollectionvIEW: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pendingImageString.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("editPendingCell", forIndexPath: indexPath) as!MypendindeditCollectionViewCell
let img:NSString = pendingImageString[indexPath.item]
cell.editImage.sd_setImageWithURL(NSURL(string: imageApikey + (img as String)))
cell.contentView.layer.borderColor = UIColor.lightGrayColor().CGColor
cell.contentView.layer.borderWidth = 1.0
return cell
}
}
I have not done any process for adding cell to my collection view i just loaded the data from the server.now help to add the cell
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pendingImageString.count + 1 // + 1 for last cell
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell : CollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CollectionViewCell
cell.btn.tag = indexPath.row
if indexPath.item = pendingImageString.count {
cell.imageview = //your static image that is shown in last cell
cell.cancelButton.isHidden = true
}
else{
let img:NSString = pendingImageString[indexPath.item]
cell.editImage.sd_setImageWithURL(NSURL(string: imageApikey + (img as String)))
cell.cancelButton.isHidden = false
}
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if indexPath.item = pendingImageString.count{
// update your array with new image file
}
}
// cancel button action
func buttonClicked(sender: UIButton?) {
let tag = sender.tag
// remove object from array and reload collectionview
}
On click of last cell of the collection view add logic to enter your product in the database then just reload the collection view
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == pendingImageString.count - 1
{
//TODO - add item to database and reload collectionview
}
}

multiple collectionView in multiple tableView cells

I have a tableView in which I have 2 custom cells, in both cells I have different CollectionView, the dataSource and delegate of both the CollectionViews is my ViewController.
So now how do I check which CollectionViews is to be configured in UICollectionView's respective methods?
Here is the view hierarchy
How do I come to know which is the collectionView that's there in the parameter of above function?
So here is my code:
class FeatureBannerTableViewCell: UITableViewCell {
#IBOutlet weak var featureCollectionView: UICollectionView!
}
class FeaturedTableViewCell: UITableViewCell {
#IBOutlet weak var FeatureTitle: UILabel!
#IBOutlet weak var seeAllButton: UIButton!
#IBOutlet weak var collectionView: UICollectionView!
}
extension ViewController: UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 2,3,6,7:
let cell = featuredTableView.dequeueReusableCellWithIdentifier("FeatureBannerTableViewCell", forIndexPath: indexPath) as! FeatureBannerTableViewCell
cell.featureCollectionView.dataSource = self
cell.featureCollectionView.delegate = self
return cell
default:
let cell = featuredTableView.dequeueReusableCellWithIdentifier("FeaturedTableViewCell", forIndexPath: indexPath) as! FeaturedTableViewCell
cell.collectionView.dataSource = self
cell.collectionView.delegate = self
return cell
}
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// how do I know if this collectionView is collectionView or featureCollectionView?
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// how do I know if this collectionView is collectionView or featureCollectionView?
}
}
I would use the tag property in UIView to identify your collection view. So when you configure your table cells in cellForRowAtIndexPath get the collection view of the cell and set its tag to something unique (I would use the row value of indexpath).
extension ViewController: UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 2,3,6,7:
let cell = featuredTableView.dequeueReusableCellWithIdentifier("FeatureBannerTableViewCell", forIndexPath: indexPath) as! FeatureBannerTableViewCell
cell.featureCollectionView.dataSource = self
cell.featureCollectionView.delegate = self
cell.featureCollectionView.tag = indexPath.row
return cell
default:
let cell = featuredTableView.dequeueReusableCellWithIdentifier("FeaturedTableViewCell", forIndexPath: indexPath) as! FeaturedTableViewCell
cell.collectionView.dataSource = self
cell.collectionView.delegate = self
cell.collectionView.tag = indexPath.row
return cell
}
}
}
Then in your collection view methods get the tag value and that will tell you which one of the many collection views it is.
extension ViewController: UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// how do I know if this collectionView is collectionView or featureCollectionView?
let datasourceIndex = collectionView.tag
// now use your datasource for the following index
}
}
I think, you can make use of isKindOfClass
Example:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyReuseIdentifier", for: indexPath as IndexPath) as! UICollectionViewCell
if cell.isKindOfClass(FeaturedBannerCollectionCell){
//implementation code
} else cell.isKindOfClass(FeaturedCollectionViewCell) {
//implementation code
}
return cell
}

Swift - how to open another viewcontroller with CollectionViewCell inside UITableViewCell

I'm really new in iOS/Swift and i'm in a small project. In this project i have a UITableView inside ViewController. And i have another file custom CollectionViewCell in side UITableViewCell.
I want when user click a cell in collectionview it will open another ViewController and it get data from this collectionviewcell.
This is my uitableview swift file:
class IndexRow: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var names:[String] = ["Movie 1","Movie 2","Movie 3","Movie 4","Movie 5","Movie 6"]
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
collectionView.registerClass(indexOneMovie.self, forCellWithReuseIdentifier: "onemovie")
let nib = UINib(nibName: "indexOneMovie",bundle: nil)
collectionView.registerNib(nib, forCellWithReuseIdentifier: "onemovie")
self.collectionView.backgroundColor = UIColor.clearColor()
self.collectionView.delegate = self
self.collectionView.dataSource = self
print("Hello")
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return names.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = self.collectionView.dequeueReusableCellWithReuseIdentifier("onemovie", forIndexPath: indexPath) as! indexOneMovie
cell.poster.image = UIImage(named: "poster.jpg")
cell.name.text = names[indexPath.row]
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print(indexPath.item)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let itemsPerRow:CGFloat = 2
let hardCodedPadding:CGFloat = 0
let itemWidth = (collectionView.bounds.width / itemsPerRow) - hardCodedPadding
let itemHeight = collectionView.bounds.height - (hardCodedPadding)
return CGSize(width: itemWidth, height: itemHeight)
}
How i can do it?
ok i have recently implemented the same in my app these are the links where i refered initially -
https://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell-in-swift/
http://www.thorntech.com/2015/08/want-your-swift-app-to-scroll-in-two-directions-like-netflix-heres-how/
you are making uicollectionview delegate confirms to uitableview cell so you cannot present or push to other view controller.
here is my code hope it will help you
homeController.swift which contains uitableview
extension HomeController : UITableViewDelegate {
func tableView(tableView: UITableView,willDisplayCell cell: UITableViewCell,forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? TableViewCell else { return }
//here setting the uitableview cell contains collectionview delgate conform to viewcontroller
tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row, andForSection: indexPath.section)
tableViewCell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
}
func tableView(tableView: UITableView,didEndDisplayingCell cell: UITableViewCell,forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? TableViewCell else { return }
storedOffsets[indexPath.row] = tableViewCell.collectionViewOffset
}
}
extension HomeController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView,numberOfItemsInSection section: Int) -> Int {
let element : [CollectionViewElement] = self.returnCollectionViewElementAccordingToIndex(collectionView.tag)
return element.count
}
func collectionView(collectionView: UICollectionView,cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell",forIndexPath: indexPath) as! horizontalCollectionViewCell
let element : [CollectionViewElement] = self.returnCollectionViewElementAccordingToIndex(collectionView.tag)
cell.cellTitleLabel.text = element[indexPath.row].videos.title
cell.cellGenerLabel.text = element[indexPath.row].videos.gener
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){
print("collectionviewtag:\(collectionView.tag) + indexpathrow:\(indexPath.row)")
//from here you can do push or present to anyview controller
// collectionviewtag is tableView cell row value and indexpathrow return collectionView cell row value.
}
}
TableViewCell.swift :custom UITableViewCell which contains collectionView
class TableViewCell: UITableViewCell {
#IBOutlet private weak var collectionView: UICollectionView!
#IBOutlet weak var cellLabel: UILabel!
#IBOutlet weak var cellButton: UIButton!
#IBAction func CellButtonActionTry(sender: UIButton) {
print("Dude \(cellButton.tag)")
}
var collectionViewOffset: CGFloat {
get {
return collectionView.contentOffset.x
}
set {
collectionView.contentOffset.x = newValue
}
}
func setCollectionViewDataSourceDelegate<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>
(dataSourceDelegate: D, forRow row: Int , andForSection section : Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row // tableView indexpathrow equals cell tag
collectionView.reloadData()
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
You can do it like this :
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print(indexPath.item)
let name = names[indexPath.item]
let distinationViewController = DistinationViewController()
distinationViewController.name = name
if let navVC: UINavigationController = UIApplication.sharedApplication().keyWindow?.rootViewController as? UINavigationController {
navVC.pushViewController(distinationViewController, animated: true)
}
}
This is just a way to do it i dont know which view you want to push or what your names array contains so kindly change those things accordingly.
get root navigationcontroller from uiapplication and perform push on it.

Resources