I am very new to UI test. I have an UITableView in my storyboard and it contains some cells.
Update:
I want to assert that the num of cells in UITableView when the app launches will be more than 0. But I don't know how to code this part.Using NSPredicate? Or others?
func testCellsNum()
{
let app = XCUIApplication()
let tableCell = app.tableRows.count
//Then what should I do ?
XCTAssertGreaterThan(tableCell, 0, "should greater than 0")//this line doesn't work
}
If you only have one table:
func testExample() {
let app = XCUIApplication()
let tablesQuery = app.tables
let count = tablesQuery.cells.count
XCTAssert(count > 0)
}
If you have multi tables,using this to get first or any index you want
tablesQuery.elementAtIndex(0).cells
Related
i made app with my Wordpress Website using rest api in Swift , i have implemented Google AdMob ads in my App, now i want to show AdMob Inline Adaptive Ads to show after half content , this is method i use for split content , i found it on internet
let cont = "This is my content here"
let words = cont.components(separatedBy: " ")
let halfLength = words.count / 2
let firstHalf = words[0..<halfLength].joined(separator: " ")
let secondHalf = words[halfLength..<words.count].joined(separator: " ")
print(firstHalf)
print(secondHalf)
it works fine but i don't know how to insert ad after firstHalf,, can you guys please help me ?
this is the code for inline adaptive banner
let adSize = GADCurrentOrientationInlineAdaptiveBannerAdSizeWithWidth(320)
let bannerView = GADBannerView(adSize: adSize)
bannerView.adUnitID = "ad unit ID"
bannerView.rootViewController = self
let request = GADRequest()
bannerView.load(request)
i have tried to add this code in func and then using something like this
firstHalf + inlineAds() + secondHalf
but it did not work, can we do this ??
If you are using tableView as UI then
1)Create cell contains label
2)Create cell with AdsView
3)Register both cell to table view
4)Create local var items:String
add first and secondString to items array
number of row count of tableView is items.count + 1
In cellforRow in Index method check
if indexPath.row == 1 {
let cell = googleAds cell
return
}
let cell = labelCell
return cell
//Second Option if your UI contains only three items
1)add ScrollView to UI
Add first label to ScrollView
3)Add GoogleAds below firstLabel
Add secondLabel to ScrollView below to GoogleAds View
Set the first and second text
I have a simply project with a tableView and a detail vc. The tableView displays 20 rows with "cell (n)" text and the detail view shows a label with the cell pressed.
I want to assert given a tap into a cell i get the text found in the tableView in the detail vc label. So for instance if i tap of the cell 3, which contains "cell 3" i want to get this text, instead of hardcoding it, and assert that i can find this text in the detail vc.
func testCanNavigateToDetailVCWithTheTextFromCell() {
let labelInTableView = app.staticTexts["cell 3"]
labelInTableView.tap()
let labelInDetailVC = app.staticTexts[labelInTableView.label]
XCTAssertTrue(labelInDetailVC.exists)
}
This seems working. But i want to do this:
func testCanNavigateToDetailVCWithTheTextFromCellV2() {
let cell = app.tables.element.cells.element(boundBy: 3) //Get the third cell of the unique tableView
cell.tap()
let textFromPreviousCell = cell.staticTexts.element(boundBy: 0).label //Since is a "Basic" cell it only has one label.
//I will also want to set an accessilibtyIdentifier to the label and access it via cell.staticTexts["id"].label
let labelInDetailVC = app.staticTexts[textFromPreviousCell]
XCTAssertTrue(labelInDetailVC.exists)
}
I set up a project with this issue here
The problem is that you are trying to get the text of the cell after tapping it. This means that the cell is no longer on the screen (the new screen has appeared). All you need to do is change the order of the lines cell.tap() and let textFromPreviousCell = cell.staticTexts.element(boundBy: 0).label. See the new function below:
func testCanNavigateToDetailVCWithTheTextFromCellV2() {
let cell = app.tables.element.cells.element(boundBy: 3) //Get the third cell of the unique tableView
let textFromPreviousCell = cell.staticTexts.element(boundBy: 0).label //Since is a "Basic" cell it only has one label.
cell.tap()
let labelInDetailVC = app.staticTexts[textFromPreviousCell]
XCTAssertTrue(labelInDetailVC.exists)
}
I am having some serious trouble getting my multiline label to show all of its lines. Often, the last line of the label simply does not appear, but it is apparent that the dynamically calculated cell height has taken in to account that it should have appeared, leaving around the appropriate amount of white space left over in my cell.
The affected label can display 1-7 lines depending on the data. I have played around with many various constraints to try and get it to display but regardless of what is on the last line, it just won't display.
The weird thing is, sometimes it will display when I segue in to the VC, but then when I use the segmented controller inside the VC to display different data and then go back again, the last line will again not display. The opposite happens frequently too (last line of label cutting off when I segue in to the VC, but then using the segmented controller inside the VC to change the displayed data and then go back, it will then display fine).
Things I have ensured: The label is set to word wrap, has line count of 0, has a height greater than or equal to the height of one of it's lines, its resistance and vertical content hugging is set to the highest of anything in the cell, and the width is set appropriately.
The below code is how I determine how many lines the label will have:
let descString = NSMutableAttributedString()
let bountyStart = NSMutableAttributedString(string: "Bounty: ", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(15)])
let bountyDesc = NSMutableAttributedString(string: bounty.description)
descString.appendAttributedString(bountyStart)
descString.appendAttributedString(bountyDesc)
let bLine = NSMutableAttributedString(string: "\n\n", attributes: [NSFontAttributeName : UIFont.systemFontOfSize(2)])
descString.appendAttributedString(bLine)
if !(bounty.turtles == 0.0){
let turtleStart = NSMutableAttributedString(string: "Your turtle count: ")
let turtleAmount = NSMutableAttributedString(string: bounty.turtleCount.description)
descString.appendAttributedString(turtleStart)
descString.appendAttributedString(turtleAmount)
}
descriptionLabel.attributedText = descString
In the screen shot below, you can see that the height of the cell is being calculated appropriately but for some reason, the last line of the label just refuses to show. It should appear after the "is for noobs" line. I've manipulated the white space to appear after the line instead of elsewhere by setting the problem labels bottom to constraint to be greater than or equal, and all other top to bottom constraints as equal to.
Constraints of the problem label:
I've been stumped for quite a while on this one, and I'm starting to think it's not the constraints I've set but something much deeper. All though I would love to be proven wrong.
Here is my VC code.
import UIKit
class TrendingVC: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var menubtn:UIBarButtonItem!
#IBOutlet var trendingTableView:UITableView!
var trendingToggle:Int = 0
let nwt = NWTrending()
let appUserId = NSUserDefaults.standardUserDefaults().stringForKey("UserId") ?? "1" //#TODO: remove ?? 1
var bountyArr: [Bounty] = []
var compArr: [Completion] = []
var peopleArr: [Person] = []
var userId: String = "0"
var username: String = ""
let bountyCellIdentifier = "BountyCellNew"
let personCellIdentifier = "PersonCell"
let completedCellIdentifier = "TrendingCompletedImageCell"
#IBAction func toggleTrending(sender:UISegmentedControl){
switch sender.selectedSegmentIndex{
case 0:
//loads the bounties on segmented control tab
trendingToggle=0
nwt.getTrendingBounties(appUserId, position: 0){(bountyArr, err) in //#TODO: change pos
self.bountyArr = bountyArr as [Bounty]
self.reloadTableViewContent()
}
case 1:
trendingToggle=1
nwt.getTrendingCompletions(appUserId, position: 0){(compArr, err) in
self.compArr = compArr as [Completion]
self.reloadTableViewContent()
}
case 2:
trendingToggle=2
nwt.getTrendingPeople(appUserId, position: 0){(peopleArr, err) in
self.peopleArr = peopleArr as [Person]
self.reloadTableViewContent()
}
default:
break
}
//reloadTableViewContent()
}
override func viewDidLoad() {
super.viewDidLoad()
trendingTableView.estimatedRowHeight = 300.0
trendingTableView.rowHeight = UITableViewAutomaticDimension
/******* Kyle Inserted *******/
//for followers and following back button text, you set it here for when you segue into that section
let backItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
navigationItem.backBarButtonItem = backItem
/******* END Kyle Inserted *******/
trendingTableView.allowsSelection = false;
trendingTableView.delegate = self
trendingTableView.dataSource = self
//sidebar code
if self.revealViewController() != nil {
menubtn.target = self.revealViewController()
menubtn.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
//loads the bounty on segue
nwt.getTrendingBounties(appUserId, position: 0){(bountyArr, err) in
self.bountyArr = bountyArr as [Bounty]
self.reloadTableViewContent()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//deselectAllRows()
}
func deselectAllRows() {
if let selectedRows = trendingTableView.indexPathsForSelectedRows() as? [NSIndexPath] {
for indexPath in selectedRows {
trendingTableView.deselectRowAtIndexPath(indexPath, animated: false)
}
}
}
func reloadTableViewContent() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.trendingTableView.reloadData()
println("reloading table view content")
self.trendingTableView.scrollRectToVisible(CGRectMake(0, 0, 1, 1), animated: false)
})
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(trendingTableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if trendingToggle == 0{
return bountyArr.count
}
else if trendingToggle == 1{
return compArr.count
}
else {
return peopleArr.count
}
}
func tableView(trendingTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if trendingToggle == 0{
return bountyCellAtIndexPath(indexPath)
}
else if trendingToggle == 1{
return completedCellAtIndexPath(indexPath)
}
else{
return personCellAtIndexPath(indexPath)
}
}
//calls method to set and display each trending bounty cell
func bountyCellAtIndexPath(indexPath:NSIndexPath) -> BountyCellNew {
let cell = trendingTableView.dequeueReusableCellWithIdentifier(bountyCellIdentifier) as! BountyCellNew
var bounty = bountyArr[indexPath.row]
cell.setBountyCellTrending(bounty)
return cell
}
func completedCellAtIndexPath(indexPath:NSIndexPath) -> CompletedCell{
let cell = trendingTableView.dequeueReusableCellWithIdentifier(completedCellIdentifier) as! CompletedCell
var comp = compArr[indexPath.row]
cell.setTrendingCompletedCell(comp)
return cell
}
func personCellAtIndexPath(indexPath:NSIndexPath) -> PersonCell{
let cell = trendingTableView.dequeueReusableCellWithIdentifier(personCellIdentifier) as! PersonCell
var peop = peopleArr[indexPath.row]
cell.setTrendingPeopleCell(peop)
return cell
}
}
Unable to comment due to rep low.
This seems like a IB thing, very likely constraints.
Make sure that any component's top constraint placed under this multiline UILabel is set to the bottom of said UILabel.
For example, in this project of mine, both the TitleView and TimeAndDetailsView contain UILabels that span multiplelines. In order to allow autolayout to properly space things all constraints need to be in order, also notice how the top constraint of TimeAndDetails is the bottom of TitleView
Edit note:
Before you tableView.reloadData() type the following 2 lines, or if uncertain, put it inside viewDidLoad
tableView.estimatedRowHeight = 60 //Type your cell estimated height
tableView.rowHeight = UITableViewAutomaticDimensionhere
Went to sleep last night, ok so I decided to add your code to my details label from the storyboards picture I had posted before, and it seems to have displayed just fine. I have a few questions and pointers below.
let descString = NSMutableAttributedString()
let bountyStart = NSMutableAttributedString(string: "Bounty: ", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(15)])
let bountyDesc = NSMutableAttributedString(string: "this would be 'bounty.description and is set to size 40", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(40)])
descString.appendAttributedString(bountyStart)
descString.appendAttributedString(bountyDesc)
let bLine = NSMutableAttributedString(string: "\n\n", attributes: [NSFontAttributeName : UIFont.systemFontOfSize(10)])
descString.appendAttributedString(bLine)
let turtleStart = NSMutableAttributedString(string: "Your turtle count: ")
let turtleAmount = NSMutableAttributedString(string: "random number 1234 goes here")
descString.appendAttributedString(turtleStart)
descString.appendAttributedString(turtleAmount)
detailsLabel.attributedText = descString
Sadly, this is in a scrollview and not in a cell, so there is the first difference. My first question; is your descriptionLabel.attributedText supposed to display different fontscolors or fonttypes/sizes?, if not then I would say to just use descriptionLabel.text instead of descriptionLabel.attributedText.
Now the fact that there is white space but nothing showing us makes me wonder if bounty.turtleCount.description was previously set to color white somewhere else, or you are simply seeing the newlines you added \n\n and nothing is there because if !(bounty.turtles == 0.0) doesnt execute. Are you sure the that if statement is executed? place a breakpoint and follow along to make sure the values are appended to descString
If this is all correct, then my guess would still be on constraints.
Could you elaborate on, quote from you; "sometimes it will display when I segue in to the VC, but then when I use the segmented controller inside the VC to display different data and then go back again, the last line will again not display." what are you doing different in the seg control when loading the values and reloading the tableview that makes it not display, and under what conditions does it work properly when you segue.
I updated xcode and now this doesn't happen anymore.
I just started with iOS programming and I could not find anywhere an answer about this.
I have a Textview, containing a large text divided in paragraphs.
I would like to trigger the reload of a Tableview whenever each paragraph reaches the middle of the TextView.
How could I do it?
Here is the code I wrote so far:
#IBOutlet weak var testo: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
if databaseDB.open() {
// Here I definethe string attributes
var style = NSMutableParagraphStyle()
style.lineSpacing = 15
let font = UIFont(name: "Georgia", size: 18.0) ?? UIFont.systemFontOfSize(18.0)
let textFont = [NSFontAttributeName:font, NSParagraphStyleAttributeName : style]
let fontSuper = UIFont(name: "Georgia-Italic", size: 13.0) ?? UIFont.systemFontOfSize(13.0)
let superFont = [NSFontAttributeName:fontSuper, NSBaselineOffsetAttributeName:6]
// and here the variables
let mainText = NSMutableAttributedString()
var paragraphId : String = ""
// I load the text from a table in the database
let querySQL = "SELECT id, versetext FROM table1"
let results:FMResultSet? = databaseDB.executeQuery(querySQL, withArgumentsInArray: nil)
while results!.next(){
paragraphId = results!.stringForColumn("id")
// I add some other information from another table (here goes also other code, but I have skipped it for simplicity)
let queryDescrSQL = "SELECT paragraph FROM table2 WHERE id = \(paragraphId)"
let paragraphResults:FMResultSet? = databaseDB.executeQuery(queryDescrSQL, withArgumentsInArray: nil)
if paragraphResults?.next() == true {
// here I add the attributes to the text to be shown
let numberParagraph = paragraphResults!.stringForColumn("paragraph")
let paragraphNumber = NSAttributedString(string: numberParagraph, attributes:superFont)
mainText.appendAttributedString(paragraphNumber)
let textParagraph = results!.stringForColumn("versetext")+" "
let paragraphText = NSAttributedString(string: textParagraph, attributes:textFont)
mainText.appendAttributedString(paragraphText)
}
}
databaseDB.close()
// and finally everything is loaded on the TextView
testo.attributedText = mainText
}
}
// I'm letting out the code for the TableView, as it is probably not relevant for the matter
I was thinking of adding some other attribute to paragraphNumber, something like here, but I haven't been able to find how to do it.
Thanks in advance,
Silvo
It's not clear how your paragraphs are divided up (is each in its own UITextView, or do they all form one large text view), but the basics of this are that you need to reload a table view when the scroll view reaches a certain scrolled position.
UITableView has a method reloadData, so call this when your scroll view is at the position.
Scrolling a scroll view just means you are modifying its contentOffset, and the scroll view sends its delegate a message every time the content offset changes. Make your controller the delegate of the scroll view, and implement the scrollViewDidScroll(_:) delegate method. In here, check the current content offset, and if it's at the value you're looking for, reload the table view:
class MyViewController: UIViewController, UIScrollViewDelegate {
// var scrollView = ...
// var tableView = ...
override viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// var scrollThreshold = (calculate some scroll threshold based on your paragraph text)
if scrollView.contentOffset.y > scrollThreshold {
tableView.reloadData()
}
}
}
I am trying to replicate a pull to refresh on a UITableView using the new Xcode UI testing framework in Xcode 7 (beta 3)
My current approach is dragging from the table to whatever element below the table I can find. This works when there is a fixed item below the table like a UIToolbar or UITabBar I would rather not rely on having a UITabBar or UIToolbar but I can't figure out a way to do the pull to refresh/drag action without using the method in XCUIElement.
func pressForDuration(duration: NSTimeInterval, thenDragToElement otherElement: XCUIElement)
But it fails when I don't have a toolbar/tabbar and try to drag using the cells
This is the relevant portion of my code:
func testRefresh() {
//For testing cell
for _ in 0...9 {
addCell()
}
refreshTable()
}
func refreshTable(var tbl: XCUIElement? = nil) {
let app = XCUIApplication()
if tbl == nil {
let tables = app.tables
if tables.count > 0 {
tbl = tables.elementAtIndex(0)
}
}
guard let table = tbl else {
XCTFail("Cannot find a table to refresh, you can provide on explicitly if you do have a table")
return
}
var topElement = table
let bottomElement: XCUIElement?
//Try to drag to a tab bar then to a toolbar then to the last cell
if app.tabBars.count > 0 {
bottomElement = app.tabBars.elementAtIndex(0)
}
else if app.toolbars.count > 0 {
bottomElement = app.toolbars.elementAtIndex(0)
}
else {
let cells = app.cells
if cells.count > 0 {
topElement = cells.elementAtIndex(0)
bottomElement = cells.elementAtIndex(cells.count - 1)
}
else {
bottomElement = nil
}
}
if let dragTo = bottomElement {
topElement.pressForDuration(0.1, thenDragToElement: dragTo)
}
}
func addCell() {
let app = XCUIApplication()
app.navigationBars["Master"].buttons["Add"].tap()
}
Additional failed attempts:
swipeDown() (multiples times as well)
scrollByDeltaX/deltaY (OS X only)
You can use the XCUICoordinate API to interact with specific points on the screen, not necessarily tied to any particular element.
Grab a reference to the first cell in your table. Then create a coordinate with zero offset, CGVectorMake(0, 0). This will normalize a point right on top of the first cell.
Create an imaginary coordinate farther down the screen. (I've found that a dy of six is the smallest amount needed to trigger the pull-to-refresh gesture.)
Execute the gesture by grabbing the first coordinate and dragging it to the second one.
let firstCell = app.staticTexts["Adrienne"]
let start = firstCell.coordinateWithNormalizedOffset(CGVectorMake(0, 0))
let finish = firstCell.coordinateWithNormalizedOffset(CGVectorMake(0, 6))
start.pressForDuration(0, thenDragToCoordinate: finish)
More information along with a working sample app is also available.
I managed to do such tasks with coordinates like Joe suggested. But i went a step further using coordinateWithOffset()
let startPosition = CGPointMake(200, 300)
let endPosition = CGPointMake(200, 0)
let start = elementToSwipeOn.coordinateWithNormalizedOffset(CGVectorMake(0, 0)).coordinateWithOffset(CGVector(dx: startPosition.x, dy: startPosition.y))
let finish = elementToSwipeOn.coordinateWithNormalizedOffset(CGVector(dx: 0, dy: 0)).coordinateWithOffset(CGVector(dx: endPosition.x, dy: endPosition.y))
start.pressForDuration(0, thenDragToCoordinate: finish)
Now i am able to drag from a specific point to another specific point. I implemented a custom refresh on some of my views. While implementing this, i also discovered that i can even use this to access the control center or the top menu.
Here is a Swift 5.1 version
let firstCell = staticTexts["Id"]
let start = firstCell.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
let finish = firstCell.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 100))
start.press(forDuration: 0, thenDragTo: finish)