Working with Accessibility
While VoiceOver reads the elements in the application in an order,Is there anyway to shift the focus between the elements?
I tried working with "nextResponder",but it is not working.
As of iOS 6, you can set the focus to a specific element with a UIAccessibilityLayoutChangedNotification, passing the element
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, firstBottom);
but if you're trying to completely change the “tab order” I don’t know of a way to do it.
This is a shot in the dark, but have you tried changing the accessibilityLabel or accessibilityHint in accordance when you want the order to change? If you can trick the VoiceOver to believe the text has changed, I would expect that it would change focus to it appropriately.
The timing would be the hard part, since it doesn't appear there are any delegate callbacks for when VoiceOver is crawling your view, so you may have to estimate the time to pass before trying to update the accessibility hint/value.
My last thought would be to mark the UIView that you want to bring attention to with the UIAccessibilityTraitUpdatesFrequently accessibility trait. That might be the closest you can get without tapping into hidden Apple libraries.
Check out this post for how to handle special ordering of elements for voice over. I just used this approach in the app I'm working on.
I tried UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, elementName); to change the focus on a different element. The behavior was that the focus got shifted to new element, but Voice Over would first announce the page title and then the accessible label value of the new element.
Customise Accessibility for a View:
You can customise the order(in which voice over should traverse the screen elements) by overriding accessibilityElements property of parent view in below manner.
self.accessibilityElements = [childView1, childView2, childView3]
With that voice over will follow the sequence like childView1 -> childView2 -> childView3.
Changing Accessibility focus to other element programatically:
At any time you can shift the focus to another element by using below code.
UIAccessibility.post(notification: .layoutChanged, argument: childView2)
With above code, voice over focus would be shifted to childView2 and then will follow the same sequence defined by accessibilityElements i.e. childView2 -> childView3 -> childView1... and so on
Customising Accessibility Order for Complex Views:
You can customise it further and If a view has multiple child views with further grand children views, then you can achieve accessibility order by defining accessibilityElements of main parent view by using accessibilityElements of all child views.
For example, for below view hierarchy, we have
View Controller Example
To define custom order of accessibility elements for such views, we can define in below manner.
var customElements = childView1.accessibilityElements
customElements.append(contentsOf: childView2.accessibilityElements)
customElements.append(contentsOf: childView3.accessibilityElements)
parentView.accessibilityElements = customElements
Related
I am building an iPhone app for a library and I want to give few options to the user to search books by. A user can search a book by title, author, topics, or date of publication.
What I want to know what's the best way to display these search options to the user? In terms of HTML, it would be easy - either use a drop down list or radio buttons. I tried using UIPickerView but honestly, that looks terribly ugly and destroys the aesthetics of the view of my app.
The other option I was thnking was using segmented control, but is it possible to have a vertical segment control in stead of horizontal one? The selection texts can be too long.
Any ideas?
If you want a vertical segmented control, you'll have to make it yourself. Create a textured image with dividers. I would export each section as a PNG separately. Then create a picture of each section with the "pressed down" gradient and export each segment as a separate PNG again.
I would then make a new class. If there is a specific number of objects in your segmented control or this is a one time thing, the class may not even be necessary. If not, then in the class constructor pass an array with the titles of the segments in your segmented control. For the first and last objects, use the pictures you made with rounded corners. For the objects in between, use the standard pictures. Then put the titles on top. When a segment is tapped (perhaps use hidden UIButtons), you can use a delegate method to tell the main search class which one was tapped, and then the class can replace the normal picture of that segment with the pressed down one.
Thanks for the options.
I ended up creating a simple table to show my choices.
User clicks on "search by", which opens up the table with options and then when you select any option, you return back to main view with the chosen search option.
In IB I have quite a few views that are shown. Many of them are hidden when the app loads, but are shown later when buttons are pressed. This is all fine, but when building this layout in IB it is extremely difficult to layout anything because there are so many overlapping views, some of which are partially transparent (ones that are set to hidden) and other are completely overlapping and covering others. This makes layout very hard.
What is the best method when laying out lots of views like this? Is there another way to break things up? Or better yet, can I hide a a view completely (like in photoshop) so that I can edit the ones underneath, then turn that layer back on?
Another option when trying to select a view that is obscured by another is the shortcut:
'ctrl' + 'shift' and click
It displays a list of all the views under the cursor.
I'm not aware of any way to hide objects in the canvas, but a useful trick for complex layouts is to double-click an item in the document tree to the left - this selects the item and puts focus on the canvas, you can the use the cursor keys to nudge it about.
This doesnt solve the problem of not being able to see things because there are, for example, five or six labels occupying the same space, but if that is the situation it may be a better idea to have a single label and change its contents in code.
I ran into this issue for an app I'm building that has an arial-view image of a park with clickable hotspots. When a hotspot is clicked a popup UIview is displayed with information about that spot in the park. I use the same VC/XIB for three parks. This makes the XIB really busy and hard to work with (i.e the same issue that you have) The detail UIViews make it hard to work with the views underneath. My workaround was to pick each detailed UIView that was hiding the part of the XIB I wanted to work on, and add 1000 to the UIView origin.x in the size inspector. This moved those UIViews enough out of the way for me to do what I needed to with the XIB. Then when I was done, I moved them back by x 1000. (I just needed to move them out horizontally to do what I needed to)
I know its clunky but given that XCode does not have a convenient way to hide portions of an XIB - it was the quickest approach I could think of!
One approach to handling overlapping items in IB is:
Ensure the groups of items that you want to hide are grouped into Views.
Give these Views names: e.g. ViewOptionA, ViewOptionB and ViewOptionC.
Can do this by clicking on name of view in the tree while it is selected and then typing new name.
When you want to hide one of those groups of items:
a) Select the View by either:
i) Clicking on it in the tree at the left or
ii) Ctrl-Shift Clicking in the layout editor and then select the view from the list.
b) In the Attributes Inspector set Alpha to 0.
When you want to unhide one of those groups of items:
As for 2) but set Alpha back to 1
[You do need to remember to unhide all views before you publish!
If you are forgetful like me then perhaps you could subclass UIView and set Alpha to 1. I haven't tried this subclassing idea yet.]
I don't quite understand the difference between these two methods. In what situation would forceFocus() be better than setFocus()?
According to SWT: The Standard Widget Toolkit, Volume 1, forceFocus():
Forces the control to receive keyboard
events. Controls that do not normally
accept keyboard input will take focus
using this method. If focus cannot be
assigned for any reason, false is
returned.
also:
Generally speaking, forcing focus is
something that you never want to do.
For example, forcing focus to a label
is not very useful because labels
don't draw in a manner that indicates
they can accept input. Forcing focus
to a control that does not expect it
can confuse users because they have no
idea where their keystrokes are going.
Application programs should always use
setFocus() to assign focus.
Coming in way late on this one, but I just finished a prolonged head-beating-against-wall session on something related to this and thought I'd do a quick report:
If your control has children, the setFocus(myControl) will do a depth-first search down the child tree and set focus to the lowest first child. On the other hand, forceFocus(myControl) will just set focus to myControl and have done with it.
I had a situation with a modeless dialog with a GridLayout, where most of the grid cells were a set of Canvas subclassed objects that needed focus. (Yes, this is a bit odd, but I wanted to be able to hover over a cell and type stuff into it.) The last control in the grid was a "done" button, which I set as the default selection.
When the mouse left the canvas cells, I was doing a setFocus(myDialogShell), which instantly set focus to the first canvas element, preventing the "done" button from receiving the \r and disposing of the whole sordid business. On the other hand, forceFocus(myDialogShell) did the trick.
Just getting started on iPhone dev today and have run through Apple's HelloWorld tutorial:
http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhone101/Articles/02_CreatingProject.html#//apple_ref/doc/uid/TP40007514-CH3-SW3
Couple of easy questions for you folks (sorry so long-winded but I'm hunting around in the dark here):
1) So I made MyViewController the delegate for the text field, in order to receive a message when the return key is pressed (textFieldShouldReturn). This seems inelegant because the method has to check which control sent the message. But to handle the touch event on the button the technique is different -- a custom message (changeGreeting) gets sent which could be associated with one or several buttons in Interface Builder, and I could create different messages for different buttons.
That seems much better and avoids the need to test in code which control sent the event. Why these two different approaches in the sample, is it just to demonstrate the two? Would it be possible to rewrite the sample only using the latter approach, or is there something fundamentally different between the text field's textFieldShouldReturn and the button's 'Touch Up' that mandates making the view controller a delegate for the text field?
2) When I position the text field near the top of the view, if it's a certain vertical displacement away from the grey status bar with the battery status symbol then it locks into place and you can see the vertical blue dashed line indicating the lock. In this mode, when I run the app the text field is too high, hard up against the status bar. I can resolve this by moving the text field very slightly so that the vertical dashed blue line no longer appears. Then the positioning seems relative to whatever's above the text field, as I can select a larger Top Bar in the Simulated UI Elements and it moves down nicely.
I'd expect to see some explicit property in the Attributes Inspector that says whether the layout is relative or absolute, but I can't find anything that changes between the two scenarios. Surely this is made more explicit somewhere in the Interface Builder UI?
Hope these Q's represent some easy points for someone...
1) You can connect the UITextField "Did End On Exit" event to an IBAction, similar to the Buttons "Touch Up".
The benefit of using the delegate is that you get a higher level of interaction for free with the UITextfield, without having to assign every event to an IBAction in interface builder.
2) In the Interface Builder Inspector, on the size tab (the little ruler icon) there are a set of controls which allow you to set the automatic positioning of a subview. I believe you are interested in the "Autosizing" section.
I want to create my own number pad to appear after user focus the textfield, so I have two question about it?
1.I use "Interface Builder to add a textfield in my view and select the "Number Pad" as the default pad for user to input number, so when I click the textfield, the number pad appear automaticlly, how can I stop it appear the number pad? because I want to show my number pad.
2.If I custom a view with number button inside it, how can I detect the event when I click the textfield? and whether after I detect the event I add a subview to show my custom number pad or not?
thanks
If you are creating a custom number pad (I assume this means a view with a grid of buttons), don't bother using a UITextField; there is no easy way to hide the native keyboard and, for all that trouble, there is nothing useful that the text field gives you.
I recommend creating a custom UIControl subclass. You can detect a touch inside the view and show your custom keypad that way. The documentation explains this pretty well.
I have a partial answer but not an ideal one, and I haven't tried this myself. Take a look at the documentation for the UITextFieldDelegate protocol.
You could have your controller set textField.delegate = self, then have it implement textFieldShouldBeginEditing to show your specialized keyboard somehow and then return NO so that it doesn't go into edit mode. When you tell your special keyboard to show itself, pass it a reference to the text field so it knows where to insert characters. The problem is that this probably won't show a cursor and won't let the user move the cursor to insert characters, etc.
So really this a bit ugly, but it may be sufficient. AFAIK there is no good way to do this :(