My each UICollectionViewCell class have a delete button:
#IBAction func hideSingleCampaign(sender: AnyObject) {
self.removeFromSuperview()
}
Once tapped, cell disappears, and UICollectionView is left with empty space. I have no UICollectionView reference from cell class so I can't use collectionView.reloadData()
How am I supposed to shift other cells up?
You need to remove the object from the array that you are using with your CollectionViewDataSource method instead of deleting cell, So first of all declare action of Button inside CollectionViewCell, so that add action of button in the cellForItemAtIndexPath like this.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath:NSIndexPath)->UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as CollectionCell
cell.delBtn.tag = indexPath.item
cell.delBtn.addTarget(self, action: #selector(self.onDeletTapped(_:)),
forControlEvents: .TouchUpInside)
return cell
}
Add this method onDeletTapped on your ViewController
func onDeletTapped(sender: UIButton) {
let index = sender.tag
yourArray.removeAtIndex(index)
self.collectinView.reloadData()
}
Related
I have a DynamicTableView and every Cell has an ImageView. How do I make an Action with knowing the indexPath.row?
The only way I know is to make a TapGestureRecognizer – that works but I then don't know which row I clicked.
So I need something like DidSelectRowAt from the TableView:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
This Action should only happen when I press the ImageView not the whole Row, therefore I can't use DidSelectRowAt from the TableView.
Thats what I did right now:
class DownloadsViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tap = UITapGestureRecognizer(target: self, action: #selector(DownloadsViewController.tappedMe))
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DownloadsViewCell
cell.fileInfo.image = UIImage(named: "detail1")
cell.fileInfo.addGestureRecognizer(tap)
cell.fileInfo.isUserInteractionEnabled = true
}
func tappedMe(){
print(arrayDocuments(IndexPath.row))
}
}
My whole DownloadViewContoller Class Code is here
My DownloadViewCell Screenshot is here
Does anyone have an Idea?
Thank you!
Add a property to your cell: var indexPath: IndexPath.
In cellForRowAtIndexPath: do cell.indexPath = indexPath
In your cell subclass add tapGestureRecognizer to UIImageView which triggers method in cell subclass.
Make a delegate protocol for your cell:
protocol YourCellDelegate: class {
func imagePressed(indexPath: IndexPath)
}
Make your ViewController adopt this protocol.
In your cell subclass add property weak var delegate: YourCellDelegate?
In cellForRowAtIndexPath: do cell.delegate = self
In cell tapRecognizer selector call delegate method delegate?.imagePressed(indexPath: indexPath)
In your viewController you'll receive imagePressed method call with indexPath as an argument.
Just set the ImageView like you had before, and then just add a blank Button over it and use the method from the Answer from Ilya V. with the IBAction
Did you try to get cell by
recognizer.view
on tap gesture method and
func indexPath(for cell: UITableViewCell) -> IndexPath?
returns an index path representing the row and section of a given table-view cell.
Here is a solution that uses a custom UIImageView subclass.
Create a subclass of UIImageView - MyImageView (or something else more appropriate)
Create a delegate for the subclass - MyImageViewDelegate
Add a delegate method called myImageViewTapped(imageView: MyImageView)
Override the touchesEnded method to call the delegate method
Add a property in MyImageView called indexPath that stores the index path of the cell that this image view is in.
In your table cells, use MyImageView instead of a regular UIImageView.
Set the delegate of the MyImageView to self i.e the VC.
Set isUserInteractionEnabled to true
In cellForRowAtIndexPath, set the image view's indexPath to the indexPath parameter
Now implement the delegate method and in the method, you will know which image view is tapped by accessing indexPath!
You can use property "tag" imageView to save indexPath.row and use it later in the selector
class DownloadsViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DownloadsViewCell
cell.fileInfo.image = UIImage(named: "detail1")
cell.fileInfo.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(tappedMe(_ :)))
cell.fileInfo.addGestureRecognizer(tap)
}
#objc func tappedMe(_ sender: AnyObject){
print("Tap to imageView in cell No. - \(sender.view.tag)")
}
}
I am attempting to programmatically create a button in each cell of a UICollectionView; however, only the first button is visible. I have tried adding print statements to see what subviews my cells have and the button is present but it is not appearing on the screen.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath)
// Configure the cell
let button = UIButton(frame: cell.frame)
button.addTarget(self, action: #selector(cellClicked), for: UIControlEvents.touchUpInside)
button.backgroundColor = UIColor.red
button.tag = indexPath.row
cell.addSubview(button)
print(cell.subviews)
return cell
}
Also, I added a print statement when clicking the buttons and only the first button shows up and prints out 0.
#IBAction func cellClicked(sender: UIButton) {
print(sender.tag)
}
Here is a screenshot of the collection view, there should be two buttons in the picture but only one appears
Any help is much appreciated.
It's very bad to add button in data source, because when cell reused, new buttons will be created. If you're using Interface Builder, please add button directly. And you can adjust their properties. You can also define a custom cell, and just CTRL-Drag an outlet. Or handle selection in collection view's delegate.
optional public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
Another solution is add button in cell's awakeFromNib(), this will be called only once.
I have an ImageView inside a CollectionViewCell. I want to be able to click the image and it take me to another ViewController. How would I do this? This is the code I have so far.
import UIKit
class FirstViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
var images = ["meal1", "photograph1", "meal1"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CollectionViewCell
//set images
cell.imageView.image = UIImage(named: images[indexPath.row])
return cell
}
}
As you said you want to detect image tap on collectionview cell please go through this code :
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.connected(_:)))
cell.yourImageView.isUserInteractionEnabled = true
cell.yourImageView.tag = indexPath.row
cell.yourImageView.addGestureRecognizer(tapGestureRecognizer)
And add below method to your ViewController
func connected(_ sender:AnyObject){
print("you tap image number : \(sender.view.tag)")
//Your code for navigate to another viewcontroller
}
Note - Make sure your user interection for cell image is enable
Add a tabGestureRecognizer to your imageview in collectionView "cellForItemAt" method, and in the method of recognizer tap call the segue to go to the desired viewcontroller.
Swift 5
CollectionView Function:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.tap(_:)))
cell.yourImg.isUserInteractionEnabled = true
cell.yourImg.tag = indexPath.row
cell.yourImg.addGestureRecognizer(tapGestureRecognizer)
return cell
}
Tap Function:
#IBAction func tap(_ sender:AnyObject){
print("ViewController tap() Clicked Item: \(sender.view.tag)")
}
You can either us a UIButton and set the image property on it with no title, or you can add a UIGestureRecognizer to the UIImageView. Either way, you'd just Present or Show the ViewController you want to display once the action has been received.
One thing I'll often do in this situation, is create a CollectionCellDelegate protocol that has a callback function (something like buttonPressed:forCollectionCell:), that I can have my CollectionView conform to, then set the delegate of each cell to the CollectionView. Then you can call up to the CollectionView when the button/image is pressed, and have the CollectionView handle whatever behaviour you want, in this case, presenting/pushing a new view controller.
I have a button inside a tableview cell (as shown in the below image), when I click the button,
didSelectRowAt indexpath
is not being triggered, could some one please suggest how I could do this ?
Please note:
I am performing a set of actions on click of the button, in addition I would also like
didselectRowAt indexPath
to be triggered.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Table view cell has been clicked")
}
When you have multiple views in the cell they may receive the touch event and not the "cell" so didSelectRowAt will not be triggered. In that case add view.isUserInteractionEnabled = false to each view, label or button.
Put the outlet to the button inside a custom cell class. Then set tag and add selector for button in cellForItemAt. This example is for colletionView just change to suit tableView.
If you want the button in each cell this is how you have to do it. Adding a static button to a single cell won't call didSelectItemAt because your tapping a button that doesn't have reference to a reusable cells indexPath.
This way we send the button.tag tot he function so we know which cell the button is related to.
class MyClassViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
.... // Stuff
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
....// Create Cell
cell.deleteCellButton.tag = indexPath.item
cell.deleteCellButton.addTarget(self, action: #selector(MyClassViewController.deleteCellButtonTapped(_:)), for: .touchUpInside)
return cell
}
func deleteCellButtonTapped(_ sender: Any) {
... // Stuff
print("Selector called")
}
}
If you are adding button or gesture on UITableViewCell , didselectRowAt indexPath not invoked . You can add button on remaining UI UITableViewCell than clear color apply on button . User didn't show button and you can perform didselectRowAt indexPath method task . If you wanna indexpath in button method code give below.
func btnChildDropDown(sender:UIButton)
{
let cell = sender.superview?.superview as! ChildAgeTableViewCell
let indexPath = self.tblViewAddRooms.indexPath(for: cell)
}
Right now I have a list of scrolling usernames using a Collection View of buttons. But I’d like to add overlapping delete buttons to each row. They'd need to be attached to the name buttons and scroll with them.
How can I add these buttons to my CollectionView?
(Also I'd like to skip the delete button on the first row for obvious reasons)
Current Code:
//Add the cells to collection
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: UsernameCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! UsernameCollectionViewCell
cell.usernameLabel.text = userNames [indexPath.row]
return cell
}
//Upon Selecting an item
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if (indexPath.row == 0){
self.performSegueWithIdentifier("newUserSegue", sender: self)
}
else {
sendData(userNames[indexPath.row])
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Got it working! Here's how:
I added a button to the cell in the Storyboard.
Connected an outlet to the UICollectionViewCell class.
Edited view controller code to:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: UsernameCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! UsernameCollectionViewCell
cell.usernameLabel.text = userNames [indexPath.row]
cell.deleteButton?.layer.setValue(indexPath.row, forKey: "index")
cell.deleteButton?.addTarget(self, action: "deleteUser:", forControlEvents: UIControlEvents.TouchUpInside)
// Remove the button from the first cell
if (indexPath.row == 0){
var close : UIButton = cell.viewWithTag(11) as! UIButton
close.hidden = true
}
return cell
}
func deleteUser(sender:UIButton) {
let i : Int = (sender.layer.valueForKey("index")) as! Int
userNames.removeAtIndex(i)
UserSelectCollection.reloadData()
}
Many thanks to JigarM for his examples on GitHub:
https://github.com/JigarM/UICollectionView-Swift
Why not create custom UICollectionViewCell in IB and just add button to it ?
Register it to your collectionView with :
- registerNib:forCellReuseIdentifier:
You can use delegate or notification to process button tap.