Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
Convert to using list of accessibility elements for iOS tab indexes (#…
Browse files Browse the repository at this point in the history
…11077)

* Convert to using list of accessibility elements for iOS tab indexes

* - add issues

Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>

fixes #6667
fixes #11026
  • Loading branch information
PureWeen committed Jul 16, 2020
1 parent e1bcaa3 commit 35e5dc9
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;


#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 7606, "[Bug] When a view appears it is not accessible via VoiceOver",
PlatformAffected.iOS)]
#if UITEST
[NUnit.Framework.Category(Core.UITests.UITestCategories.Github10000)]
[NUnit.Framework.Category(UITestCategories.ManualReview)]
[NUnit.Framework.Category(UITestCategories.Accessibility)]
#endif
public class Issue7606 : TestContentPage
{
protected override void Init()
{
Label visibilityLabel = new Label()
{
Text = "Swipe right and I should be read by voice over.",
IsVisible = false
};

Content = new StackLayout()
{
Children =
{
new Button()
{
Text = "Click Me",
Command = new Command(() => visibilityLabel.IsVisible = !visibilityLabel.IsVisible)
},
visibilityLabel
}
};
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;


#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 8613, "[Bug] Accessibility, screenreader ignores or skips items in nested stacklayout",
PlatformAffected.iOS)]
#if UITEST
[NUnit.Framework.Category(Core.UITests.UITestCategories.Github10000)]
[NUnit.Framework.Category(UITestCategories.ManualReview)]
[NUnit.Framework.Category(UITestCategories.Accessibility)]
#endif
public class Issue8613 : TestContentPage
{
Label CreateLabel(bool? IsInAccessibleTree, string text)
{
Label label = new Label()
{
Text = text
};

if(IsInAccessibleTree.HasValue)
AutomationProperties.SetIsInAccessibleTree(label, IsInAccessibleTree);

return label;
}

Entry CreateEntry(bool IsInAccessibleTree, string placeholderText)
{
Entry entry = new Entry()
{
Placeholder = placeholderText
};

AutomationProperties.SetIsInAccessibleTree(entry, IsInAccessibleTree);
return entry;
}

protected override void Init()
{
// Based on Sample
// https://github.com/xamarin/xamarin-forms-samples/blob/master/UserInterface/Accessibility/Accessibility/AccessibilityPage.xaml
Content = new ScrollView()
{
Content =
new StackLayout()
{
Children =
{
CreateLabel(true, "Voice Over Swiping should progress sequentially through all visible elements"),
CreateLabel(true, "Second Label IsInAccessibleTree = true"),
new StackLayout()
{
Children =
{
new Label()
{
Text = "Enter Your Name: ",
},
CreateEntry(true, "If Voice Over swiping gets stuck here test has failed"),
},
},
CreateLabel(true, "Label After the Entry")
},
},
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;


#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 8691, "[Bug] TabIndex is ignored for first element on page for VoiceOver",
PlatformAffected.iOS)]
#if UITEST
[NUnit.Framework.Category(Core.UITests.UITestCategories.Github10000)]
[NUnit.Framework.Category(UITestCategories.ManualReview)]
[NUnit.Framework.Category(UITestCategories.Accessibility)]
#endif
public class Issue8691 : TestContentPage
{
protected override void Init()
{

Content = new StackLayout()
{
Children =
{
new Label()
{
Text = "2nd TabIndex",
TabIndex = 20
},
new Label()
{
Text = "I should be the first element focused when voice over is on",
TabIndex = 10
},
new Label()
{
Text = "3rd TabIndex",
TabIndex = 30
},
}
};
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;


#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 9137, "A11y: Image in a11y tree stops voiceover from hopping to the next element",
PlatformAffected.iOS)]
#if UITEST
[NUnit.Framework.Category(Core.UITests.UITestCategories.Github10000)]
[NUnit.Framework.Category(UITestCategories.ManualReview)]
[NUnit.Framework.Category(UITestCategories.Accessibility)]
#endif
public class Issue9137 : TestContentPage
{
Label CreateLabel(bool IsInAccessibleTree, string text)
{
Label label = new Label()
{
Text = text
};
AutomationProperties.SetIsInAccessibleTree(label, IsInAccessibleTree);
return label;
}

protected override void Init()
{
Content = new StackLayout()
{
Children =
{
new StackLayout()
{
Children =
{
CreateLabel(false, "IsInAccessibleTree is false")
},
},
new StackLayout()
{
Children =
{
new StackLayout()
{
Children =
{
new Label()
{
Text = "Turn Voice Over on and verify that you can swipe all the way forward and then backwards. If you get stuck toggling between the same two elements test has failed",
TabIndex = 10
},
new Image()
{
TabIndex = 20,
Source = "coffee.png"
},
},
},
new Label()
{
Text = "Tab Index 70",
TabIndex = 70
}
},
},
}
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue10744.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue10909.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8613.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue9137.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8691.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7606.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11137.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11106.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8291.cs" />
Expand Down
1 change: 1 addition & 0 deletions Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ internal static class UITestCategories
public const string Github5000 = "Github5000";
public const string Github10000 = "Github10000";
public const string RadioButton = "RadioButton";
public const string Accessibility = "Accessibility";
public const string Shape = "Shape";
}
}
59 changes: 23 additions & 36 deletions Xamarin.Forms.Platform.iOS/Renderers/PageContainer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Foundation;
using ObjCRuntime;
using System;
using System.Collections.Generic;
using UIKit;
Expand All @@ -8,8 +9,9 @@ namespace Xamarin.Forms.Platform.iOS
internal class PageContainer : UIView, IUIAccessibilityContainer
{
readonly IAccessibilityElementsController _parent;
List<NSObject> _accessibilityElements = null;
NSArray _accessibilityElements = null;
bool _disposed;
bool _loaded;

public PageContainer(IAccessibilityElementsController parent)
{
Expand All @@ -22,22 +24,40 @@ public PageContainer()
IsAccessibilityElement = false;
}

List<NSObject> AccessibilityElements
public override bool IsAccessibilityElement
{
get => false;
set => base.IsAccessibilityElement = value;
}

[Internals.Preserve(Conditional = true)]
public virtual NSArray AccessibilityElements
{
[Export("accessibilityElements", ArgumentSemantic.Copy)]
get
{
if (_loaded)
return _accessibilityElements;

// lazy-loading this list so that the expensive call to GetAccessibilityElements only happens when VoiceOver is on.
if (_accessibilityElements == null || _accessibilityElements.Count == 0)
{
_accessibilityElements = _parent.GetAccessibilityElements();
var elements =_parent.GetAccessibilityElements();
if(elements != null)
{
_accessibilityElements = NSArray.FromNSObjects(elements.ToArray());
}
}

_loaded = true;
return _accessibilityElements;
}
}

public void ClearAccessibilityElements()
{
_accessibilityElements = null;
_loaded = false;
}

protected override void Dispose(bool disposing)
Expand All @@ -49,38 +69,5 @@ protected override void Dispose(bool disposing)
}
base.Dispose(disposing);
}

[Export("accessibilityElementCount")]
[Internals.Preserve(Conditional = true)]
nint AccessibilityElementCount()
{
if (AccessibilityElements == null || AccessibilityElements.Count == 0)
return 0;

// Note: this will only be called when VoiceOver is enabled
return AccessibilityElements.Count;
}

[Export("accessibilityElementAtIndex:")]
[Internals.Preserve(Conditional = true)]
NSObject GetAccessibilityElementAt(nint index)
{
if (AccessibilityElements == null || AccessibilityElements.Count == 0)
return NSNull.Null;

// Note: this will only be called when VoiceOver is enabled
return AccessibilityElements[(int)index];
}

[Export("indexOfAccessibilityElement:")]
[Internals.Preserve(Conditional = true)]
int GetIndexOfAccessibilityElement(NSObject element)
{
if (AccessibilityElements == null || AccessibilityElements.Count == 0)
return int.MaxValue;

// Note: this will only be called when VoiceOver is enabled
return AccessibilityElements.IndexOf(element);
}
}
}

0 comments on commit 35e5dc9

Please sign in to comment.