iOS: How to assert a cell is selected in UITest? - ios

I have the following hierarchy:
ViewController -> TableView -> TableViewCell -> CollectionView -> CollectionViewCell -> UIImage
On the tableViewCell, inside the collectionView, there are 4 cells. Each cell has a unique label.
When the user taps the collectionViewCell the cell becomes selected and also the UIImageView is highlighted. In this case this means there is a different image there. I have the accessibility on the UIImageView enabled and the traits set to both button and image to try to access those XCUIElementTypes
I have not been able to make any UITest work because it always tells me that there are multiple results for the UIImageView even though the cell that the image view is in has a unique identifier.
How do I assert that the cell has been selected?
Using the record function I get:
let whatIsYourFilingStatusCellsQuery = XCUIApplication().tables.cells.containingType(.StaticText, identifier:"Single").childrenMatchingType(.Image).matchingIdentifier("btn_unselected").tap()

#Che has the right idea.
In didSelectItemAtIndexPath:, I did this:
cell.accessibilityIdentifier = #"isSelected";
Then, in the test file:
XCTAssertEqualObjects(randomCellToTap.value, #"isSelected");
I'm also going to do the opposite thin in didDeselect...

Add the selection state to the accessibilityTraits, like this:
cell.accessibilityTraits = [.button, .selected]
Then in the UITest you can check if the cell.isSelected state, also this will reduce the complexity for programming voice over states.
The other solutions will require you to implement the voice over state manually.

I really dont have pretty solution. But I see there is only one way to assert cell is selected. Set some property to the cell at the moment of selecting. E.g accessibilityIdentifier is going to have unique id, and accessibilityValue is going to have any info what you need ("selected=true" or "selected")
UPD you have access for these properties:

Related

Implementing different user interaction function for subviews of a cell

I have a UICollectionView which allows for a user to select a cell and upon doing so view 'A' will appear. I am wondering if it is possible for the subviews of this cell, ex: UIlabel and UIImageView to provide a different functionality for when they alone are selected. For example, if the UIImageView is selected, I want to segue to view 'B' as opposed to 'A'.
I have attempted to implement a UITapGestureRecognizer for both the label and the image, however, the cell's functionality overrules and the resulting view is still 'A'. Any ideas?
Thank you in advance.
What you want to achieve is possible through delegates if you don't have custom cell make a custom cell class then inside custom cell declare your protocol
I assume you have to disable the default behavior of the collection view cells:
cell.selectionStyle = UITableViewCellSelectionStyle.none
However, if you set that and you encounter an overlap issue, please take a look at the 'cancelTouchesInView' property of the 'UITapGestureRecognizer'. Basically, by setting that to false, you allow the children to also receive touch actions.
Furthermore, do not forget each gesture recognizer should have it's own method for you to be able to segue into two different places.

Xcode7.3 UITest of Disclosure Indicator in a custom TableViewCell

(Xcode 7.3.1, iOS9.3, Swift)
I'm struggling with setting up an UI Test in my assignment for what would seem to be a fairly standard and common use case.
I have a sub-classed UITableViewCell which contains a textField that does not fill the entire cell and has the Disclosure Indicator set. The user is able to type the data into textField by tapping on it, but if they tap outside the textField, but still in the cell, my app segues to another view. The project works fine and the child view receives the correct data from the cell's textField.
My Master-Detail app has UITableViews on both the Master and Detail views. My UI Test selects the first cell in the Master View, waits a little for the DV and then selects the second cell in the detail view, which is configured in accordance to my earlier description.
I am simulating a tap on the second cell using
tablesQuery.cells.elementBoundByIndex(1).tap()
However, the simulator interprets it as a tap in the textField, so no segue occurs. I have read SwiftyCruz's question and the given answers but his use case is a little different, as I'm not using a Detail Disclosure, also the accepted answer doesn't translate to my situation. In following ferunandu explanation, I have tried setting the Accessibility Label and Identifier on the Cell as well as a different string for the accessoryView?.accessibilityIdentifier. Though UI Test still doesn't work, ferunandu's snippet does change the disclosure indicator from > to a blue box.
Using a breakpoint, I've been querying the app object in the debug window trying to find the right control, but to no avail. I am able to find the textField using indices as well as the identifier, but nothing I've tried has returned .exists true for the accessoryView.
Am I trying to access the cell incorrectly? Or, is it actually not possible to do what I'm attempting?
The code for my test is:
XCUIDevice.sharedDevice().orientation = .Portrait
let app = XCUIApplication()
wait()
let tablesQuery = app.tables
tablesQuery.cells.elementBoundByIndex(0).tap()
wait()
tablesQuery.cells.elementBoundByIndex(1).tap()
What am I not understanding properly here? Any advice would be greatly appreciated.
(I know I could have used a standard UITableViewCell with the data entry done on the child view, but I prefer not to change the design of my app to accommodate XCTest's, seems a bit backwards.)
I have a sub-classed UITableViewCell which contains a textField that does not fill the entire cell...
I am simulating a tap on the second cell using
tablesQuery.cells.elementBoundByIndex(1).tap()
However, the simulator interprets it as a tap in the textField, so no segue occurs.
It sounds like your tap on the table view cell is being intercepted by the text field. When UI tests tap on a table view cell, the tap will be at the centre of the UI element's frame.
To offset the tap location from the centre of the cell, use coordinate(withNormalizedOffset:) and tap the coordinate instead of the element directly.
let cell = app.tables.cells.elementBoundByIndex(0)
// Retrieves coordinate that is 50% of the way along the cell
// and 20% of the way down the cell - adjust these values to
// find the right values to avoid tapping the text field area
// of the cell
let cellCoordinateForTap = cell.coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.2))
cellCoordinateForTap.tap()

adding subview in a single uitableviewcell

Im facing a problem with adding a UIImageView to a single UITableViewCell. I add the subview like this in the cellForIndexPath delegate method which is ONLY added to the cell if the self.mediaTypeArray contains the string: "Image" at the index: indexPath.row
let cell = self.timelineTableView.dequeueReusableCellWithIdentifier( NSStringFromClass(ClassicCaseCell), forIndexPath: indexPath) as? ClassicCaseCell
if self.mediaTypeArray[indexPath.row] == "Image" {
let imageView = UIImageView()
imageView.frame.size = (cell?.evidenceView.frame.size)!
imageView.frame.origin = CGPointZero
imageView.image = img
cell?.evidenceView.addSubview(imageView)
}
Which it works great however, I find that every other two cells contains that same imageView even when self.mediaTypeArray[indexPath.row] is NOT equal to "Image" and I don't understand why. I think it may have something to do with reusable tableviewcells but I still don't see why it would do this. Please help!
I'm getting the feeling that this is because of cells being re-used.
A way you can work around this is to override your ClassicCaseCell's prepareForReuse method to remove any image view from its evidenceView.
I would however recommend that you don't add the image view in this fashion (through the delegate's cellForIndexPath method). Instead, your ClassicCaseCell should hold the image view as an instance variable which you can then set in cellForIndexPath. This way, there is only at most one. You can also make sure to set the image view to be nil in prepareForReuse, making sure that it won't appear in the cell if it is not set.
First, don't use tag property as recommended elsewhere. That was a technique used a long time ago, but Apple discourages that practice now. Second, I'd suggest you simplify your life and simply don't programmatically add image views in cellForRowAtIndexPath. If you programmatically add image views, cell reuse introduces a clumsy process of determining whether (a) you need an image view; (b) whether there is an existing image view; and (c) possibly adding/removing image view and/or getting reference to existing one.
One very simple solution is to just have two cell prototypes, one with an image view and another without. Then, based upon the media type, dequeue a cell with the appropriate storyboard identifier and use it.
The other alternative is to have the image view in the cell regardless, and hide/show it as appropriate. The challenge then becomes how to best manage two sets of the constraints, one for when the image view is visible and one when it's not. You can do this with judicious choice of constraint priorities, activating/deactivating the appropriate constraints in cellForRowAtIndexPath, etc. It can be done, but this is more cumbersome than the above approach, whereby you just employ two cell prototypes.
You only need to add the UIImageView once so if the cell is re-used again, it (might) already be there. Your problem is to detect if you've already added it or not. Here are a couple suggestions:
1) ALWAYS create it (and just don't set the image, or hide it)
2) assign it a unique tag and look for the tag when you need to set it... no tag, then create it
Override prepareForReuse delegation in your tableview cell and remove imageview from there

How to have DidSelectRow activate keyboard?

Goal
I want to have a keyboard (custom) to show when I click on a cell in my tableview and have said keyboard edit a label in the selected cell.
What I have read and tried
Stack overflow and other searched threads/tutorials
A Swift example of Custom Views for Data Input (custom in-app keyboard)
How to make custom keyboard only for my app in Swift?
iOS 8: Creating a Custom Keyboard in Swift
Along with other results and searches (also the recommended readings within these threads), these were great for me getting the keyboard the way I want it (which is app-specific, I don't want to have the user install the keyboard to use the app), however, none explain how I could "activate" the keyboard without a textfield.
My thought process is this: I will have a keyboard with a textfield in place in order to receive the input from the keys pressed. This input would then be sent to the label that is in the selected cell. The problem is in the keyboard showing without anything to call it...
Why I don't want a textfield in the cell: Because I think it is more smart/elegant to have the tableview cell activate the keyboard. I have seen this in other apps and I can't figure out how it is done.
Any help on this matter is much appreciated. I am completely new to programming, am using Swift (so have no clue Obj-C or the like).
Thank you!
Well after a lot of discussions around other websites, it is a shame to say it but what I wanted was actually impossible (like stated by Duncan). For some reason I thought that in programming that word did not exist, but alas it is there. Well, the way that I did work around this limitation was as replied here in the comments as well as from the resource materials I already read:
I have 2 views:
tableView - this will have the list of items that I would like to edit a value. Has multiple labels where one of them would be edited by the user.
keyboardView - custom keyboard that would allow user input.
For the keyboard I used the delegate method as described in "A Swift example of Custom Views for Data Input". This worked perfectly and would send the data over to the tableView under the keyWasTapped function.
Next was the tableView. I had 3 labels in the custom cell, where one was called valueLabel. I had to add a textField to my storyboard hidden behind the tableview with which the user will interact and in the didselectrow I included the command to summon the keyboard:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
textField.becomeFirstResponder()
}
From there I would grab the text from the textfield and update the UILabels in my custom cell. At least this way I can select a cell and not the textfield. So considering that the answer that would fit what I wanted is combination of the replies, I thought it would be best to say it here.
Thanks to Sandeep and Larme for their time.
TVDN,
Have a textField in cell not the label :) Create a custom cell create an IBOutlet to that class ffrom textField in cellForRowAtIndexPath
let tableCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! myCell
tableCell.yourTextField.resignFirstResponder()
and in didSelectRowAtIndexPath
let tableCell = self.tableView.cellForRowAtIndexPath(indexPath)
self.tableView.dequeueReusableCellWithIdentifier("cell") as! myCell
tableCell.yourTextField.becomeFirstResponder()

How to integrate a "like" button in swift tableviewCell

What is the logic i should follow to integrate a like button in a tableview cell?
How do you update a text label inside the cell signaling how many likes it has in real time IE: when you click the like button it either adds a like or removes a like?
The button also is highlighted when current_like = true
and not highlighted when current_like = false
Where do I update that kind of stuff?
How can you update a cells label and display the new label from within the cell? or is it NECESSARY to reload cell for row at Index Path?
The two main problems/steps you have to achieve are:
- Update the label with the new like
- Update the datasource of the table to keep the data persistent.
So, what I would make:
Set your custom UITableViewCell as target for the Button, so the cell can know when the button was clicked. In the target function/selector you should update the label.
Now, you have to inform the datasource of your table that the cell has a new like. You can create a protocol in UITableViewCell and set the TableDataSource as its delegate. Then, when the button was clicked you can notify the delegate.
You can achieve the same behavior with NSNotificationCenter, instead delegation.
Regards ;)
To change the cell's content without reloading you need to create a pointer to that cell. You can change your cell's parameters directly using a pointer without reloading cell. So it would be something like
self.myCell.label.text = something
And to assign the pointer to your cell you must put something like this in your cell adding method:
self.myCell = yourLikeCounterCell

Resources