I have a UITableView with some data coming from a web service. It has multiple UILabels and UIImageView in every cell. How can I get labels and UIImageView from a cell and validate if they have valid data? I want to test it for first cell only
I am able to get the first cell instance as following but unable to get its child elements.
My Code to get first cell:
let app = XCUIApplication()
let myTable = app.tables.matching(identifier: "property_list_tableview")
let cell = myTable.cells.element(matching: .cell, identifier: "cell_0")
Now I want to extract labels and imageview from the cell. I have already added accessibility identifiers for labels and imageviews.
Use following to compare text for label value
let app = XCUIApplication()
let myTable = app.tables["property_list_tableview"]
XCTAssertEqual(myTable.cells["cell_0"].staticTexts["field_name"].label, "Text to compare")
You have an option to add accessibility labels as UI identifiers, and then you can easily select it and get its value.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
let = tableView.dequeueReusableCell(withIdentifier: "CustomCell")! as! CustomCell
cell.yourUIImage.image?.accessibilityLabel = "image " + String(indexPath.row)
}
then in UITests.swift
func testAreImagesLoaded() {
let image = "image "
XCTAssert(XCUIApplication().images[image + String(0)].exists)
XCTAssert(XCUIApplication().images[image + String(1)].exists)
XCTAssert(XCUIApplication().images[image + String(2)].exists)
}
you can access value for string example
let labelElement = XCUIApplication().staticTexts["myLabel"]
...
XCTAssertEqual(labelElement.value as! String, "your text")
but don't forget to add accessibility label for UILabel
see this link for more info https://medium.com/flawless-app-stories/automated-ui-testing-in-swift-ios-46e1c9993316
Related
I have a table view and 10 different prototype cell. I used storyboard and created custom UITableCell class for each cell.
There is a checkbox in CheckBoxCell. I created these checkboxes in loop according to options count.
Problem is that after I checked a checkbox, checkbox value changes but when I scroll up or down the table view, checkbox value changes with inital value.
I investigated some questions in stackoverflow. I live this problem, because of after every scroll dequeReusebleCell works and re-create the cell in the queue. I tried to use these solutions, but I cannot succeeded.
I am new for Swift and I don't know how can I solve this problem.
May someone tell me how can I solve this problem and what is the correct approach?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let object = surveyDetailArray.first!.elements[indexPath.row]
switch object.type {
case CellConfig.checkbox.rawValue:
let cell = tableView.dequeueReusableCell(withIdentifier: "CheckboxCell", for: indexPath) as! CheckboxCell
let object = surveyDetailArray.first!.elements[indexPath.row]
for label in object.options {
cell.checkbox = BEMCheckBox()
cell.checkbox.onAnimationType = .bounce
cell.checkbox.offAnimationType = .bounce
cell.checkbox.boxType = .square
cell.checkbox.onFillColor = .red
cell.checkbox.offFillColor = .white
cell.checkbox.onCheckColor = .white
cell.checkbox.delegate = self
cell.checkbox.tag = label.id
cell.contentView.addSubview(cell.checkbox)
}
return cell
}
In your problem the cell is dequeuing and is getting back to the old state, that's will obviously happen because of dequeReusableCell,
now for the solution, you should use a model to store the states of different checkboxes and on cellforRowAt
add the code for checkbox persistance according to the model, when you enable a checkbox,
change the value of the variable in the model also and leave rest it on your cellForRowAt code. I'm adding a small example for your understanding, hope it helps.
CODE
Struct ButtonsStates {
var isButtonEnabled : Bool = false
}
// In your ViewController use the above model for saving buttonValues
var buttonStates : [ButtonStates]? // initialize as many as the rows
// in cellForRowAt
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ExampleCell", for: indexPath) as? ExampleCell else { return fatalError("") }
// here if the cell is dequeued still when cell will again be visible then this condition will be checked
cell.customButton.isSelected = buttonStates[indexPath.row].isButtonEnabled
return cell
I need to add UILabels to UITableViewCell, but its dynamic, first cell can have 1 label, second can have 4 and I dont know before hand. So I tried this
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
let cell: ReviewTableViewCell = reviewTableView.dequeueReusableCell(withIdentifier: "Review", for: indexPath) as! ReviewTableViewCell
var reviewObj:Review!
reviewObj = reviewArray[(indexPath as NSIndexPath).row]
let viewsAdded = commentViewsAddedDict[indexPath.row]
if(viewsAdded == nil)
{
for comment in reviewObj.commentArray
{
let label1 = UILabel()
label1.text = “text1”
label1.textColor = UIColor(hexString: "#333333")
let label2 = UILabel()
label2.text = “text2”
label2.numberOfLines = 0
label2.sizeToFit()
label2.textColor = UIColor(hexString: "#666666")
let label3 = UILabel()
label3.text = "----------------------------------------------------------------------"
label3.textColor = UIColor(hexString: "#eeeeee")
cell.stackView1.addArrangedSubview(label1)
cell.stackView1.addArrangedSubview(label2)
cell.stackView1.addArrangedSubview(label3)
}
commentViewsAddedDict[indexPath.row] = true
}
return cell
}
But what happens, the previously added views are not removed and it again tries to add new views.
So I want to know, what is the efficient way to do this.
Secondly, where I am going wrong.
Regards
Ranjit
You are using commentViewsAddedDict to figure out whether rows have been added or not. But whether these labels were added or not is not a function of the row in the table, but rather of the cell, which is reused.
So, I'd would advise:
eliminate this commentViewsAddedDict logic; and
move the logic regarding how many labels have been added to ReviewTableViewCell.
So, you might end up with:
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
let cell = reviewTableView.dequeueReusableCell(withIdentifier: "Review", for: indexPath) as! ReviewTableViewCell
var reviewObject = reviewArray[(indexPath as NSIndexPath).row]
cell.updateLabels(for: reviewObject)
return cell
}
And in ReviewTableViewCell:
func updateLabels(for reviewObject: ReviewObjectType) {
// add label if needed
// update label `text` if needed
// remove any labels that need to be removed
}
It's a little hard to be specific on the logic in updateLabels as the provided code snippet in the question is unclear, but the basic idea is that the ReviewTableViewCell should keep track of whether its labels have been added or not, and on the basis of the reviewObject, decide whether it needs to add a label, update an existing label, or remove any labels not needed for this particular reviewObject. But all of this "label state" logic is a function of the cell (which can be reused), not of which row in the table to which the cell corresponds.
When i clicked the subview,it didn't trigger subview's action. But i have selected whole cell. How to fix it?
Here is my code.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("idcell", forIndexPath: indexPath) as UITableViewCell
let lblTitle : UILabel = cell.contentView.viewWithTag(101) as! UILabel
lblTitle.text = (deptId[indexPath.row] as? String)! + " " + (deptDesc[indexPath.row] as? String)!
var height:CGFloat = 40
if(indexPath.row == departmentSelectedRow){
for i in 0...deptProfile.count-1{
let label = UILabel(frame: CGRectMake(0,height,400,30))
label.targetForAction("sadasdd", withSender: nil)
height = height+40
label.text = ("ewrewrewre")
label.backgroundColor = UIColor.redColor()
cell.addSubview(label)
}
}
return cell
}
If you want to interact with something inside a cell (rather than select the whole cell) then you should use a button or a gesture recogniser. These are properly interactive elements that you can add a target and action to.
You should also change how you're using cells, specifically:
Use custom cell classes, so you can directly reference the cell elements instead of using viewWithTag which is bad practice
Use multiple different cell classes (and thus identifiers) so you can use actually different cells for selected and unselected rows
Don't create and add subviews on the fly, this is what the different cell classes are for
I do create a custom cell with 2 UIImageView inside and a UILabel this way:
let ChildCellID = "ChildCell"
if indexPath.section < 2 {
var cell = SectionCell.loadOrDequeueWithTableView(tableView, reuseIdentifier: SectionCellID)
if cell == nil {
cell = SectionCell.viewFromNib()
}
cell.delegate = self as SectionCellDelegate
cell.avatar?.loadAvatarURL(child.avatarUrl)
cell.avatar.layer.cornerRadius = cell.avatar.frame.size.width / 2
The attribut avatar is the UIImageView I decided to round. When a cell is selected my code goes:
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
let tableViewCell = tableView.cellForRowAtIndexPath(indexPath)
if let cell : SectionCell = tableViewCell as? SectionCell {
cell.selection = !cell.selection
} else if let cell : ChildCell = tableViewCell as? ChildCell {
cell.selection = !cell.selection
}
return indexPath
}
Selection is the checkbox as UIImageView. My problem is when I select a cell, the cell.avatar loses his property meaning it goes back as a square form. The cell.avatar is still loaded and I tried cell.avatar.layer.cornerRadius = cell.avatar.frame.size.width / 2in the willSelectRowAtIndexPath and didSelectRowAtIndexPath but without success.
Am I missing anything that makes my avatar loses his rounded property ?
The only thing that could possibly be the origin of this problem would be the fact that I do use the checkbox UIImageView which is really near my avatar.
I found out that the checkbox near my avatar lacked a constraints that, for some random reason and even though it did not impact the avatar view, was resetting the avatar view rounded property.
I'm putting an image and a text label in the same table cell. I want to make the text label followed by the image but the image comes first. Here is the code.
cell.textLabel?.text = "Text label comes here"
let image = UIImage(named: ChangeYTDArrow!)
var imageView = UIImageView(image: image!)
cell.imageView!.image = image
And here is what happens withe the code.
Thank you in advance.
You should probably use a custom tableViewCell in the interface builder, it allows you to everything you want in a tableViewCell like multiple UILabel or everything you want.
To do so, go in your storyboard, select your tableView then on the tableView property set the field Prototype Cells to 1, a new cell will appears and you will be able to design what you want.
Add an identifier to this cell then create a new class for your cell and add your #IBOutlet, here you should have multiple label & an UIImageView()
Then in your controller with textView method, at the cellForRowAtIndexPath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let reuseIdentifier = "yourCellIdentifier"
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as? yourTableViewCell ?? GolfCourseTableViewCell(style: .Default, reuseIdentifier:yourCellIdentifier)
cell.yourLabel1.text = "blahblah"
cell.yourLabel2.text = "blablahbl"
let image = UIImage(named: ChangeYTDArrow!)
var imageView = UIImageView(image: image!)
cell.imageView!.image = image
return cell
}