What is the purpose of accessibility and traits? - ios

I would like to find out the purpose of accessibility and traits.
What is the purpose of traits's list of properties list here.

An Accessibility Trait allows you to choose the best description for what an element in your application does.
accessibilityLabel
The accessibilityLabel for an element is read by VoiceOver, and is designed to be a quick, one or two word label for what the element is. For instance, a “share” button may have an accessibilityLabel of “Share”. An “email” button may say “Email”. You get the idea. The goal is to give a brief word or two to give the user an understanding of what the element is and/or does. To implement, just go ahead and set the #property on the element:
[self.saveButton setAccessibilityLabel:#"Save"];
accessibilityHint
The accessibilityHint is designed to be a more lengthy description to be ready by VoiceOver. For instance, in the case of the “save” button above, you may want it to say something like “Saves the current information and returns back to the list of articles.” The #property is set similarly to the accessibilityLabel:
[self.saveButton setAccessibilityHint:#"Saves the current information and returns back to the list of articles."];
accessibilityTraits
You don’t have to use this for common UIKit controls, as it comes by default with the traits you’d imagine. But check out Apple’s WWDC ‘13 session on Accessibility for iOS and you’ll see how they adjusted the traits for some buttons.
AccessibilityTraits can be OR’d together to return multiple options, or just return a single one. As with the others, you can override this in a custom subclass or set it via it’s #property:
- (UIAccessibilityTraits)accessibilityTraits {
return UIAccessibilityTraitsButton;
}
Check ThisLink for more information

Related

Can we access the objects in Storyboard by ObjectID

I have a bunch of static objects (UILabel, buttons, views) in multiple Scenes. They are not connected to any IBOutlet. But I'd like to access them at appdelegate (or first VC), and change their properties before it is loaded.
Anyway to do this?
EDIT: Adding my intention:
I actually wanted to make a custom "multi-language" app. I want to be able to change language from within the app. I can get a list of all the objects by applying built in localization of storyboard (Main.strings is autogenerated). Then I disable localization again. Then from this autogenerated file, I want to be able to connect it to a json data based on language that I select.
Of course you can. For example, you can use tags of UIView. Just set tags in Storyboard. It's easy but not so good. Another way to do this is using Accessibilities. Enable and set for it in Storyboard.
And then you can access it by accessibilityIdentifier property.
I will post my choice of "solution". So what I did was make use of accessibilityIdentifier to set the "key" for the multilanguage phrase translation purpose.
And I make use of the UIView+Recursion class (you can find this simple class somewhere in SO), and basically iterate all the objects in a particular Scene and if the text matches, set the key in accessibilityIdentifier property (either in viewDidload or viewWillAppear or viewDidlayoutSubviews).
This way you can have language changes "on-the-fly" within the app, without restarting.

iOS UITest - Navigate to all available screens

I am using iOS UITest for a Swift application. I use something like,
func testAllScreenNavigation() {
let app = XCUIApplication()
app.tabBars.buttons["Home"].tap()
app.navigationBars["Home"].buttons["More"].tap()
app.sheets.buttons["Cancel"].tap()
}
etc. to navigate some of the specific, tabs, buttons, etc. and switch to respective screens. But i want to navigate each and every screens of my Application (It can be BFS style navigation or DFS style navigation, no matter). Is there any way iOS provides so i can get all navigable elements and then explore deeper and deeper automatically for my App?
I also need to keep trace of which xcuoelement in a screen is already processed and which are not yet processed.
The only way I can think of is using Xcode UI test recorder feature.
While you are recording, navigate through all of your screens via the device/simulator and then the XCUIApplication() variable would be recorded with the appropriate references.
If the button/nav bar/any element has text on it, it will show up in the recorded code or else it will be referenced numerically.
Hope that helps.
Kind regards,
Mukund
I like your idea for getting all views and check whether the layouting and localization for example is fine.
I think you need to specify your criteria for "screens" and how they are accessed.
Basically, one could thing of the following structure
- UITabBarController
-- UISplitViewController
--- UINavigationController
---- UIViewController
----- UIBarButtonItems
----- UIView
----- UIButton
----- UISwitch
----- UITableViewCell
You could now go top down from the UITabBarController to the next controlling instance (might also skip one, e.g. SplitViewControllers on iPhones).
You can use the general property:
XCUIApplication().tabBars
Nevertheless that transition is the problem: How would you get from one ViewController to another and are they all position in the ViewController's View or do you have to loop the subviews of a view.
UIButton -> Touch Up Inside
UISwitch -> Value Changed
UITableViewCell -> DidSelectRowAtIndexPath
UIView -> UILongPressGestureRecognizer
This is how I would basically set it up:
For each UIViewController instance, get the related View (and perform the following call recursively).
Check all the subviews of a view.
For UIViews, go even further and check their subviews
For UIButtons, perform TouchUpInside
and so on.
Make sure to have a condition to stop going deeper, as UITableViews got a lot of subviews or your UIWebViews would of course be set up in a different way.
This way you should be able to navigate through a lot Views in your app hierarchy, but you will need some extensions for UIBarButtonItems, custom Gesture Recognizers and of course also for your "special" controls that might listen to value changes and perform a layout-change.
Accessing specific elements
In addition to the above approach where you simply get an array of elements of a specific type, you can access specific elements (e.g. those where you know they are of a very specific type with certain ValueChangeListeners or something)
To access a specific object in particular, like the TabBar example from above, you can use the accessibilityLabel like so. At first you need to declare the accessibilityLabel in your code or in the .xib-file/.storyboard:
// just to illustrate, so you get an idea:
self.tabBarController.isAccessibilityElement = true
self.tabBarController.accessibilityLabel = "tabBar"
And then do:
let tabBar = XCUIApplication().tabBars["tabBar"]
Here is Apple's documentation for setting these accessibilityLabels:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html
A great way to get the related identifier of an element would be to use the Accessibility Inspector from Apple:
https://developer.apple.com/library/content/technotes/TestingAccessibilityOfiOSApps/TestAccessibilityiniOSSimulatorwithAccessibilityInspector/TestAccessibilityiniOSSimulatorwithAccessibilityInspector.html
Accessing elements in general
To access elements in general, you need to make use of the XCUIElementType of these objects, here you will access the objects based on their classes.
E.g. you could call:
"tabBars", "navBars", "tables", "buttons", and so on from the elements in general.
Still you would be facing the issue with "special controls". As the Apple documentation lacks (imho) some detail about properties and attributes, I do recommend the docs here: https://blog.metova.com/guide-xcode-ui-test/ It provides a great overview of what is accessible and may help you getting some better understanding.
An overview of the available XCUIElementTypes can be found here. Basically, the elementType property is an enumerated value that represents the type of an element. XCUIElementType is a very large enumeration and some of its members do not apply to iOS applications (they apply to MacOS X apps). Some of the more commonly used values are:
Alert
Button
NavigationBar
TabBar
ToolBar
ActivityIndicator
SegmentedControl
Picker
Image
StaticText
TextField
DatePicker
TextView
WebView
https://developer.apple.com/reference/xctest/xcuielementtype?language=objc

Xcode UI Testing [xcode7-beta6] - Asserting actual label values when using accessibility labels

The question is actually really simple:
Is there a way to assert the displayed value from a specific label (e.g. UILabel) when using an accessibility label on this object?
As far as I see it, all the assertions (e.g. XCTAssertEquals) made in the examples, be it from a WWDC Talk or Blogposts, are only checking if an element exists for a query like XCTAssertEquals(app.staticTexts["myValue"].exists, true) or if the number of cells in a table is correct XCTAssertEquals(app.tables.cells.count, 5). So, when avoiding accessibility labels it's possible to check if an object has a certain value displayed, but not which object / element.
And when using accessibility labels, it robs me of the opportunity to query against the displayed values, because app.staticTexts["myValue"] will now fail to deliver a result but app.staticTexts["myAccessibilityLabel"] will hit.
Assuming I want to test my "Add new Cell to Table" functionality, I can test that there is really a new cell added to the list, but I have no idea if the new cell is added at the top or the bottom of the list or somewhere in between.
For me, an easy way to check if a specific element has a certain value should be a no-brainer when it comes to UI Testing.
It is possible that due to the missing documentation I might overlook the obvious. If so, just tell me.
Be sure to set the .accessibilityValue property of the UILabel whenever you set its .text value. Then in UITest, you can test the accessibility value like this:
let labelElement = app.staticTexts["myLabel"]
...
XCTAssertEqual(labelElement.value as! String, "the expected text")
I think you are asking a few different things, so I will try to answer each question individually.
Is there a way to assert the displayed value from a specific label (e.g. UILabel) when using an accessibility label on this object?
In short, no. UI Testing works by hooking into accessibility APIs, so you are limited to querying for objects based on that. You can, however, check the -value property of certain elements, such as controls. This is used to test if a switch is on or off. Note that these boil to down using accessibility APIs as well, just a different method (-accessibilityValue over -accessibilityIdentifier and -accessibilityLabel).
...but I have no idea if the new cell is added at the top or the bottom of the list or somewhere in between.
To interrogate an XCUIElement for its frame you can use the new XCUIElementAttributes protocol which exposes -frame. For example:
let app = XCUIApplication()
app.launch()
app.buttons["Add New Cell to Table"].tap()
let lastCell = app.cells["Last Cell"]
let newCell = app.cells["New Cell"]
XCTAssert(newCell.exists)
XCTAssert(newCell.frame.minY > lastCell.frame.maxY)
For me, an easy way to check if a specific element has a certain value should be a no-brainer when it comes to UI Testing.
If you think of everything in terms of accessibility this becomes a non-issue. UI Testing can only interact with your elements via accessibility APIs, so you must implement them. You also get the added benefit of making your app more accessible to user's with those settings enabled.
Try setting both the -accessibilityLabel or -accessibilityIdentifier for the cell to the displayed text. UI Testing can be finicky as to which one it uses.
It is possible that due to the missing documentation I might overlook the obvious. If so, just tell me.
XCTest and UI Testing don't have any official documentation. So I've gone and extracted my own from the header files exposed in the framework. Note than even though they were pulled from source, they are unofficial.
XCTest / UI Testing Documentation
What works for me is to set the accessibility identifier of the UILabel to let's say MyLabel.
func myLabelText() -> String {
let myLabelUIElement: XCUIElement = self.application.staticTexts["MyLabel"]
return myLabelUIElement.label
}
Tested with Xcode 8 and iOS 10
From the apple forums it looks like it is possible to get the value of the label:
The only way I've found is to not set an Accessibility Label, but use identifier instead. Then XCUIElement.label will change to match the current text of the label.
However there is a gotcha: if you have previously set Accessibility Label in XC, and remove it, an entry setting the label to "" remains in the storyboard. In this case, not only will calling .label will return "", but you won't be able to query for the label by it's text!
The only thing you can do is delete and re-add the label, or manually edit the xml.
lastobelus - https://forums.developer.apple.com/thread/10428

iOS Interface Builder: How to make templates

I've recently started developing an iOS app, which I've never done before, so it's been going a bit slow, but I'm learning, so that's understandable.
I want to make a custom interface, so I've been making subclasses of the default view classes (like UIButton) so that I can define custom drawing. I've been told this is the best way to define custom interface elements that can be reusable. It definitely seems to be working that way. However, I haven't been able to make elements completely reusable by just using a subclass.
For example, in order to prevent a button's text from changing color when it is clicked, I have to manually go into the interface builder and set the button type to "Custom." After that, code that I enter into the subclass's constructor to change attributes seems to work. But I have to do this for every button I add, and in code the "buttonType" attribute is read only. Is there a way for me to define (just once) certain attributes for every instance of my button subclass that I add to the interface?
My goal is to be able to have a button subclass or template that defines all attribute values that I want my buttons to have, and every instance that I add automatically reflects those properties without me having to change anything. More so, I want to be able to modify that subclass/template and have those changes reflected in every existing instance. I have to imagine that this is possible in iOS. There is simply no way to build sophisticated interfaces without this capability.
Define a custom Button class (inherited from UIButton) in your project and in the init set the properties which you wanted to be set across.
In the interface builder go to the the class inspector and enter the button to be of the previously declared button.
buttonType needs to be set for all the button as this is defined at initialization time and exposed as read only property. If you want absolute reusability for your case, create a view, with an embedded button in code. when you create a button, create using the static method buttonWithType.
Wherever you need, drag and drop a UIView and set the view type to be the custom view.

Custom UITextInput implementation not showing multi-stage input suggestions

I have a custom UITextInput-based text editor. It works very well, except for multi-stage input via marked text.
My marked region renders correctly, and marked text is inserted, but the candidate list above the keyboard is blank.
For example, here is the Japanese (Kana) keyboard showing suggestions on a standard UITextView:
And here is my custom editor displaying the same marked text:
I have spent several days debugging this issue and have found that the cause is private class UIKeyboardImpl returning NO for the method delegateSupportsCorrectionUI
If I override this method in a category on UIKeyboardImpl and return YES instead, then multistage input suggestions correctly display for my text editor. However this does not address the underlying cause of the problem (and it's not usable).
I have also looked very closely at Apple's SimpleTextInput sample code. This implements a basic Core Text editor. SimpleTextInput correctly displays multistage input suggestions, however I cannot seem to find a single difference in its implementation of UITextInput that causes it to work and mine to break.
(In fact, I am unable to "break" the SimpleTextInput sample's ability to display multi-stage input. Which leads me to think that my focus on the UITextInput implementation is the wrong track. And it is something else altogether.)
Okay, this is a bit embarrassing. I just now noticed:
#property(nonatomic, readonly) UIView *textInputView
Discussion
The view that both draws the text and provides a coordinate system for
all geometric values in this protocol. (This is typically an instance
of the UITextInput-adopting class.) If this property is unimplemented,
the first view in the responder chain is selected.
In the documentation.
I had stupidly #synthesize'd this property and forgot about it, meaning my UITextInput implementation was returning a nil textInputView. Simply leaving it unimplemented chooses the first view from the responder chain as described, which provides the text system with the necessary coordinate system to handle auto-correction and multistage input suggestions.
This was after three days of debugging. Now I feel stupid.

Resources