I am performing UI Automation in XCode using Swift
I would like to perform Tap gesture on UICollectionView with multiple Cells using Swift
As my Cells keeps on updating everyday. Is there anyway I can perform Tap gesture on anyone of the cell.
Try this
let app = XCUIApplication()
app.tables.cells.forEach { $0.tap() }
If you want to tap on the first cell of the collectionView, assuming there is only one collection view in the current screen
let app = XCUIApplication()
app.collectionViews.element(boundBy:0).cells.element(boundBy:0).tap()
If there are multiple collectionView, find the collection View of current interest using 'po app.collectionViews.count', then find the index of the required collectionView. Say if it is at index 'n'. Then
let app = XCUIApplication()
app.collectionViews.element(boundBy:n).cells.element(boundBy:0).tap()
If you want to tap on any cell at random, within the collection view. then you can try this
let app = XCUIApplication()
let randomCell = arc4random() % app.collectionViews.element(boundBy:n).cells.count
app.collectionViews.element(boundBy:n).cells.element(boundBy:randomCell).tap()
Related
I'm developing an iOS app with a UITableView that requires a swipe gesture to perform a certain action for any cell (row) in the table.
When I initially implemented the IBAction for the gesture in my view controller (the one with the UITableView in it), I wasn't even able to run the app, as Xcode informed me with an error that it doesn't allow attaching gestures to repeating interface elements (the cell being a repeating element, since a new one is generated each time one is needed via dequeueing a reusable cell).
So I then proceeded to place the IBAction for the swipe gesture inside my custom table cell class instead, at which point I no longer receive the error that prevents me from building/running, but am still receiving a warning in the debug console while running the app, which states that since iOS9, the system now enforces the prohibition of gestures attached to repeating elements (in this case, my table cell).
If anyone has insight into gesture recognizers, I'd appreciate it if you could help me figure out the following questions.
Should I take this warning at face value and assume that I'm not allowed to attach any gestures to table cells at all?
In either case, is there some other way to attach a gesture to repeating elements like prototype table cells and avoid any kind of warnings/prohibitions?
You should put the swipe in your viewcontroller and get a cell through Swipe IBAction with the code below. Once you've done that, you can do what you want: cell.do_something or cell.element.do_something
You get the cell with the position of swipe, like:
Objective C:
CGPoint location = [sender locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
Swift:
let location : CGPoint = sender.locationInView(self.tableView)
let indexPath : NSIndexPath = tableView.indexPathForRowAtPoint(location)
let cell : UITableViewCell = tableView(tableView, cellForRowAtIndexPath: indexPath)
I am currently developing an iOS application in Apple's Swift.
I have a table view with table cells in it each of which displays the current time of a timer (it is not a real timer it is actually just a timestamp).
The application itself has a timer which updates the visible cell of the table view with the current states of the cell timers.
The application provides the possiblity to slide a cell which lets appear a delete button.
The problem I am faced with is that the delete button immediately disappears due to the fact that the cell is updated by the application's timer.
Here is the code where the table view cells are updated:
// Update visible rows in table
func updateTable() {
// Get all visible cells
let cells = timerTable.visibleCells as! Array<TimerTableViewCell>
for cell in cells {
let indexPath = timerTable.indexPathForCell(cell)
cell.timeLabel.text = String(timerArray[indexPath!.row].getRemainingTimeAsString())
timerTable.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.None)
}
}
I would be glad if anyone would have a solution/workaround for my problem.
Thank you in advance.
I think the simpler solution is to remove your reloadRowsAtIndexPaths call, and just update the timeLabel. You could try this:
// Update visible rows in table
func updateTable() {
// Get all visible cells
let cells = timerTable.visibleCells as! Array<TimerTableViewCell>
for cell in cells {
let indexPath = timerTable.indexPathForCell(cell)
cell.timeLabel.text = String(timerArray[indexPath!.row].getRemainingTimeAsString())
}
}
Because reloadRowsAtIndexPaths says to the UITableView: Hey, just take the rows at theses indexPaths and rebuild them from scratch. Ignore its previous state.
I want to build a UICollectionView, or use something similar library to make grid view, in which i can rearrange cells. My app supports iOS 7, and, unfortunately UICollectionView method - (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath
is only available on iOS 9 and later.
Is there any way i can achieve the effect of 'drag and reorder' collection view cells on iOS7? Thanks.
Well, I happened to come across the very same issue, so let me add my two cents on this.
In order to achieve to get the same effect of what UIKit does when rearranging cells, I followed a more or less similar approach described below:
Add a long-press gesture recognizer into collection view so that
you’d be able to detect which cell was tapped without messing up
with collectionView:didSelectItemAtIndexPath method.
When user long-presses on a cell, take a screenshot of that cell, add the snapshot view into collection view and then hide the cell. As the user moves his finger on the screen, update the snapshot’s center to move it programmatically so it will look like the user is dragging the cell.
The trick is to decide when and how to swap cells. As the snapshot moves around, it will intersect with other cells. When it happens, you should update your data source and call moveItemAtIndexPath so it will swap the positions of original cell and the cell which the snapshot intersects.
When dragging ends, remove the snapshot from collection view and show the original cell.
You can map those steps to gesture recognizer’s states, so that we know what you should do at each state:
Began: 1
Changed: 2, 3
Ended, Cancelled, Failed: 4
I put an example project in my github account, so you can download and play around with it. Here, I will just implement the gesture callback:
Note: The below code may not work because I threw away some implementation specific details. But it should show the basic intent. Please download the full code from my github repo.
func longPressRecognized(recognizer: UILongPressGestureRecognizer) {
let location = recognizer.locationInView(collectionView)
let indexPath = collectionView.indexPathForItemAtPoint(location)
switch recognizer.state {
case .Began:
let cell = collectionView.cellForRowAtIndexPath(indexPath)
let snapshotView = cell.snapshotViewAfterScreenUpdates(true)
snapshot.center = cell.center
collectionView.addSubview(snapshotView)
cell.contentView.alpha = 0.0 // hides original cell
case .Changed:
snapshotView.center = location // will follow user's finger.
dataSource.swap(originalCellIndexPath.item, indexPath.item) // swaps data
collectionView.moveItemAtIndexPath(originalCellIndexPath, toIndexPath: indexPath) // swaps cells
originalCellIndexPath = indexPath
default:
let cell = cellForRowAtIndexPath(originalCellIndexPath)
cell.contentView.alpha = 1.0
snapshotView.removeFromSuperview()
}
}
Here is the effect you will get (recorded on iOS 8.4 Simulator):
I have a simple app that shows table view with 3 cells.
1. First cell has only button.
2. Second cell has button and label.
3. Third cell has only label.
When recording with Xcode instruments, the code is not generated properly:
var target = UIATarget.localTarget();
//tap background of each cell
target.frontMostApp().mainWindow().tableViews()[0].cells()[0].buttons()["Button"].tap();
target.frontMostApp().mainWindow().tableViews()[0].cells()[1].tap();
target.frontMostApp().mainWindow().tableViews()[0].cells()[2].tap();
//tap button of each cell
target.frontMostApp().mainWindow().tableViews()[0].cells()[0].tap();
target.frontMostApp().mainWindow().tableViews()[0].cells()[1].tap();
//this cell has no button
//tap label of each cell
//this cell has no label
target.frontMostApp().mainWindow().tableViews()[0].tapWithOptions({tapOffset:{x:0.49, y:0.25}});
target.frontMostApp().mainWindow().tableViews()[0].tapWithOptions({tapOffset:{x:0.40, y:0.43}});
It seems like recorder doesn't recognize pressed view and inserts something else instead.
Is this bug in Xcode tools or behaviour somehow related to UIAccessibility?
I have a button on a CollectionView that animates the labels on the CollectionView (slides them back & forth). I'm wondering if it's possible to also animate the labels on the cells using the same button.
I was considering collectionView:performAction as a possible solution, but I wanted to get some other ideas before sinking too much time into figuring out something that potentially might not work.
Bingo:
for item in self.collectionView!.visibleCells() as [UICollectionViewCell] {
var indexpath : NSIndexPath = self.collectionView!.indexPathForCell(item as CollectionViewCell)!
var cell : CollectionViewCell = self.collectionView!.cellForItemAtIndexPath(indexpath) as CollectionViewCell
//access cell data
println(cell.labelName.text)
}
found here