My code is so complex so Im gonna minimalize it a little bit.
I have a tableviewController that has 2 cell and a button in view.(Button not in cell).
I'm changing button action according to selected cell :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0{
self.botButton.addTarget(self, action: #selector(self.showA), for: .touchUpInside)
else{
self.botButton.addTarget(self, action: #selector(self.showB), for: .touchUpInside)
}
botButton is my button outlet
This is my action buttons :
#objc func showA(){
let showParcelsViewController = self.storyboard?.instantiateViewController(withIdentifier: "showA") as! showAVC
self.navigationController?.pushViewController(showParcelsViewController, animated: true)
}
#objc func showB(){
let decribeland = self.storyboard?.instantiateViewController(withIdentifier: "showB") as! showBVC
self.navigationController?.pushViewController(decribeland, animated: true)
}
When page load, If I select a row and then tap button , Its perfecly worked.But , For example, If I select 1.row then change to selected row to 2.row and tap button , View pushes First row's
viewcontroller (showAVC) and then pushes Second row's viewcontroller (showBVC) quickly.
How can I fix it?
Target keeps on adding-up as you keep selecting the rows you need to remove the previous target when you add a new one:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
botButton.removeTarget(self, action: #selector(self.showB), for: .touchUpInside)
botButton.addTarget(self, action: #selector(self.showA), for: .touchUpInside)
} else {
botButton.removeTarget(self, action: #selector(self.showA), for: .touchUpInside)
botButton.addTarget(self, action: #selector(self.showB), for: .touchUpInside)
}
}
Here is another way.
Set the button tag to indexPath.row in didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.botButton.tag = indexPath.row
}
Then use the button tag to know which controller to present.
#IBAction func showBtnPressed(sender : UIButton) { //
let index = sender.tag
if index == 0 {
let showParcelsViewController = self.storyboard?.instantiateViewController(withIdentifier: "showA") as! showAVC
self.navigationController?.pushViewController(showParcelsViewController, animated: true)
} else {
let decribeland = self.storyboard?.instantiateViewController(withIdentifier: "showB") as! showBVC
self.navigationController?.pushViewController(decribeland, animated: true)
}
}
Related
i am creating images app from my Wordpress website's json using swift , i have created CollectionView and every cell displaying images and working fine but i want to add show comment in every cell for each post, its showing the exact comments for every post/cell but when i click on it it shows the comments of very first post of collection view. this is my code to show comments and for clickable button.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! homeViewCell
let url = dataArray[indexPath.row]["thumbnail_images"]["medium"]["url"].stringValue
cell.imageArticle.sd_setImage(with: NSURL(string: url) as URL?, placeholderImage:UIImage(named: "empty.png"))
cell.nametag.text = String(htmlEncodedString:dataArray[indexPath.row]["tags"][0]["title"].stringValue)
cell.nametagg.text = String(htmlEncodedString:dataArray[indexPath.row]["categories"][0]["title"].stringValue)
cell.fbButton.addTarget(self, action: #selector(homeController.fbClick), for: .touchUpInside)
cell.commentButton.addTarget(self, action: #selector(homeController.comment), for: .touchUpInside)
cell.commentButton.setTitle("\(dataArray[indexPath.row]["comment_count"].stringValue) comments", for: .normal)
cell.leaveComment.addTarget(self, action: #selector(homeController.leavecomment), for: .touchUpInside)
cell.contentline.text = String(htmlEncodedString:dataArray[indexPath.row]["excerpt"].stringValue)
return cell
}
and this is the code for button click
#objc func comment(_ sender: Any) {
let vc = CommentViewController()
vc.dataArray = dataArray[indexPath.row]["comments"].array
self.navigationController!.pushViewController(vc, animated: true)
}
i hope you understand my question, thanks
You've declared the indexPath as a global variable and it's value is NSIndexPath(row: 0, section: 0) as you said.
in comments(_:) function you've used indexPath.row but this row is 0 so it is first post's comments.
You don't need to set tapgesture to cell's button.
In homeViewCell you should create IBAction for the button and call closure when it triggered ->
class homeViewCell: UICollectionViewCell {
public var didTapComment: (() -> Void)?
#IBAction func didTapCommentButton(_ sender: UIButton) {
didTapComment?()
}
}
Set didTapComment's action in cellForItemAt like this ->
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! homeViewCell
cell.didTapComment = { [weak self] in
let vc = CommentViewController()
vc.dataArray = dataArray[indexPath.row]["comments"].array
self?.navigationController!.pushViewController(vc, animated: true)
}
let url = dataArray[indexPath.row]["thumbnail_images"]["medium"]["url"].stringValue
cell.imageArticle.sd_setImage(with: NSURL(string: url) as URL?, placeholderImage:UIImage(named: "empty.png"))
cell.nametag.text = String(htmlEncodedString:dataArray[indexPath.row]["tags"][0]["title"].stringValue)
cell.nametagg.text = String(htmlEncodedString:dataArray[indexPath.row]["categories"][0]["title"].stringValue)
cell.fbButton.addTarget(self, action: #selector(homeController.fbClick), for: .touchUpInside)
cell.commentButton.setTitle("\(dataArray[indexPath.row]["comment_count"].stringValue) comments", for: .normal)
cell.leaveComment.addTarget(self, action: #selector(homeController.leavecomment), for: .touchUpInside)
cell.contentline.text = String(htmlEncodedString:dataArray[indexPath.row]["excerpt"].stringValue)
return cell
}
}
Remove followings;
cell.commentButton.addTarget(self, action: #selector(homeController.comment), for: .touchUpInside) line from cellForItemAt
#objc func comment(_ sender: Any) function
let indexPath = NSIndexPath(row: 0, section: 0)
Simple as my question on title. I'm trying to go to another view controller depending on the image that someone tap on my table view. Eg: If you tapped on image1 perform segue gotoview1, if you tapped on image2 perform segue gotoview2.
I have an array of the images:
let gameImages = [UIImage(named: "DonkeyKong"), UIImage(named: "TRex"), UIImage(named: "SuperMarioRun"), UIImage(named: "Arcades1")]
and this is my cell for index, I tried to perfom the segue with the func imageAction but the app will crash:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
cell.frontImage.image = gameImages[indexPath.row]
cell.title.text = gameTitles[indexPath.row]
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "imageAction:")
cell.frontImage.isUserInteractionEnabled = true
cell.frontImage.addGestureRecognizer(tapGestureRecognizer)
func imageAction(_ sender:AnyObject) {
if cell.frontImage.image == UIImage(named: "DonkeyKong"){
performSegue(withIdentifier: "goToDonkey", sender: self)
}
}
return cell
}
I have a custom cell where I just linked the images as an outlet and perform some basic modifications. Just saying in case this matters.
try this instead.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
cell.imageButton.addTarget(self, action: #selector(self.imageAction(_:)), for: .touchUpInside)
cell.imageButton.tag = indexPath.row
cell.frontImage.image = gameImages[indexPath.row]
cell.title.text = gameTitles[indexPath.row]
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "imageAction:")
cell.frontImage.isUserInteractionEnabled = true
cell.frontImage.addGestureRecognizer(tapGestureRecognizer)
return cell
}
func imageAction(_ sender:UIButton) {
switch sender.tag{
case 0:
self.performSegue(withIdentifier: "goToDonkey", sender: self)
case 1:
performSegue(withIdentifier: "goToTRex", sender: self)
case 2:
performSegue(withIdentifier: "goToSuperMarioRun", sender: self)
case 3:
performSegue(withIdentifier: "goToArcades1", sender: self)
default:
break
}
}
imageAction function is not member of self since is part o a tableview delegate function not self. that's why the unrecognized selector instance.
but i maybe rewrite the func using another delegate, but since you dnt want to use the cell didselect only the image this may solve your problem.
I have table view cells like quiz. And in each cell I have a buttons And how can I identify in which cell button was pressed. Maybe by IndexPath???
This is how I connected button to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionCell")!
variant1 = cell.contentView.viewWithTag(1) as! UIButton
variant2 = cell.contentView.viewWithTag(2) as! UIButton
variant3 = cell.contentView.viewWithTag(3) as! UIButton
variant4 = cell.contentView.viewWithTag(4) as! UIButton
variant1.addTarget(self, action: #selector(self.variant1ButtonPressed), for: .touchUpInside)
variant2.addTarget(self, action: #selector(self.variant2ButtonPressed), for: .touchUpInside)
variant3.addTarget(self, action: #selector(self.variant3ButtonPressed), for: .touchUpInside)
variant4.addTarget(self, action: #selector(self.variant4ButtonPressed), for: .touchUpInside)
return cell
}
func variant1ButtonPressed() {
print("Variant1")
variant1.backgroundColor = UIColor.green
}
func variant2ButtonPressed() {
print("Variant2")
variant2.backgroundColor = UIColor.green
}
func variant3ButtonPressed() {
print("Variant3")
variant3.backgroundColor = UIColor.green
}
func variant4ButtonPressed() {
print("Variant4")
variant4.backgroundColor = UIColor.green
}
This is how it looks like in Storyboard:
You should use delegate pattern, basic example:
protocol MyCellDelegate {
func didTapButtonInside(cell: MyCell)
}
class MyCell: UITableViewCell {
weak var delegate: MyCellDelegate?
func buttonTapAction() {
delegate?.didTapButtonInside(cell: self)
}
}
class ViewController: MyCellDelegate {
let tableView: UITableView
func didTapButtonInside(cell: MyCell) {
if let indexPath = tableView.indexPath(for: cell) {
print("User did tap cell with index: \(indexPath.row)")
}
}
}
Use this line to get indexPath, Where you have to pass UIButton on target selector
func buttonTapped(_ sender:AnyObject) {
let buttonPosition:CGPoint = sender.convert(CGPointZero, to:self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}
Since actions need to be inside the view controller, ctrl + drag from your button to the view controller - this will use the responder chain.
Basically you need to convert the view (button) to the coordinate system of the table view in order to tell what is the IndexPath and if you have the IndexPath you have the object that corresponds to the button inside the cell that was tapped:
#IBAction func buttonTapped(_ sender: Any) {
if let indexPath = indexPath(of: sender) {
// Your implementation...
}
}
private func indexPath(of element:Any) -> IndexPath? {
if let view = element as? UIView {
// Converting to table view coordinate system
let pos = view.convert(CGPoint.zero, to: self.tableView)
// Getting the index path according to the converted position
return tableView.indexPathForRow(at: pos) as? IndexPath
}
return nil
}
It is important to mention that there many solutions for your question. But you should know that in Apple's sample projects they also use this technic.
This is how you add tag to a UIButton inside UITableView, add below lines of code in
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
cell.yourButton.tag = indexPath.row
cell.yourButton.addTarget(self, action:#selector(btnPressed(sender:)), for: .touchUpInside)
Add this function in your ViewController
func btnPressed(sender: UIButton)
{
print("Button tag \(sender.tag)")
}
Hope this helps...
Simple Subclass button just like JSIndexButton
class JSIndexButton : UIButton {
var indexPath : IndexPath!
}
Now at cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ItemCell
let itemCategory = dataList[button.indexPath.section];
let item = itemCategory.items[button.indexPath.row];
cell.imgView.setImageWithURL(item.photoUrl);
cell.btnBuy.indexPath = indexPath;
cell.btnBuy.addTarget(self, action: #selector(JSCollapsableTableView.btnBuyPressed(_:)), for: UIControlEvents.touchUpInside)
return cell;
}
Check Button Action
#IBAction func btnBuyPressed(_ button: JSIndexButton) {
let itemCategory = dataList[button.indexPath.section];
let item = itemCategory.items[button.indexPath.row];
}
#objc func ItemsDescription(_ sender: UIButton?,event: AnyObject?) {
let touches: Set<UITouch>
touches = (event?.allTouches!)!
let touch:UITouch = (touches.first)!
let touchPosition:CGPoint = touch.location(in: self.tableView)
let indexPath:NSIndexPath = self.tableView.indexPathForRow(at: touchPosition)! as NSIndexPath
}
adding target
cell.ItemsDescription.addTarget(self, action: #selector(ItemsDescription(_:event:)), for: UIControlEvents.touchUpInside)
everyone, I am trying to add gesture recognizer to StackView which located on TableViewCell with that code and it doesn't work:
#IBOutlet var categoryStackView: UIStackView! {
didSet {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(categoryStackViewTapped))
categoryStackView.addGestureRecognizer(tapGesture)
}
}
And it doesn't work, I checked StackView and it is enable for user interactive
#objc func categoryStackViewTapped(sender: UITapGestureRecognizer) {
print("Here we are")
}
I am suggesting another approach if you have multiple stackView in your tableView. Add the current index row as tag to your , and then add click action to your button handler function.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell
cell.stackView.tag = indexPath.row
cell.stackView.addTarget(self, action:#selector(categoryStackViewTapped(sender:)), for: .touchUpInside)
return cell
}
Then in your handler function, you can get the correct index path by reading this tag
func categoryStackViewTapped(sender: UIStackView) {
let myIndexPath = IndexPath(row: sender.tag, section: 0)
//... other code here
}
Button inside UITableViewCell don't respond to setImage method inside its clicked method. Here is the cellForRowAt section for TableView.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=tableView.dequeueReusableCell(withIdentifier: "BroadcastViewCell", for: indexPath) as!BroadcastViewCell
var show=self.week?[selectedIndex].shows?[indexPath.item]
cell.myLabel.text=show?.programName
if let resim=show?.thumbURL{
cell.myImage.downloadImage(from: resim)
}
cell.notifyButton.tag = indexPath.row
cell.notifyButton.setImage( UIImage(named:"icnAlarm"), for: .normal)
cell.notifyButton.setImage( UIImage(named:"icnAlarmCheck"), for: .selected)
if (self.notifications?.contains(where: {$0.identifier == (show?.airDate)!+(show?.airTime)!}))!
{
cell.notifyButton.isSelected=true
}else
{
cell.notifyButton.isSelected=false
}
cell.notifyButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
return cell;
}
And I just set its selected state inside the buttonClicked implementation.
func buttonClicked(sender:UIButton)
{
sender.isSelected=true
}
But the image doesn't change after it. It only changes after cellForRowAt method invocation for its particular row. I don't understand why it responds differently to the same code part. Thanks for any help.
You have to tell the button what to do:
func buttonClicked(sender:UIButton)
{
if myButton.isSelected {
myButton.isSelected = false
}else{
myButton.isSelected = true
}
}
configure your button to be custom:
let myButton = UIButton(type: .custom)
Or in Attributes Inspector: