diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7606.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7606.cs
new file mode 100644
index 00000000000..8fa987aa62f
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7606.cs
@@ -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
+ }
+ };
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8613.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8613.cs
new file mode 100644
index 00000000000..e32cf653a94
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8613.cs
@@ -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")
+ },
+ },
+ };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8691.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8691.cs
new file mode 100644
index 00000000000..aee6454aad4
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8691.cs
@@ -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
+ },
+ }
+ };
+ }
+
+ }
+}
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue9137.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue9137.cs
new file mode 100644
index 00000000000..20e7828890d
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue9137.cs
@@ -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
+ }
+ },
+ },
+ }
+ };
+ }
+ }
+}
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
index b2ce36e9119..f9a4a982bac 100644
--- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
@@ -26,6 +26,10 @@
+
+
+
+
diff --git a/Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs b/Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs
index 5b96cb0e74b..64d2e486246 100644
--- a/Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs
+++ b/Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs
@@ -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";
}
}
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/PageContainer.cs b/Xamarin.Forms.Platform.iOS/Renderers/PageContainer.cs
index 7f4512905a1..1851da2aafb 100644
--- a/Xamarin.Forms.Platform.iOS/Renderers/PageContainer.cs
+++ b/Xamarin.Forms.Platform.iOS/Renderers/PageContainer.cs
@@ -1,4 +1,5 @@
using Foundation;
+using ObjCRuntime;
using System;
using System.Collections.Generic;
using UIKit;
@@ -8,8 +9,9 @@ namespace Xamarin.Forms.Platform.iOS
internal class PageContainer : UIView, IUIAccessibilityContainer
{
readonly IAccessibilityElementsController _parent;
- List _accessibilityElements = null;
+ NSArray _accessibilityElements = null;
bool _disposed;
+ bool _loaded;
public PageContainer(IAccessibilityElementsController parent)
{
@@ -22,15 +24,32 @@ public PageContainer()
IsAccessibilityElement = false;
}
- List 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;
}
}
@@ -38,6 +57,7 @@ List AccessibilityElements
public void ClearAccessibilityElements()
{
_accessibilityElements = null;
+ _loaded = false;
}
protected override void Dispose(bool disposing)
@@ -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);
- }
}
}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs
index 94951f14d72..de58ae9343c 100644
--- a/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs
+++ b/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs
@@ -44,18 +44,10 @@ void IEffectControlProvider.RegisterEffect(Effect effect)
public event EventHandler ElementChanged;
- List DefaultOrder()
- {
- var views = new List();
- if (Container != null)
- views.AddRange(Container.DescendantsTree());
- return views;
- }
-
public List GetAccessibilityElements()
{
if (Container == null || Element == null)
- return new List();
+ return null;
SortedDictionary> tabIndexes = null;
foreach (var child in Element.LogicalChildren)
@@ -68,11 +60,11 @@ public List GetAccessibilityElements()
}
if (tabIndexes == null)
- return DefaultOrder();
+ return null;
// Just return all elements on the page in order.
if (tabIndexes.Count <= 1)
- return DefaultOrder();
+ return null;
var views = new List();
foreach (var idx in tabIndexes?.Keys)