How to access the index of the selected cell in UICollectionView - ios

I have a few cells in my UICollectionView in which I create buttons:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath)
let button = UIButton(frame: CGRectMake(0, 0, cell.bounds.size.width, cell.bounds.size.height))
button.setTitle("ButtonTitle")
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
Then I add a action to the button which switches to a new ViewController setting the name of the button as title with:
func buttonAction(sender: AnyObject?) {
let vc = NewViewController()
vc.title = sender!.titleLabel!!.text
revealViewController().pushFrontViewController(vc, animated: true)
}
But I now also would like to access the index of the selected cell / button. Does anybody know how to do this?

do like
Step-1
button.tag = indexPath.row
button.setTitle("ButtonTitle")
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
Step-2
func buttonAction(sender: UIButton) {
if let sendertitle = sender.titleLabel?.text {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("NewViewControllerIdentifier") as? NewViewController
vc.title = sendertitle
revealViewController().pushFrontViewController(vc, animated: true)
}
}
Choice -- 2
if you want to implemnt the simple way do like
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath)
return cell
}
on didSelect call
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("NewViewControllerIdentifier") as? NewViewController
vc.indexPath = indexPath.row
revealViewController().pushFrontViewController(vc, animated: true)
}

In your buttonAction:
let btn = sender as! UIButton
let point = btn.convertPoint(btn.bounds.origin, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)

Just use this method :
let point = button.convertPoint(btn.bounds.origin, toView: self.collectionView)
let mypath = self.collectionView?.indexPathForItemAtPoint(point)
Use your button position to get the proper indexpath

If the entire cell is displaying a button maybe you should just remove the button and implement the following:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// Perform any action you want after a cell is tapped
// Access the selected cell's index with the indexPath.item value
let item = itemCollection[indexPath.item]
}

Related

How to double tap (instead of a single tap) a collection view cell in swift?

Right now, I have a bunch of messages in a collectionview cell.
My code to single tap the cell right now is
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Which cell: ", indexPath)
}
How do I make it such that this will only print if it is double tapped not single tapped?
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath) as! customCell
let tapCell = UITapGestureRecognizer(target: self, action: #selector(self.doubleTap(selectedIndex:)))
tapCell.numberOfTapsRequired=2
cell.tag=indexPath.row
cell.addGestureRecognizer(tapCell)
return cell
}
#objc func doubleTap(gesture: UITapGestureRecognizer){
print("Selected Index Is", gesture.view?.tag)
}
You can add UITapGestureRecognizer in collection view.
private var doubleTapGesture: UITapGestureRecognizer!
func setUpDoubleTap() {
doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(didDoubleTapCollectionView))
doubleTapGesture.numberOfTapsRequired = 2
collectionView.addGestureRecognizer(doubleTapGesture)
doubleTapGesture.delaysTouchesBegan = true
}
Call above method from your viewDidLoad as
override func viewDidLoad() {
super.viewDidLoad()
setUpDoubleTap()
}
Then add Gesture selector method in your class
#objc func didDoubleTapCollectionView() {
let pointInCollectionView = doubleTapGesture.location(in: collectionView)
if let selectedIndexPath = collectionView.indexPathForItem(at: pointInCollectionView) {
let selectedCell = collectionView.cellForItem(at: selectedIndexPath)
// Print double tapped cell's path
print("Which cell: ", selectedIndexPath.row)
print(" double tapped")
}
}
didDoubleTapCollectionView method will call only when you will double tap on collection view cell item.
I hope above example will solve your problem.

How to present a ViewController after pressing a button inside of a CollectionViewCell

Hey i'm new to programming and my problem is, i have a UICollectionViewController with 4 cells that are horizontal scrollable. Inside of the 4th cell i have a UIButton(optionsButton) on top of a UIView (ProfileContainerView).
The UIViewController I want to present is called ProfileEditViewController and is set up in Main.storyboard.
How can i present a UIViewController after pressing this button?
ProfileCell:
class ProfileCell: UICollectionViewCell {
let profileContainerView: UIView = {
let view = UIView()
return view
}()
lazy var optionsButton: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(#imageLiteral(resourceName: "Settings"), for: UIControlState.normal)
btn.addTarget(self, action: #selector(handleOptionsButton), for: UIControlEvents.touchUpInside)
return btn
}()
#objc func handleOptionsButton() {
print("Button pressed")
}
}
HomeViewController:
class HomeViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let profileCelId = "profileCell"
override func viewDidLoad() {
super.viewDidLoad()
setupSwipeView()
}
func setupSwipeView() {
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cell)
collectionView?.register(ProfileCell.self, forCellWithReuseIdentifier: profileCelId)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 3 {
return collectionView.dequeueReusableCell(withReuseIdentifier: profileCelId, for: indexPath)
}
return cell
}
}
You can use delegates to implement this.
Below is the code to implement this
protocol ProfileCollectionViewCellDelegate {
func buttonPressedAtIndexPath(inCell: ProfileCell)
}
class ProfileCell: UICollectionViewCell {
var delegate : ProfileCollectionViewCellDelegate?
let profileContainerView: UIView = {
let view = UIView()
return view
}()
lazy var optionsButton: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(#imageLiteral(resourceName: "Settings"), for: UIControlState.normal)
btn.addTarget(self, action: #selector(handleOptionsButton), for: UIControlEvents.touchUpInside)
return btn
}()
#objc func handleOptionsButton() {
if self.delegate != nil {
self.delegate?.buttonPressedAtIndexPath(self)
}
}
}
For your HomeViewController
class HomeViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, ProfileCollectionViewCellDelegate {
let profileCelId = "profileCell"
override func viewDidLoad() {
super.viewDidLoad()
setupSwipeView()
}
func setupSwipeView() {
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cell)
collectionView?.register(ProfileCell.self, forCellWithReuseIdentifier: profileCelId)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: profileCelId, for: indexPath)
cell.delegate = self
return cell
}
fun buttonPressedAtIndexPath(inCell: ProfileCell) {
let indexOfCell = self.collectionView.indexPath(for: cell)
if indexOfCell.row == 3 {
//Do your work here
}
}
}
You can present your ProfileEditViewController, which is styled in your Main.storyboard the following way:
1) Give your ProfileEditViewController a StoryBoard ID. E.g. "ProfileEditViewController" - Some question regarding this is here: What is a StoryBoard ID and how can i use this?
2) Register the UIViewController for the action on the UIButton or offer an appropriate callback functionality.
As your HomeViewController is also your Collection View's datasource, you can easily extend your DataSource method
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell`
Implementation could look like:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 3 {
let profileCell = collectionView.dequeueReusableCell(withReuseIdentifier: profileCelId, for: indexPath)
if let _cell = cell as? ProfileCell,
let button = _cell.optionsButton as? UIButton {
button.addTarget(self, action: #selector(handleOptionsButton), forControlEvents: UIControlEvents.TouchUpInside)
}
return profileCell;
}
return cell
}
Make sure that your buttons Action is now also being implemented by your HomeViewController
#objc func handleOptionsButton() {
print("Button pressed")
}
3) Now in HomeViewController.handleOptionsButton you need to provide a functionality to support the transition to that specific Controller with the desired StoryboardID:
let storyboard = UIStoryboard(name: "Main", bundle:Bundle.main)
let controller = storyboard.instantiateViewController(withIdentifier: "ProfileEditViewController")
self.present(controller, animated: true, completion: nil)
let proCell = ProfileCell()
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cell, for: indexPath)
if indexPath.item == 3 {
let profileCell = collectionView.dequeueReusableCell(withReuseIdentifier: profileCelId, for: indexPath)
let button = proCell.optionsButton
button.addTarget(self, action: #selector(handleOptionsButton), for: UIControlEvents.touchUpInside)
return profileCell;
}
return cell
}

CollectionView Custom Cell accessing atributes outside of the cell

I am trying to change some attributes of the custom view cell in my collection view outside of the cell, but I can not figure it out what I am doing wrong:
In cellForItem I have :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PatientProfileCell", for: indexPath) as! PatientQuestionCell
if Auth.auth().currentUser?.uid == userID {
cell.commentButton.setTitle("Edit post", for: .normal)
cell.commentButton.addTarget(self, action: #selector(editPostAction), for: .touchUpInside)
}
} else {
print("Not the current user for collection view")
}
}
Everything works perfectly here but when I do trigger the action nothing happens. This is the function :
//post actions
func editPostAction() {
print("Edit post Enabled")
let cell = PatientQuestionCell()
cell.questionView.isEditable = true
cell.questionView.isSelectable = true
cell.questionView.isScrollEnabled = true
cell.questionView.becomeFirstResponder()
}
How can I change the attributes of the cell with this function?
You can make use of the tag property of UIButton like this:
Inside your collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) method, add this line of code:
cell.commentButton.tag = (indexPath.section * 100) + indexPath.item
Then set the target of your button like this:
cell.commentButton.addTarget(self, action: #selector(editPostAction(sender:)), for: .touchUpInside)
Now, add a new method, which is your selector:
func editPostAction(sender: UIButton) {
let section = sender.tag / 100
let item = sender.tag % 100
let indexPath = IndexPath(item: item, section: section)
let cell = self.collectionView?.cellForItem(at: indexPath) as! PatientQuestionCell
cell.questionView.isEditable = true
cell.questionView.isSelectable = true
cell.questionView.isScrollEnabled = true
cell.questionView.becomeFirstResponder()
}

IndexPath in tablew view cell returned is wrong on click of a label in a UITableView

I have a label inside a table view cell.
On click of the label I want to segue to another view controller after retrieving the correct indexPath.
I have added a gestureRecognizer to the label. On click of the label it returns the wrong indexPath , it does not return the index Path of the cell in which the label is.
How can I solve this problem . Any help will be appreciated. Thank you
Following is my code
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
cell = tableView.dequeueReusableCell(withIdentifier: "ViewControllerCell", for: indexPath) as! TableCell
cell.name.text = feeds[indexPath.row].name
nameClicked()
return cell
}
func nameClicked(){
cell.name.isUserInteractionEnabled = true
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(TrendViewController.handleTap(gestureRecognizer:)))
cell.name.addGestureRecognizer(gestureRecognizer)
}
func handleTap(gestureRecognizer: UIGestureRecognizer) {
var touchPoint = cell.name.convert(CGPoint.zero, to: self.tableView)
var clickedLabelIndexPath = tableView.indexPathForRow(at: touchPoint)!
nameFromView = feeds[clickedLabelIndexPath.row].name
print("IndexPath at touch",clickedLabelIndexPath)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "profile") as! ProfileViewController
vc.clickedLabelIndexPath = clickedLabelIndexPath
vc.nameFromView = feeds[clickedLabelIndexPath.row].name
self.navigationController?.pushViewController(vc, animated: true)
}
You have declare cell as instance property in your class,so you are adding gesture to the same cell's label. So create new cell using dequeueReusableCell and changed your method nameClicked by adding argument of type cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//create new cell
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewControllerCell", for: indexPath) as! TableCell
cell.name.text = feeds[indexPath.row].name
nameClicked(cell)
return cell
}
func nameClicked(_ cell: TableCell){
cell.name.isUserInteractionEnabled = true
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(TrendViewController.handleTap(gestureRecognizer:)))
cell.name.addGestureRecognizer(gestureRecognizer)
}
Now change your handleTap method like this to get the correct indexPath.
func handleTap(_ gestureRecognizer: UIGestureRecognizer) {
let point = self.tableView.convert(CGPoint.zero, from: gestureRecognizer.view!)
if let indexPath = self. tableView.indexPathForRow(at: point) {
print("\(indexPath.section) \(indexPath.row)")
//Add your code here
}
}
Note: If your cell having only single cell then it is batter if you use didSelectRowAt method instead of adding gesture to label.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
}
Maybe you should make condition for check if cell is touched ?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewControllerCell", for: indexPath) as! TableCell
if cell.isTouched {
cell.setTouched
}
else {
cell.setNoTouched
}
return cell
}

Displaying checkmark on collectionview

I have a collection view where when each cell is tapped a larger version of the cell image pops up and disappears when tapped again. On top of this I'd like to be able to select a view in the corner of the cell that displays a blue checkmark(SSCheckMark View) or a greyed out checkmark when tapped again. My current code is:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as! PhotoCell
cell.backgroundColor = .clear
cell.imageView.image = UIImage(contentsOfFile: imagesURLArray[indexPath.row].path)
cell.checkmarkView.checkMarkStyle = .GrayedOut
cell.checkmarkView.tag = indexPath.row
cell.checkmarkView.checked = false
let tap = UITapGestureRecognizer(target: self, action: #selector(checkmarkWasTapped(_ :)))
cell.checkmarkView.addGestureRecognizer(tap)
return cell
}
func checkmarkWasTapped(_ sender: SSCheckMark) {
let indexPath = IndexPath(row: sender.tag, section: 1)
if sender.checked == true {
sender.checked = false
} else {
sender.checked = true
}
collectionView.reloadItems(at: [indexPath])
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
addZoomedImage(indexPath.row)
addGestureToImage()
addBackGroundView()
view.addSubview(selectedImage)
}
But when I run and select the checkmark view I get the error:
unrecognized selector sent to instance on the first line of checkmarkWasTapped() i can see that it doesn't like sender but i don't know why. Any help would be great.
UITapGestureRecognizer tap's sender is the gesture. The checkmarkWasTapped method definition is wrong. And you can get the checkmarView using sender.view. Try this.
func checkmarkWasTapped(_ sender: UIGestureRecognizer) {
let checkmarkView= sender.view as? SSCheckMark
let indexPath = IndexPath(row: checkmarkView.tag, section: 1)
if checkmarkView.checked == true {
checkmarkView.checked = false
} else {
checkmarkView.checked = true
}
collectionView.reloadItems(at: [indexPath])
}

Resources