iOS automation : how to tap button that has no id - ios

I have the following items in logElementTree output:
UIAButton: rect:{{20, 427}, {41, 41}}
UIAButton: rect:{{140, 427}, {41, 41}}
These buttons have no identifier, no name, and are not drawn in XIB. On my automation test script I only use index (something like target.frontMostApp().mainWindow().buttons()[7].tap())
But then, this line will not always work because index is changing. I just want to ask, if there's a way to tap this button other than using index? Please note that the button has no name, so I cannot use buttons()["name'"].tap()

Technically this would be the best way to go about doing what you would like, so I'll leave it here for other developers who see this question. In your case since you have limited technical experience, I would recommend asking your developer to assign ids or names to buttons. Providing good names for buttons and other UI elements means that your app is also accessible for those users with impaired eyesight, since voiceover will read them the names you've given to your buttons. The following block of code will programmatically assign a label and a accessibility hint to a dynamically created object that conforms to UIAccessibility.
From Apple's documentation (in this case they're doing it on a view, but you could do it on any object like a button):
- (id)init
{
_view = [[[MyCustomView alloc] initWithFrame:CGRectZero] autorelease];
[_view setIsAccessibilityElement:YES];
[_view setAccessibilityTraits:UIAccessibilityTraitButton];
[_view setAccessibilityLabel:NSLocalizedString(#"view.label", nil)];
[_view setAccessibilityHint:NSLocalizedString(#"view.hint", nil)];
}
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html#//apple_ref/doc/uid/TP40008785-CH102-SW5
Javascript "hack" (not very clean, but it works...):
var window = UIATarget.localTarget();
window.tap({x:yourXCoordinateHere , y:yourYCoordinateHere});

Related

programmatically adding textview

Usually I do all of the creation of views in storyboard but now I need to add a view via code and although I understand the very basics of doing one, I need some help on how I go about setting up views but more than that I need to get them placed inside and existing view and also create multiple items. So I will have a parent UIScrollView and inside of that one I need to be able to create up to x additional blocks each block containing two lables and one textview. Something along these lines
I can use the storyboard to create the Parent ScrollView
ParentSCROLLVIEW
child_1_block
lable_1_1
label_1_2
textview_1
child_2_block
lable_2_1
label_2_2
textview_2
child_3_block
lable_3_1
label_3_2
textview_3
child_4_block
lable_4_1
label_4_2
textview_4
.
.
.
child_x_block
lable_x_1
label_x_2
textview_x
I am trying to create just a single one now but I am not sure of the syntax to place it inside of an existing scrollview currently it is going into the main view working on that right now. The other confusing this I have been thinking about is teh creation of a dynamic amount of the same type of objects are is the object name determined if it is going to be dynamic
UITextView *newTextView = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, 300, size.height + 16)];
Currently I use this to create a new textview but I will need to do the same thing for multiple entities
such as *newTextView1, *newTextView2, *newTextView3, *newTextView4, ..., *newTextViewX
Am I able to construct a string with the appended number and then use that string as the name of the object I need to create...never did that before so I am not sure but I have a feeling I would see errors
I am hoping some one could show me some sample code or point me in the right direction or even suggest what the correct terms I can search for...anything would be helpful
Jeff
You can't give dynamic names to instances.
What you can do, is give them different tags, in case they subclassing from UIView (Like UITextView).
Run this in a loop:
UITextView *myTextView = [[UITextView alloc] initWithFrame....];
[self.view addSubview:myTextView];
myTextView.tag = loopIndexInt; // This is where you put your dynamic number.
Now, In order to retrieve a specific UITextView by it's tag, do:
UITextView *textView = [self.view viewWithTag:5]; // in order to get textView with tag number 5.
Anyway, it really depends on what you're trying to do.

How to Get the value(label) from the TextField inside the custom cell of a table View to post on te URL

I want my iOS app to pass to a URL the text the user types. The relevant code is pasted below. WHen I run the app, the text does not get assigned any value. It stays as null.
I am new to iOS programming and am probably missing something obvious here. Any help/pointers will be appreciated.
Mac OS version : 10.6.8 (64 bit)
Xcode version : 3.2.3
In the snapshop.. "I have created a Table View and placed custom cell inside each row of a table. TextField is inside the custom cell view. Post is a button next to the textField which is a done Button. If I click that button, the text we are entering in the textField should post on URL which i have specified in my code snippet."
NOTE: The text is like a comment and posting this comment that should post on particular image in that cell.
I have copied the code snippet below. I have also attached an image to explain.
-(IBAction)postAction:(id)sender
{
int index=[sender tag];
homecell *cell = [[homecell alloc] init];
UITextField *txt_c =(UITextField *)[cell.txt_comment viewWithTag:index];
NSLog(#"jj %#",txt_c);
gen_data *ut1=[[gen_data alloc]init];
gen_data *ut=[gen_data getInstance];
}
I assume that you have created that view using the interface builder, probably all you need is to connect that textfield as an IBOutlet and access the value.
IBOutlet is the key word here, search for it and you will find really fast the way to do it.
Check this link.
You should get some basics of iOS programming first.
Why are you creating a totally new custom cell that is irrelevant to your UITableView? When you create it that way, it's a totally new UITableViewCell.
You should connect it to it first, get its NSIndexPath and then operate with it and do what you want.
BTW, if you want to see the text of UITextfield you should NSLog myTextField.text
And you could easily write viewWithTag:sender.tag, no need to create an extra ivar here

Get UIVIew from accessibilityLabel for KIF automation

I am using the KIF Framework for functional UI testing. Let's say I am on a current iPad screen where many views (labels, buttons, textfields etc) have unique accessibility labels assigned. If I have the accessibilityLabel string handy, can I get a reference to the associated UIView from current screen using it?
For example, [[UIView alloc] viewWithTag:5] returns UIVIew of provided tag. I am looking for something like [[UIView alloc] viewWithAccessiblityLabel:#"my label"].
P.S: I know the brute-force method would be to iterate all views in self.subviews recursively, and compare accessibility label to find what am I searching for. I am looking for a better approach.
There is actually an incredibly simple way to achieve what you describe when using KIF for UI automation, though KIF doesn't make it obvious that this is possible. waitForViewWithAccessibilityLabel returns a reference to the view when it is found:
Swift
let view = tester().waitForView(WithAccessibilityLabel: "My label")
Objective-C
UIView *view = [tester waitForViewWithAccessibilityLabel:#"My label"];
Hugely useful, once discovered.
I am using KIF for UI automation! Here are the steps to get view from given accessibilityLabel. Method viewContainingAccessibilityElement:element is extension method to UIAccessibilityElement class.
UIAccessibilityElement *element = [[[UIApplication sharedApplication] keyWindow] accessibilityElementWithLabel:label];
UIView *view = (UIView*)[UIAccessibilityElement viewContainingAccessibilityElement:element];
It sounds to me (from your comment: "I need this functionality in automating UI tests") like you are looking for the accessibilityIdentifier. From the documentation:
The UIAccessibilityIdentification protocol is used to associate a unique identifier with elements in your user interface. You can use the identifiers you define in UI Automation scripts because the value of accessibilityIdentifier corresponds to the return value of the name method of UIAElement.

UIRefreshControl weirdness

Just started to use "UIRefreshControl" today, and noticed some weird things.
If I created the UIRefreshControl in InterfaceBuilder, and wire it all up, my selector never gets called. IB says that the control should fire on the "value changed" message, and should call my code, but never does.
So I abandoned doing this in Interface Builder, and just decided to do this in the code. Not too many lines. So I put this code inside of my "viewDidLoad" routine:
self.refreshControl = [UIRefreshControl new];
[self.refreshControl addTarget:self action:#selector(doStuff:) forControlEvents:UIControlEventValueChanged];
NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.alignment = NSTextAlignmentCenter;
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:#"Pull to refresh" attributes:#{NSParagraphStyleAttributeName : paragraphStyle}];
It draws fine, but the refresh-control is visible when the view first appears. I'd prefer that it not be visible until the user actually pulls down on the table. I'm making a call to a web-service, and wait for the results to come back, then I populate the table. So, you only see the control for a second or two. But it just looks weird.
So, the questions are:
1. Is this a known bug that you can't wire up the control solely from Interface Builder?
2. Is there a way to add this control to the table-view controller, without it appearing until the user actually wants to see it?
You don't need to add a refresh control, or declare one in your viewController. Adding pull-to-refresh is a two-step process.
Step 1: In your storyboard, go to your tableViewController and, where it says "Refreshing", select "Enabled".
Step 2: Add the following code to your tableViewController.m file, in viewDidLoad:
[self.refreshControl addTarget:self
action:#selector(refresh)
forControlEvents:UIControlEventValueChanged];
That's the entire process, other than doing stuff in your -refresh method. There's no more to it.
Yes, there is a bug with the UIRefreshControl that the action is never called. Keith Harrison talks about this in his blog entry UIRefreshControl Fun and Games:
Investigating with the debugger it seems that the refresh method is
never called. I can only assume that for some reason the storyboard is
not properly connecting the UIRefreshControl to our action or is
having some other strange interaction with the
UIControlEventValueChanged event. I have filed a bug report with Apple
(rdar://14178445) but there is also a simple workaround you can use.
The workaround is as described by #AMayes.

How can i auto generate text fields

I'm new to iOS and I'm busy with my first app. I have a question: In the code below is there any way to have my app auto generate a text field when a button is pressed without me having to set it up first?
For example in the code below would it be possible to get input from a text field and use it in place of "field1" to auto generate another textfield.
UITextField *field1 = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 30)];
field1.placeholder = #"Textbox 1";
field1.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:field1];
x = 10;
w = 100;
h = 30;
y = y + 60;
UITextField *field1 = [[UITextField alloc] initWithFrame:CGRectMake(x, y, w, h)];
field1.placeholder = #"Textbox 1";
field1.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:field1];
OK i see it was my code. In CGRectMake i replaced the values with variables which i incremented now with every button press and its more text fields, before it was adding on on top of the other thats why it seemed like it wasnt adding new text fields.
Now 2 questions on the above,
can i access the textfields individualy using this method.
Is this considered best practice or does it just use a lot of memory.
General Answer
Going by your question and comments, it looks like you want to keep adding an arbitrary number of fields and manage them as you go (keep them around so you can access and manipulate them). You'll still need to write code to create a field and stash it somewhere.
You can create a -createAndStoreFieldWithName: method that's called by your action. Such a method would use something similar to the code you wrote above but would store it somewhere. I'd recommend an 'NSMutableDictionary' that lives permanently in your controller, since dictionaries provide named access to their content.
This way, if you want to create a field called "Foo", your -createAndStoreFieldWithName: method would check to see if an entry named Foo already exists in the fieldsDictionary dictionary and, if not, would create the field (and add it to its superview, positioned wherever you wish) in code and call -[fieldsDictionary setObject:newlyCreatedField forKey:fieldName] to store it. That way, you can always get the field by name by asking -[fieldsDictionary objectForKey:desiredFieldName].
If you later want to remove them, use the same "get the field by name" approach to access the field, remove it from its superview, and remove it from the dictionary so it can be disposed of properly.
Of course if multiple fields with the same name can exist (ie, more than one Foo field), you'll need to add a layer of abstraction. In this case, you can use a unique identifier (like a UUID) the user never sees. The thing to figure out (which is hard to specify without more detail from you) is how you'll match the identifier to the proper field (given the possibility of multiple Foo fields, for example).
Also, you could just add the fields to the superview and loop through its -subviews array to locate fields by their label but this is anti-pattern to proper MVC design. Your controller object (the one creating the labels and adding them to other views) should be keeping track of these fields as I mentioned above, not the superview. This lets your controller be the intelligent mediator, since it knows what the fields are for and will use them (or possibly hand them off to some other controller) as it needs.
Situation-Specific Answer
BUT - consider whether individual UITextFields are the right way to go. Perhaps a UITableView with added rows is the better choice here? In this case your controller just decides there's one more row and kicks the table view to update. When asked about the cell, just give it your desired label from some labels container (maybe just a straight NSMutableArray of labels maintained by your controller - it matches array index to row index). MUCH simpler.
More detail in your question will get you more specific answers. Again, though, edit your original question - don't keep appending comments.
I assume you have a buttonPressed: method? Your question is not very clear, so if you'd like to clarify, I'm sure we can provide you with a better answer. Assuming you have a textField named inputField as a property of your view controller, you could do something like this:
- (void)buttonPressed:(id)sender {
NSString *currentText = self.inputField.text;
UITextField *newTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 30)];
newTextField.placeholder = currentText;
newTextField.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:newTextField];
}
This would create a new UITextField upon button press with its placeholder text set to whatever was in your other UITextField. This is assuming you just want to do this once and that you only have a single new UITextField. If you can do this multiple times, but still just want a single UITextField updated with new text, newTextField should be a property, and you check if it is not nil and alloc init if it is. If it's not nil (it already exists), you need only update the text/placeholder property with the new text.
If every time the user enters text, you need a new UITextField in a new location, the solution would be a little different. Again, all of these answers (mine and others) are making assumptions about what you are trying to achieve.
UPDATE: Allow me to update this answer appropriately, as was previously pointed out. The long-and-short of it would be to use #JoshuaNozzi's approach, as it will achieve what you're trying to do. Just updating this for completeness.

Resources