VoiceOver and Scrolling in OpenGL backed UIViews - ios

I am looking for advice on how to make a scrollable list drawn with OpenGL into a CAEAGLLayer backed UIViews accessible with VoiceOver. Ideally, the scrollable list in OpenGL should behave just like a native UIScrollView:
When the user swipes left/right with three fingers I want to scroll up/down a page. This behavior is easily achieved by implementing the accessibilityScroll: method.
When the user swipes left/right with one finger to go through all items in the list and the accessibility focus moves to the last/first partially visible item of the list I want the list to scroll slightly to ensure that the newly focused item is fully visible.
When the user taps directly on the last/first partially visible item of the list to move the accessibility focus directly to it I want no scroll action to happen.
When the user continues to swipe left/right with one finger while the last/first partially visible item of the list is selected, I want the list to scroll up/down one page to reveal previously hidden items.
As I said above, I know how to implement item #1, but after extensively studying Apple's documentation I still have no idea how to make items #2 to #4 work with the iOS accessibility API. I do think that those items are important for a great accessibility experience, though.
Any advice is greatly appreciated. Thanks!

UIAccessibility is an informal protocol that vends the data necessary for accessibility clients such as VoiceOver to interpret and navigate a user interface. UIKit classes implement the full suite of methods in this protocol, rendering most views accessible by default. However, interface elements that aren't directly backed by a UIView, such as the view containing your CAEAGLLayer, must manually implement all relevant accessibility methods.
The best, and often only, way to render this content accessible is to implement a shadow hierarchy of UIAccessibilityElement objects corresponding to the content of the scene. To do so, you must declare the UIView in the UIKit view hierarchy to be a UIAccessibilityContainer. This is a two step process. First, you must ensure that the view backing your container is not, itself, an accessibility element, either by setting or overriding isAccessibilityElement to return false. Second, you must implement the four methods defined in the informal UIAccessibilityContainer protocol for accessing child accessibility elements:
func accessibilityElementCount()
func index(ofAccessibilityElement element: Any)
func index(ofAccessibilityElement: Any)
var accessibilityElements: [Any]?
In most cases, you will want to maintain an array of UIAccessibilityElement objects, each representing one component of your custom-rendered user interface. This array can be used to fulfill your backing view’s contract as an UIAccessibilityContainer. You can then extend elements to behave like scroll views, buttons, and other controls using methods in the UIAccessibility protocol.
To accomplish #1, you will want to implement accessibiilityScroll() on the an element in your accessibility hierarchy to respond to scrolling actions, returning true and posting a UIAccessibilityPageScrolledNotification with a string parameter describing the new scroll position. It sounds like you have this part working without issue.
Requirements #2, #3, and #4 all require programmatically scrolling your view when an element receives focus (or, in the case of #3, opting to do nothing). This can be accomplished using the UIAccessibilityFocus informal protocol. In particular, each element can respond to the func accessibilityElementDidBecomeFocused() and communicate its focus status back up to the object responsible for scrolling the view. If this triggers a paginated scroll, don't forget to post the page scrolled notification discussed, above.

Related

Advantages of tableView vs. creating UI elements in code?

I have a feature that allows users to tap the side of the screen to go through content (like instagram stories). The information is presented in different formats:
example of screen types
Currently, I am just creating UI elements (through CGRect) and placing the content on the screen according to the template type that comes in. There are many screen types and the canvas has to repaint with each switch. Should I be utilizing tableView for speed (development or otherwise)?
Repainting on condition
Is this bad practice to constantly reload and recreate items with each tap?
A table view is a vertically scrolling sequence of "cells" and has nothing in common with the situation you are describing, as far as I can see.
Actually what you are describing might be a UIPageViewController, which lets you "page" through entire screens of material and yet create each "page" dynamically according to what comes "next" in the series.
Each page would be its own view controller and could configure its own views. There would be no subviews to remove; rather, the "next" view is just sitting there waiting to be asked for. Each time the user changes the "page", the old screenful of views just goes away after sliding off the screen.

Implement iOS home screen folder

I have a collectionView with each cell showing an image. I want to implement a drag and drop. This will solve two purpose.
Rearrange cells.
When a image is dragged into other, it forms a collection (think of it as a group containing two or more images).
This behavior is inline with the home screen folder where app icon can be grouped into folders.
Please suggest how I can implement this.
This isn't trivial but it is not impossible either.
Reordering is simpler, and you can take advantage of UICollectionView's methods:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/#//apple_ref/doc/uid/TP40012177-CH1-SW67
Reordering Items Interactively
Collection views allow you to move
items around based on user interactions. Normally, the order of items
in a collection view is defined by your data source. If you support
the ability for users to reorder items, you can configure a gesture
recognizer to track the user’s interactions with a collection view
item and update that item’s position.
To begin the interactive repositioning of an item, call the
beginInteractiveMovementForItemAtIndexPath: method of the collection
view. While your gesture recognizer is tracking touch events, call the
updateInteractiveMovementTargetPosition: method to report changes in
the touch location. When you are done tracking the gesture, call the
endInteractiveMovement or cancelInteractiveMovement method to conclude
the interactions and update the collection view.
During user interactions, the collection view invalidates its layout
dynamically to reflect the current position of the item. If you do
nothing, the default layout behavior repositions the items for you,
but you can customize the layout animations if you want. When
interactions finish, updates its data source object with the new
location of the item.
The UICollectionViewController class provides a default gesture
recognizer that you can use to rearrange items in its managed
collection view. To install this gesture recognizer, set the
installsStandardGestureForInteractiveMovement property of the
collection view controller to YES.
To implement the "grouping" behavior, you need to define your cell in such a way that it can determine whether it is a single item and should just display itself, or if it consists of multiple items, in which case it should be able to pop up a view to act as a tray, possibly its own view controller to handle taps independently. This part is going to be hard as you'll need to implement reordering again, and dragging out, etc.
You might want to search Github and others to see if anyone has implemented a "Springboard" type of app that can get you started.

Dynamically Change isAccessibilityElement

I've got a bit of a weird situation. I need to have an element not be read out by VoiceOver when I use the 2 finger swipe method, but to be read when tapping on it still.
The object is part of a TableView cell, and I've given the TableView cell its own accessibilityLabel, because it contains two interactive elements, one of which doesn't actually need to be read when tapped on, so I've disabled its accessibility property.
However, my other one needs to be read still when tapped on. The issue is, it's already being read as part of the cell's accessibilityLabel, and then it is read again because it is still an accessible element. Is there any way to differentiate between why VoiceOver is reading an element? Or to dynamically change the accessibilityLabel?
You can dynamically change accessibilityLabel simply by assigning it or overriding the method on the accessible view. However, you shouldn't rely on VoiceOver respecting the change in real time.
Users can navigate via tap or swipe and expect elements to persist regardless of how they were reached. In general, I discourage clever solutions that assume how users interact with VoiceOver.
I'd encourage you to either override the cell summary to omit the label or disable accessibility on the label and leave the content in the cell summary.

Need Suggestion to Swipe through Views

I have a UITableView on didSelectrowAtIndex it pushes to a viewController which shows the detail on UIScrollView which is vertically scrollable.
Now I need that If I tap on any row it would be pushed to the same viewController and shows the detail of selected row,Additionally I need to have the Swipe Functionality to see the data for all rows in tableview instead of go back & select another row.
I know it can be achieved by UIPageControl or [scrollView setPagingEnabled:YES];,But I am wondering If there is any better approach to do the same or I should go with any of these two,If yes then Which one is better?
Please Help Guys.....Any Help would be much appreciated.
Thanks in Advance....:)
Of course, there are plenty of approaches to achieve what you want. You should keep in mind that UIPageControl has limitation on number of items (dots) displayed (over 20 items would overlap). Also, it would be appropriate to have next/previous buttons for the users preferring tapping buttons instead of swiping/panning. I prefer using UISwipeGestureRecognizers and buttons on toolbar for navigating between items.
The key, in my opinion, is to control how many UIKit controls are held in memory at the same time. However you implement this, you want to make sure that you have solution that holds UIKit controls for the currently visible child element only, or perhaps the previous and next ones, too, but not for the full array (especially if you're dealing with UIImageView objects).
One control to consider is UIPageViewController. At first blush, it might look complicated, but in reality it's quite simple and offers a nice way to swipe between various "pages" while not holding all of the pages in memory at the same time. See View Controller Catalog for iOS: Page View Controller.
If you decide to implement a scroll view with paging enabled, I'd suggest specifying the delegate and implementing the UIScrollViewDelegate methods to add and remove subviews as appropriate. This is historically what I've done, but I now lean towards page view controllers.
There are also a ton of third-party implementation of "infinite scrollers", which implement this sort of functionality, though I can't vouch for any particular one.

iOS Nested View Hierarchy to support drag drop functionality to allow end-user to configure forms

I'm building a native iOS app that will allow end users to build forms by dragging and dropping from a toolbox of custom controls. A typical scenario would be a power-user dragging some UILabels and UITextField representations around on a UIView. Groups of controls would be supported, allowing several controls to be visually contained within a section. The whole thing will probably be contained in a UIScrollView allowing horizontal scrolling between 'pages'.
'Normal' users will then use the form for data entry.
So, I have two related requirements: to allow different types of UIView/UITableView/UIImageView/UILabelView/UITextField etc to be laid-out and grouped (and probably contained within an outer UIScrollView), and to allow the layout to be adjusted/dragged/dropped by power-users.
My question is rather broad, but is essentially this: what is the most appropriate nested UIView hierarchy to support these two related requirements? Is a basic structure of UITableViews contained within an outer UIScrollView fundamentally sound if I want to drag-drop within nested views within the UITableView? Should I avoid UITableView because it will complicate dragging?
I appreciate this question is borderline subjective in scope, but I'd really appreciate some guidance from people who have tacked this sort of thing before and who have some words of wisdom for me.
Many thanks.
Sounds like an interesting app! I would say that using a UITableView would make the app horribly complicated. A UIScrollView would be a good base to drag and drop controls onto. Perhaps with an "add page" button, the user could tap this it required, and you could extend the contentSize of the UIScrollView to be the width of +1 pages, and then scroll it so the user can drag more controls onto that page.
To drag the controls around, you could use a UIPanGestureRecognizer attached to each control, and when triggered it changes the centre position of the control. You might need to turn off user interaction of some of these controls - e.g. for a UITextField the power user will want to drag it around but not want to enter a value into it.
For grouping controls, you could do something like this:
a "group mode", whereby the user taps a button to enter this mode, then taps a number of controls (would need some visual indication on them to show they are selected) and then taps done.
the selected controls are then be removed from the UIScrollView
a new UIView is created and positioned at the centre point of the selected controls, and is big enough for the controls to fit in at the same distances apart. It is added as a subview of the scroll view
the controls are all added as subviews of this view
the gesture recognizer is added to this view, instead of the individual controls. Then when dragging around, this group of controls will all move as a group with fixed layout.
If you have groups of controls that you think might be commonly used, you could even create them in advance, each in their own nib, and then allow the user to drag them onto the scroll view as a pre-made group.
What you might find, especially if some of your controls are quite large (e.g. I'd expect an image view to be significantly bigger than a label or text field), the pan gesture recognizer gets a bit limiting because when trying to drag views around you'll inadvertently pick the wrong one if they are positioned close together or overlapping. In which case, you might need the extra precision of handling all the touch events yourself - so when a touch starts, you test against all the controls (or groups) to see which has the closest centre to your tap position, and then as you get the touch moved events you can update this centre position.
I've not made anything quite as complex as what you're describing, but I did make an app where the user could drag small images onto a large image to apply as "decorations". They could drag on any number of these decorations, and also scale them with a pinch gesture. In this case, I had a UIImageView as a background which held the main image. Then the decorations were on the edge of the image, and a pan gesture recognizer was used to detect them being dragged onto the image. On drag, I'd actually create a new instance of the decoration (UIImageView) so that there was always another one left in the toolbox. A pinch gesture recognizer was used for the user to scale the decorations. For the user to move around a decoration they had already placed, I had to use manual touch handling to detect the right decoration (since they could easily be overlapping, and ones that looked round to the user are actually square in terms of UIViews, so it was easy for the user to accidentally drag the corner of one when they intended to drag a different one). Mixing an matching gesture recognizers and manual touch handling seemed to work out just fine, which was good because it's much easier to use the gesture recognizers for the more complex behaviour like pinching.
Back to your app, once your power user has set up everything, then when the normal user loads the app, you can just turn off any touch handling code (and remove or don't create the gesture recognizers) and they will get static forms as laid out by the power user. Then make sure user interaction is enabled on all of the controls (e.g. UITextField) and they will be able to enter data into them.
Doing all of the above with controls onto a scroll view will be complex enough I think, and you may end up having to deal with lots of niggly behaviour in trying to get it working nicely for both normal and power users. I think this would be 100x harder if you were also dealing with UITableViews and UITableCells at the same time.
Please let me know if you'd like me to elaborate on any aspect of the app I outlined above, as it does seem to have a fair chunk of functionality in common with your app. Hope to see/hear more about your app in the future!
EDIT
One more thing occurred to me - if you are keen to use a UITableView in your solution, then I would suggest that your power user lays out one UITableViewCell at a time. In fact, they would be dragging UILabels and other controls onto just a basic UIView, which when they've finished you then use to record the positions of the controls. You then use these positions when presenting those controls in a cell - in cellForRowAtIndexPath you would create and init all of the chosen controls, and position all of them in a newly created cell at the positions and layout the power user had chosen. The power user could also have a control to change the height of the cell they are laying out, for more flexibility. They would create a series of cell layouts one after the other (I guess each of these would be a "group" of controls), and then those cells would be presented in order in the table view. You could then give the power user some final tweaking control by letting them put the table into edit mode so they can reorder the cells (or even remove some).
Depending on the application, perhaps the user could also have these cell layouts they've previously created always available, so after they've built up a few common control groups, they can just keep reusing them to very rapidly build up a form. And then occasionally they would create a new cell layout when none of the ones they have created so far are suitable, and again it would be saved as a template for them to use again in future forms.
I would just make an EditView class that is just a UIView with some transparent background color. When you want to drag controls around, then set some type of edit mode where you overlay a bunch of EditViews on all the appropriate controls. The EditView would then support whatever gestures / touch handling you want to resize or move it. Dragging a view onto another view and releasing might prompt something like "Nest view A in view B?" which then you can make the related addSubview calls.
Nesting tableviews inside scrollviews won't be an issue, though I'm not sure what kind of behavior would be expected if you were trying to nest anything inside a tableview...that would have to be specified using standard UITableView methods.
For saving layouts and such, you might need to create your own pseudo-view hierarchy that contains various pieces of meta-data needed to recreate the final layout which you would then be able to store somewhere.

Resources