What I'm facing is I've developed code in Swift. It has a some custom cells and UIButtons in them. The buttons are IBOutlet Connections with properties in their respective Cells and actions against them are bound in the same classes. Now the Question is that it works perfect in iOS 8. But I don't know what's wrong with iOS 7 whenever I touch the button, the complete cell is tapped and Tableview's didSelectRowAtIndexPath: is called. What I've done so far is
Check .userInteractionEabled for parent views and cell and changing it's values
Remove buttons and re add them as found on different forums but no luck
Checked Tableview's delegate methods numberOfSections, numberOfRowsInsection: and heightForRowAtIndexPath: and changed their return types to NSInteger and CGFloat as found on stackoverflow Against a Question.
Has anyone else faced this issue and found solution so far?
Edit
Checked by reseting target action of UIButton after setting values to cell. No Luck
I have tested your problem. Its running fine for me. Please see my code below.
In TestTableViewController.swift
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as CustomCell
// Configure the cell...
cell.testButton.addTarget(cell, action: "testButtonClicked", forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
NSLog("didSelectRowAtIndexPath called")
}
In CustomCell.swift
class CustomCell: UITableViewCell {
#IBOutlet var testButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func testButtonClicked() {
NSLog("Button Clicked")
}
}
When I clicked on the button it logs "Button Clicked" and when clicked on cell it logs "didSelectRowAtIndexPath called". You might be doing something wrong in reset() function or second last function.
I have done the code below in my app and it is working fine.
Do not try to code on your custom tableview cell.
You can get values of that cell in your viewcontroller.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row==0)
{
static NSString *simpleprice = #"Price";
price = (PriceCell *)[tableView dequeueReusableCellWithIdentifier:simpleprice];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PriceCell" owner:self options:nil];
price = [nib objectAtIndex:0];
[price.btnPrice addTarget:self action:#selector(buyPressed) forControlEvents:UIControlEventTouchUpInside];
}
return price;
}
Hope this helps you. This works for ios7 and ios8 also.
Thanks...
i have faced such issue after long time digging it, i found root cause and it was in Cell XIB
what i did as Before:
i) First i have added CustomCell .h and .m.
ii) After then i have added XIB by new file option, you have noticed,
there is option like "View" that's i have selected and add in to the bundle. Bind it with my CustomCell.h.
Solution :
i) Add XIB by new file option, Choose View in user interface.
ii) In XIB you can see view at left panel. delete that view.
iii) Drag and drop there UITableViewCell from User Interface panel. After complete this steps XIB would be seen similar something like below image.
Change your cell.button Action target to self, and copy your button action method into viewController class (.mm) file where the UITableView is available.
Related
I'm just making a simple project for fun but I am having a very strange bug in my program.
This function returns 29 which is correct however it is called three times when my table view is instantiated.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
print("ingredient count: \(Sushi.ingredients.count)")
return Sushi.ingredients.count
}
And I think that (_: numberOfRowsInSection:) being called 3 times is what leads this code to execute almost three times the number of elements in the ingredients array.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
guard let cell = tableView.dequeueReusableCell(withIdentifier: "IngredientCell", for: indexPath) as? IngredientCell
else
{
return UITableViewCell(style: .default, reuseIdentifier: nil)
}
cell.ingredientLabel.text = Sushi.ingredients[indexPath.row]
print("ingredient label text: \(cell.ingredientLabel.text)\nindex path: \(indexPath)\ncell index: \(cell.index)\nbutton tag: \(cell.selectButton.tag)\nindex path row: \(indexPath.row)\ncell object: \(cell)\n")
return cell
}
The table view is populated appropriately as far as it lists all the ingredients in the array correctly. However, each row of the tableView is a prototype cell that has a UILabel, a UIButton, and a UIImage. When the button is pressed the text and color change for that button but also for every thirteenth button in the tableview cells.
This class is for my individual cells
class IngredientCell: UITableViewCell
{
public var userSelected = false
public var index: Int = 0
#IBOutlet weak var picture: UIImageView!
#IBOutlet weak var selectButton: UIButton!
#IBOutlet weak var ingredientLabel: UILabel!
//This method is called when a button is pressed
#IBAction func selectIngredient(_ sender: UIButton)
{
if userSelected == false
{
userSelected = true
selectButton.setTitleColor(.red, for: .normal)
selectButton.setTitle("Remove", for: .normal)
}
else
{
userSelected = false
selectButton.setTitle("Select", for: .normal)
selectButton.setTitleColor(.blue, for: .normal)
}
}
}
I decided to debug using print statements to see if I could figure out what exactly is going on and I found that numberOfRowsInSection is called 3 times and cellForRowAtIndexPath is called a varying amount of times. I keeps iterating over the table view giving the cell objects the same memory addresses which I suppose is what causes multiple buttons to change when only one is pressed. My console output proved that different buttons in the storyboard had the same addresses in memory.
Sorry, I can only write in Objective-C. But anyway, I am just going to show you the idea.
One of the ways to update the UI contents of just a single item of a table is to reload that specific cell.
For example: Your cell contains just a button. When you press a specific button you want to change the title of that button only. Then all you have to do is to create an dictionary which references all the titles of all buttons in your table, and then set the title to that button upon reload of that specific cell.
You can do it this way:
#interface ViewController ()<UITableViewDelegate, UITableViewDataSource>
#property NSMutableDictionary *buttonTitlesDict;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
buttonTitles = [[NSMutableDictionary alloc] init];
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"SimpleTableItem";
UITableViewCell *tableCell = [tableView dequeueReusableCellWithIdentifier:identifier];
if(tableCell == nil)
{
tableCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
tableCell.button.title = [_buttonTitles objectForKey:#(indexPath.row)];
tableCell.button.tag = indexPath.row;
}
//reloads only a specific cell
- (IBAction)updateButton:(id)sender
{
NSInteger row = [sender tag];
[_buttonTitlesDict setObject:#"changedTitle" forKey:#(row)];
[_tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:row inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
}
As you can see, I just created a dictionary that holds the button title for each button with the index of the cell as the reference key. Every time I want to update the title of the button, I will get that title from the button every time the cell is loaded upon cellForRowAtIndexPath. take note that when you scroll the table, cellForRowAtIndexPath will be called when new cells appear in front of you. So as you scroll, the correct titles will be updated to the buttons.
Hope this helps.
I have an uitableview with a custom cell which gets data from the array.
Custom cell has an uilabel and an uibutton (which is not visible until the uilabel text or the array object which loads for the text - is nil).
On launch everything is fine. When i press the uibutton the array is being appended, the new cells are being inserted below the cell.
But when i scroll - all of a sudden the uibutton appears on other cells where this conditional uilabel text isEmpty is not implied.
Here is how the whole process looks like
Here is my code for cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! TblCell
cell.lblCarName.text = someTagsArray[indexPath.row]
if let text = cell.lblCarName.text where text.isEmpty {
cell.act1.hidden = false
} else {
println("Failed")
}
cell.act1.setTitle(answersdict[answersdict.endIndex - 2], forState:UIControlState.Normal)
cell.act2.setTitle(answersdict.last, forState:UIControlState.Normal)
return cell
}
So my general question is how do i stop the reuse of those custom cells?
As far as i'm aware there is no direct way of doing this on reusablecellswithidentifier in swift, but maybe there are some workarounds on that issue?
When a cell is reused, it still has the old values from its previous use.
You have to prepare it for reuse by resetting that flag which showed your hidden control.
You can do this either in tableView:cellForRowAtIndexPath: or the cell's prepareForReuse method.
Update:
Here's an example you can add for TblCell:
override func prepareForReuse()
{
super.prepareForReuse()
// Reset the cell for new row's data
self.act1.hidden = true
}
I'm trying to create an autocompleter using iOS 8, Swift and Xcode 6.3
I have a problem that I'm trying to solve, but I gave up... I hope someone can help here. The problem is that (custom) UITableViewCell's are not displaying when the initial dataSource is empty. When adding data to datasource and reloading the tableView, the cells SHOULD display, but they don't... At least, the first time they don't... A second time, they DO... When I initialize the table with non-empty data, the problem doesn't occur. I guess something goes wrong with dequeueReusableCellWithIdentifier. In beginning, no reusable cells are found, or something. But I don't know why...
Relevant code, in ViewController.swift:
// filteredWords is a [String] with zero or more items
#IBAction func editingChanged(sender: UITextField) {
autocompleteTableView.hidden = sender.text.isEmpty
filteredWords = dataManager.getFilteredWords(sender.text)
refreshUI()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! AutocompleteTableViewCell
cell.title.text = filteredWords[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredWords.count
}
func refreshUI() {
self.autocompleteTableView.reloadData()
}
I created a sample project on github:
https://github.com/dirkpostma/swift-autocomplete
And a movie on YoutTube to show what goes wrong:
https://www.youtube.com/watch?v=ByMsy4AaHYI
Can anyone look at it and spot the bug...?
Thanks in advance!
You've accidentally hidden your cell.
Open Main.storyboard
Select Cell
Uncheck Hidden
Side note: As for why it's displaying the second time around with the cell hidden? It appears to be a bug. It should still be hidden (print cell.hidden, notice it's always true despite showing the text on the screen).
I think you need to change your code. Check out below code. It is because if you remember in Objective C you needed to check if the Cell was nil and then initialise it. The reuse identifier is usually reusing an already created cell, but on the first launch this does not work because there is no Cell to use. Your current code assumes always that the cell is created (re-used) because you are using ! in the declaration, so if you use the optional (?) it can be null and you then can create the cell
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? AutocompleteTableViewCell
if cell == nil
{
//You should replace this with your initialisation of custom cell
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")
}
cell.title.text = filteredWords[indexPath.row]
return cell
I have a button in a custom cell
class CardTableViewCell: UITableViewCell {
#IBOutlet weak var detailsButton: UIButton!
}
in my view controller then I do this
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//.....more code....
openDealCell.detailsButton.addTarget(self, action: "push:", forControlEvents: UIControlEvents.TouchUpInside)
}
and then:
func push(sender:AnyObject) {
println("tap")
}
But when I clicked the button nothing happens.
What I am doing wrong?
Fix it! The problem was not in the code but in my cell in the storyboard:
where user interaction for the cell wasn't enabled and all the other view didn't allow the user interaction.
Right now I created demo project with exactly the same code as your and it works. Please check here at GitHub. The issue definitely in the other stuff that you didn't show us, that code does not have any problems
I have a custom UITableViewCell subclass and its associated xib. I have a UILabel and a UIButton in this cell and I have wired the touch up inside action of the button to the subclass.
What I need is when that button in the cell is tapped, to get the indexpath of the cell which has that button. And maybe send it back to the view controller via a delegate or something.
Since I'm inside a UITableViewCell subclass, I can't use a solution like this because I don't have a reference to the tableview from inside the cell subclass. Upon further investigation I found another solution and I implemented it in Swift like this.
import UIKit
class ContactCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
selectionStyle = .None
}
#IBAction func callButtonPressed(sender: UIButton) {
let indexPath = (self.superview as UITableView).indexPathForCell(self)
println("indexPath?.row")
}
}
But when I tap on the button, it crashes with an error message saying Swift dynamic cast failed.
Any idea what's wrong with my code?
Or I'm open to any other suggestions which would allow me to achieve the desired result in any other way.
Thank you.
Sounds like you need a delegate:
Delegates in swift?
Then just pass the cell itself as a parameter to the delegate, and then you can easily do tableView.indexPathForCell(cellFromDelegateMethod)
Hey you can use "Tag" of the button also.Inside the cellForRowAt method of table delegate u can tag the button with Indexpath.row . here is the example what i m tried to say.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// get ur cell nib .As it has a button
cell.startOrConntinuBtn.addTarget(self, action: #selector(sumbitOrContinue), for: .touchUpInside)
cell.startOrConntinuBtn.tag = indexPath.row }
and in the touch method "sumbitOrContinue" -
func sumbitOrContinue(sender: UIButton!) {
let tag = sender.tag
// do what you want to do like this example
let detail = self.detailList[tag]
let vc = self.storyboard?.instantiateViewController(withIdentifier: "mockExamInt") as! MockWindowVc
vc.detailId = detail.id
self.navigationController?.pushViewController(vc, animated: true)}
UIButton.Type really does not have member superview, but sender have
var cell: UITableViewCell = sender.superview.superview as UITableViewCell