I am new on Xcode + ios testing. I am going to automate ui of an existing ios project in swift. The problem is that I am unable to find table view of a view. Here is my code which i used to finding a cell from table view but this is not working in this case:
XCUIApplication().tables.cells.allElementsBoundByIndex[1].tap()
I am attaching screenshot of the view flow. The ios code of view is not written by me.
So you want to find a tableView's cell and then tap on it.
This solution is posted after testing in Xcode 9.0 beta 6, using Swift 4
As mentioned by Oletha in a comment, it's a good idea to understand the hierarchy of your view by printing the debugDescription:
let app = XCUIApplication()
override func setUp() {
super.setUp()
continueAfterFailure = false
app.launch()
print(app.debugDescription)
}
The print statement will give you a detailed list of views and their hierarchy.
Next, you should add an accessibilityIdentifier to your tableview and cell as follows:
a) Inside your viewDidLoad() or any other relevant function of the controller-
myTableView.accessibilityIdentifier = “myUniqueTableViewIdentifier”
b) Inside your tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) function-
cell.accessibilityIdentifier = "myCell_\(indexPath.row)"
Once done, you can write the following code in your test case to get the desired results:
let myTable = app.tables.matching(identifier: "myUniqueTableViewIdentifier")
let cell = myTable.cells.element(matching: .cell, identifier: "myCell_0")
cell.tap()
Note that this will simply help you find and tap the required cell, you can add some XCTAssert checks inside your test case to make it more useful.
P.S.: There are many guides online which can help you learn the UI testing with Xcode. e.g.: https://blog.metova.com/guide-xcode-ui-test/
Objective-C version of Kushal's answer. Last step is more simplified, though.
A- Where you define your table (usually in the viewDidLoad of its viewController), define its accessibilityIdentifier:
_myTable.accessibilityIdentifier = #"MyOptionsTable";
B- In cellForRowAtIndexPath, give each cell an accessibilityIdentifier:
cell.accessibilityIdentifier = #"MySpecialCell";
C- In your test case, get the table, then tap on the cell:
XCUIApplication *app = [[XCUIApplication alloc] init];
XCUIElement *table = app.tables[#"MyOptionsTable"];
XCUIElement *cell = table.cells[#"MySpecialCell"];
[cell tap];
First set cell accessibilityIdentifier:
cell.accessibilityIdentifier = "Cell_\(indexPath.row)"
Get cell by identifier, then tap cell:
let cell = app.cells.element(matching: .cell, identifier: "Cell_0")
cell.tap()
If you have the cell's accessibilityIdentifier in place, the following straight-forward solution works well for me:
app.cells["your_accessibility_identifer"].tap()
The most voted answer doesn't work for me, with the table view access identifier and the table view cell identifier. It finds the table view at first, then when it comes to finding the cell it simply fails.
Source: 1.
Related
I'm the beginner of UITests. Currently, I have an issue that I can not access element inside collectionViewCell.
My UI elements structure:
UIViewController
UITableView
-- UITableViewCell
---> UICollectionView
----> UICollectionViewCell
-----> The element that I would like to get.
I've tried with the code below:
let tableCellContainer = app.tables["HomeRVMPTableView"].cells.element(boundBy: 0) // Get the first Cell of TableView
let collectionContainer = tableCellContainer.collectionViews["ContainerItemCollectionView"].cells.element(boundBy: 1) // Get the second Cell inside collectionView
let collectionElement = collectionContainer.staticTexts["BuyCashCard"]
XCTAssertFalse(!collectionElement.exists) // FAILED
Is there any way to access collectionViewCell/tableViewCell easier? I'm so tired when start working UITests with that.
Thank you guys for helping me, I just found another way to access the collection view inside the tableview.
Here is my code:
let tableCellContainer = app.tables["MainTableView"].cells.element(boundBy: 1)
let cell = tableCellContainer.staticTexts["Brazil"]
cell.tap()
XCTAssert(cell.exists)
Hope this will help another guy who has the same issue like me.
For that line to fail, the collectionElement must actually exist. Replace your failing line with XCTAssert(collectionElement.exists)
XCTAssertFalse fails when the condition is true. XCTAssert (equivalent to XCTAssertTrue) succeeds when the condition is true.
I am using Xcode Version 8.2.1 (8C1002) - and I believe therefore Swift 3 - to try to make a table view with cells other than default, and can't seem to get the cell detailTextLabel or cell imageView to be other than nil.
I have set the Deployment Target of the project to 9.0 on Universal Devices.
In the interface builder, I have established Content as Dynamic Prototypes with 1 Prototype Cell.
In the interface builder, I have set that Table View Cell Style as “Right Detail” with an image “animal08.png” and an Identifier of “PlayerCell”.
In the view controller for the table view, in the override func viewDidLoad():
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "PlayerCell")
and
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell")!
print(cell.subviews.count)
cell.textLabel!.text = "Name: " + myNames[indexPath.row]
cell.detailTextLabel!.text = "Ranking: " + myRankings[indexPath.row].description
let avatarIndex = indexPath.row + 1
let avatarIndexName = "animal0" + avatarIndex.description
cell.imageView!.image = UIImage.init(named: avatarIndexName)
return cell;
}
When I run the program on an iPhone 5 iOS 5.2(13C75) simulator, when I get to the “cell.detailTextLabel!.text statement, I get:
“fatal error: unexpectedly found nil while unwrapping an Optional value”
Similarly, the attempt to access the imageView property finds nil.
Sure enough, the print(cell.subviews.count) statement reveals that there is only 1 subview of the cell.
I can see no set or get UITableViewCellStyle property of the UITableViewCell other than through the interface builder, or in the init(style: UITableViewCellStyle, reuseIdentifier: String?) initializer.
I believe using the init method defeats the benefits of dequeueReusableCell, and should not be used in this circumstance.
I have read (without substantiation) that the tableview.register method resets the tableViewCellStyle to default.
The above code seems to be identical to boilerplate code on the Apple Developer site and numerous tutorials by a wide range of contributors.
So, how can I access the detailTextLabel and imageView subviews and still use dequeued cells?
You should change the cell style to a value other than UITableViewCellStyleDefault. Unwrapping isn't necessary as well in this situation AFAIK.
Regarding the other stuff you try:
By the way printing cell.subviews.count won't give you any usable value as all subviews in UITableViewCell should be added (and default subviews are) inside its -contentView (cell property).
Also using cells initialised using -initWithStyle:reuseIdentifier: has no extra drawbacks as you may still use the reusing mechanism as well as specify the required cell style at the same time. Dequeuing actually has more sense when reusing Storyboard-defined cells with attributes specified in the Interface Builder.
I've been learning table views from tutorials on YouTube. I was following every step told in the video, and did everything the same as the author of this tutorial, but for some reason I got a strange error - I've heard that it's about old version of Xcode used in the tutorial - I'm working on the latest one.
Debugger tells that "CustomCell" is undeclared.
Every help will be appreciated!
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayOfCwiczenia.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomCell = tableView.dequeueReusableCellWithIdentifier("Cell") as CustomCell
let person = arrayOfCwiczenia[indexPath.row]
cell.setCell(cwiczenia.nazwa, imageName: cwiczenia.obrazek)
return cell
}
CustomCell looks to be a subclass of UITableViewCell
Looks like they left out the part where you need to create it
Create a new file called CustomCell and make sure it's base class is UITableViewCell
You must have a file where you define the behaviour of the custom cell - if that's not called 'CustomCell' then it won't be declared.
Just make sure that you have the same name when you define the class and when you use it.
I would suggest looking at Apple's walkthrough on how to implement a table view. It has step by step instructions with pictures. If you scroll down the the Design Custom Table Cells section I think you will be able to see how to link the custom cell to a file properly
Your tutorial should have mentioned all the details, but here it goes...
You need to define a subclass of UITableViewCell named CustomCell:
import UIKit
class CustomCell: UITableViewCell
{
// Your custom properties/methods.outlets/etc.
}
(typically, in a source file named CustomCell.swift, but this is not compulsory)
If you are using a storyboard, you need to set the class of your prototype cells to "CustomCell" in the identity inspector (third tab from the left on the right inspector pane - the one with the icon that looks like a newspapaer):
Also, In the attributes inspector (fourth tab from the right, icon looks like a slider), set the cell's identifier (in the case of your code, "Cell"):
If you are not using a storyboard, you need instead to register the custom cell class and identifier programmatically:
func viewDidLoad()
{
tableView.registerClass(CustomCell.self, forCellReuseIdentifier: "Cell")
// other setup...
}
I had this same error. Tried cleaning and building which worked but the main issue seemed to just be saving the CustomCell which then becomes recognised by the compiler and removes the error.
Its not limited to cells I've had it with other custom classes before as well. Good one to know about though!
I'm following tutorial from raywenderlich (How To Make A Swipeable Table View Cell With Actions) site, that shows you how to create custom cell with layers and delegate.
Now I got everything working correctly, buy I would like one of my cells to close if other cell open, how can I achieve this? or Like Messenger app, don't allow users to open another cell option unless they close the current one.
I can't wrap my head around this, I see few other people also ask the same question in comments, but no one reply.
Anyone with Objective-C knowledge, it's okay, I can translate it to swift myself.
The reason I'm using this tutorial, is because the Apple API doesn't allow custom button (using PaintCode) to be used as Action button.
I find a very simple solution for anyone else who trying to achieve the same method.
Create a Method - closeOtherCells:
Since we store all cells in NSMutableSet we can see which ones are available for closing.
func closeOtherCells(close close: Bool){
if close{
//Check For Available Cell
for cells in cellsCurrentEditing {
//Get Table Cells at indexPath
var cellToClose: CustomTableViewCell = self.tableView.cellForRowAtIndexPath(cells as! NSIndexPath) as! CustomTableViewCell
//Call Reset Method in our Custom Cell.
cellToClose.resetConstraintContstantsToZero(true, notifyDelegateDidClose: true)
}
}
}
Now Simply in your cellDidOpen: delegate method call closeOtherCells: with true parameter.
func cellDidOpen(cell: UITableViewCell) {
//Close Other Cells
closeOtherCells(close: true)
//Store Open Cell in NSMutableSet
let indexPath: NSIndexPath = self.tableView.indexPathForCell(cell)!
self.cellsCurrentEditing.addObject(indexPath)
}
I hope this help others. :)
I'm trying to do the opposite of what most people on this topic are asking to do. Most people want a button within a table view cell to call a method in their VC / VC table. I already have a protocol doing that.
Problem / Question
What I am trying to add now is the opposite: I need a button press on my main ViewController (which houses my table) to call a method within my CusomTableViewCell class (note: the button pressed on the main VC is not in the table). I have the protocol class created and the function written, but I don't know how to set the CustomCellViewClass as the delegate. When I did the opposite, I inserted "cell.delegate = self" into the cellForRowAtIndexPath method. I've also used prepareForSegue to assign a delegate. But with no segue and now cell-creation-method, I'm lost!
Example of Desired Function
My end goal is that pressing a button that is in the main VC will change the title of a button within the cells. A simple example would be that I have one view with a single table, on button press the table contents switch between two arrays, cars and motorcycles. When the table is showing cars, the cell button titles should all read "Look inside" but when showing the motorcycle button it should read "Look closer".
Code
I've already written the function that I want the cell to execute:
func cellButton_Title_Switch (currentList: String) {
if vcState == "cars" {
cellButton.setTitle("Look inside", forState: UIControlState.Normal)
}
else {
cellButton.setTitle("Look closer", forState: UIControlState.Normal)
}
}
I created the protocol:
protocol delegateToChangeCellBut {
func cellButton_Title_Switch (currentList: String)
}
I have the self.delegate.cellButton_Title_Switch(currentList) within my VC button and the protocol added to my custom cell class declaration. But how do I do that last missing piece in the custom cell class, where I assign the class to the delegate?
My original problem was that my UITableView's cell has buttons and labels, some of which change to match the state of things outside the table, things handled by the mainViewController.
The custom cell is defined by a customCellviewController. All the custom cell buttons and labels have their IBOutlets connected to the customCellviewController. I couldn't figure out how to make an action/change outside the table (in the mainViewController) immediately cause the cell labels and buttons to change.
Note: Protocols tend to work they other way around (a cell action triggers a function in the mainVC). I couldn't figure out how to use a protocol to solve this. Luckily, the solution was much simpler than a protocol.
The Solution!
I wrote the "updateCell" method that would change the labels and buttons and that code now sits in the customCellviewController. Then I called/triggered the "updateCell" function from the mainViewController simply by adding the call into my cellForRowAtIndexPath function. So it looks something like this:
var stateOfPage = "Green"
//Creates the individual cells. If the above function returns 3, this runs 3 times
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Setup variables
let cellIdentifier = "BasicCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! customCell
cell.updateCell(stateOfPage)
return cell
}
Now the above code/method runs when the table gets built. So to update the cells, have some button tap or other action reload the table data:
tableView.reloadData()
Good luck.