added new 'ExceptionalAccessibility' sample (#285)

This commit is contained in:
Mykyta Bondarenko 2018-10-02 16:26:38 -04:00 коммит произвёл Craig Dunn
Родитель d83d51f884
Коммит 3a08d39567
39 изменённых файлов: 2055 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,23 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExceptionalAccessibility", "ExceptionalAccessibility\ExceptionalAccessibility.csproj", "{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
Debug|iPhone = Debug|iPhone
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}.Release|iPhone.ActiveCfg = Release|iPhone
{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}.Release|iPhone.Build.0 = Release|iPhone
{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}.Debug|iPhone.ActiveCfg = Debug|iPhone
{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}.Debug|iPhone.Build.0 = Debug|iPhone
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,22 @@

namespace ExceptionalAccessibility
{
using Foundation;
using UIKit;
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to application events from iOS.
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,243 @@
{
"images": [
{
"size": "20x20",
"scale": "2x",
"idiom": "iphone"
},
{
"size": "20x20",
"scale": "3x",
"idiom": "iphone"
},
{
"filename": "icon-spotlight-29@2x.png",
"size": "29x29",
"scale": "2x",
"idiom": "iphone"
},
{
"filename": "icon-spotlight-29@3x.png",
"size": "29x29",
"scale": "3x",
"idiom": "iphone"
},
{
"filename": "icon-spotlight-40@2x.png",
"size": "40x40",
"scale": "2x",
"idiom": "iphone"
},
{
"filename": "icon-spotlight-40@3x.png",
"size": "40x40",
"scale": "3x",
"idiom": "iphone"
},
{
"filename": "icon-app-60@2x.png",
"size": "60x60",
"scale": "2x",
"idiom": "iphone"
},
{
"filename": "Icon-app-60@3x.png",
"size": "60x60",
"scale": "3x",
"idiom": "iphone"
},
{
"size": "20x20",
"scale": "1x",
"idiom": "ipad"
},
{
"size": "20x20",
"scale": "2x",
"idiom": "ipad"
},
{
"size": "29x29",
"scale": "1x",
"idiom": "ipad"
},
{
"size": "29x29",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "icon-spotlight-40.png",
"size": "40x40",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "icon-spotlight-40@2x.png",
"size": "40x40",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "Icon-app-83.5@2x.png",
"size": "83.5x83.5",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "icon-app-76.png",
"size": "76x76",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "icon-app-76@2x.png",
"size": "76x76",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "app-store-logo.png",
"size": "1024x1024",
"scale": "1x",
"idiom": "ios-marketing"
},
{
"size": "60x60",
"scale": "2x",
"idiom": "car"
},
{
"size": "60x60",
"scale": "3x",
"idiom": "car"
},
{
"role": "notificationCenter",
"size": "24x24",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "notificationCenter",
"size": "27.5x27.5",
"subtype": "42mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "companionSettings",
"size": "29x29",
"scale": "2x",
"idiom": "watch"
},
{
"role": "companionSettings",
"size": "29x29",
"scale": "3x",
"idiom": "watch"
},
{
"role": "appLauncher",
"size": "40x40",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "appLauncher",
"size": "44x44",
"subtype": "40mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "appLauncher",
"size": "50x50",
"subtype": "44mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "quickLook",
"size": "86x86",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "quickLook",
"size": "98x98",
"subtype": "42mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "quickLook",
"size": "108x108",
"subtype": "44mm",
"scale": "2x",
"idiom": "watch"
},
{
"size": "1024x1024",
"scale": "1x",
"idiom": "watch-marketing"
},
{
"size": "16x16",
"scale": "1x",
"idiom": "mac"
},
{
"size": "16x16",
"scale": "2x",
"idiom": "mac"
},
{
"size": "32x32",
"scale": "1x",
"idiom": "mac"
},
{
"size": "32x32",
"scale": "2x",
"idiom": "mac"
},
{
"size": "128x128",
"scale": "1x",
"idiom": "mac"
},
{
"size": "128x128",
"scale": "2x",
"idiom": "mac"
},
{
"size": "256x256",
"scale": "1x",
"idiom": "mac"
},
{
"size": "256x256",
"scale": "2x",
"idiom": "mac"
},
{
"size": "512x512",
"scale": "1x",
"idiom": "mac"
},
{
"size": "512x512",
"scale": "2x",
"idiom": "mac"
}
],
"info": {
"version": 1,
"author": "xcode"
}
}

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.8 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Просмотреть файл

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Просмотреть файл

@ -0,0 +1,94 @@
{
"images": [
{
"idiom": "universal"
},
{
"filename": "husky.jpg",
"scale": "1x",
"idiom": "universal"
},
{
"scale": "2x",
"idiom": "universal"
},
{
"scale": "3x",
"idiom": "universal"
},
{
"idiom": "iphone"
},
{
"scale": "1x",
"idiom": "iphone"
},
{
"scale": "2x",
"idiom": "iphone"
},
{
"subtype": "retina4",
"scale": "2x",
"idiom": "iphone"
},
{
"scale": "3x",
"idiom": "iphone"
},
{
"idiom": "ipad"
},
{
"scale": "1x",
"idiom": "ipad"
},
{
"scale": "2x",
"idiom": "ipad"
},
{
"idiom": "watch"
},
{
"scale": "2x",
"idiom": "watch"
},
{
"screenWidth": "{130,145}",
"scale": "2x",
"idiom": "watch"
},
{
"screenWidth": "{146,165}",
"scale": "2x",
"idiom": "watch"
},
{
"idiom": "mac"
},
{
"scale": "1x",
"idiom": "mac"
},
{
"scale": "2x",
"idiom": "mac"
},
{
"idiom": "car"
},
{
"scale": "2x",
"idiom": "car"
},
{
"scale": "3x",
"idiom": "car"
}
],
"info": {
"version": 1,
"author": "xcode"
}
}

Двоичные данные
ios12/ExceptionalAccessibility/ExceptionalAccessibility/Assets.xcassets/husky.imageset/husky.jpg поставляемый Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 201 KiB

Просмотреть файл

@ -0,0 +1,75 @@
namespace ExceptionalAccessibility
{
using Foundation;
using System;
using System.Collections.Generic;
using UIKit;
/// <summary>
/// The carousel container is a container view for the carousel collection view and the favorite and gallery button.
/// We subclass it so that we can override its `accessibilityElements` to exclude the collection view
/// and use our custom element instead, and so that we can add and remove the gallery buton as
/// an accessibility element as it appears and disappears.
/// </summary>
public partial class DogCarouselContainerView : UIView, IUIAccessibilityContainer
{
private CarouselAccessibilityElement carouselAccessibilityElement;
private Dog currentDog;
public DogCarouselContainerView(IntPtr handle) : base(handle) { }
public List<Dog> Dogs { get; set; }
public Dog CurrentDog
{
get => this.currentDog;
set
{
this.currentDog = value;
this.SetAccessibilityElements(NSArray.FromNSObjects(this.GetAccessibilityElements()?.ToArray()));
if (this.currentDog != null && this.carouselAccessibilityElement != null)
{
this.carouselAccessibilityElement.CurrentDog = this.currentDog;
}
}
}
#region Accessibility
private List<NSObject> GetAccessibilityElements()
{
List<NSObject> result = null;
if (this.currentDog != null)
{
CarouselAccessibilityElement accessibilityElement;
if (this.carouselAccessibilityElement != null)
{
accessibilityElement = this.carouselAccessibilityElement;
}
else
{
accessibilityElement = new CarouselAccessibilityElement(this, this.currentDog);
accessibilityElement.AccessibilityFrameInContainerSpace = dogCollectionView.Frame;
this.carouselAccessibilityElement = accessibilityElement;
}
// Only show the gallery button if we have multiple images.
if (this.currentDog.Images.Count > 1)
{
result = new List<NSObject> { this.carouselAccessibilityElement, this.galleryButton };
}
else
{
result = new List<NSObject> { this.carouselAccessibilityElement };
}
}
return result;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace ExceptionalAccessibility
{
[Register ("DogCarouselContainerView")]
partial class DogCarouselContainerView
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
public UIKit.UICollectionView dogCollectionView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
public UIKit.UIButton galleryButton { get; set; }
void ReleaseDesignerOutlets ()
{
if (dogCollectionView != null) {
dogCollectionView.Dispose ();
dogCollectionView = null;
}
if (galleryButton != null) {
galleryButton.Dispose ();
galleryButton = null;
}
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@
namespace ExceptionalAccessibility
{
using System;
using UIKit;
/// <summary>
/// Subclass of `UICollectionViewCell`; represents a particular dog in the collection view.
/// </summary>
public partial class DogCollectionViewCell : UICollectionViewCell
{
public DogCollectionViewCell(IntPtr handle) : base(handle) { }
}
}

Просмотреть файл

@ -0,0 +1,29 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace ExceptionalAccessibility
{
[Register ("DogCollectionViewCell")]
partial class DogCollectionViewCell
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
public UIKit.UIImageView dogImageView { get; set; }
void ReleaseDesignerOutlets ()
{
if (dogImageView != null) {
dogImageView.Dispose ();
dogImageView = null;
}
}
}
}

Просмотреть файл

@ -0,0 +1,46 @@
namespace ExceptionalAccessibility
{
using System;
using UIKit;
/// <summary>
/// This modal view displays a gallery of images for the dog, and has a dark transparent background so that
/// you can still see the content of the view below it.It is meant to be a full screen modal view,
/// but it creates problems for VoiceOver because it isn't a view controller that is presented modally.
/// Because it's simply a view that's added on top of everything else, and because it has a transparent background so the
/// views behind are still visible, VoiceOver doesn't inherently know that the views behind it should no longer
/// be accessible.So the user can still swipe to access those views behind it while this view is presented.
/// This creates a confusing, bad experience, so we override `accessibilityViewIsModal` to indicate
/// this view and its contents are the only thing on screen VoiceOver should currently care about.
/// </summary>
public partial class DogModalView : UIView
{
public DogModalView(IntPtr handle) : base(handle) { }
public override bool AccessibilityViewIsModal { get => true; set { /* ignore */ _ = value; } }
public override void LayoutSubviews()
{
base.LayoutSubviews();
this.closeButton.Layer.CornerRadius = this.closeButton.Bounds.Width / 2f;
this.closeButton.Layer.BorderWidth = 1f;
this.closeButton.Layer.BorderColor = UIColor.LightGray.CGColor;
}
partial void closeButtonTapped(UIButton sender)
{
UIView.AnimateNotify(0.25d, () =>
{
this.Alpha = 0f;
}, (bool finished) =>
{
if (finished)
{
this.RemoveFromSuperview();
}
});
}
}
}

Просмотреть файл

@ -0,0 +1,50 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace ExceptionalAccessibility
{
[Register ("DogModalView")]
partial class DogModalView
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
public UIKit.UIButton closeButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
public UIKit.UIImageView firstImageView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
public UIKit.UIImageView secondImageView { get; set; }
[Action ("closeButtonTapped:")]
partial void closeButtonTapped (UIButton sender);
void ReleaseDesignerOutlets ()
{
if (closeButton != null) {
closeButton.Dispose ();
closeButton = null;
}
if (firstImageView != null) {
firstImageView.Dispose ();
firstImageView = null;
}
if (secondImageView != null) {
secondImageView.Dispose ();
secondImageView = null;
}
}
}
}

Просмотреть файл

@ -0,0 +1,91 @@
namespace ExceptionalAccessibility
{
using CoreGraphics;
using Foundation;
using System;
using System.Collections.Generic;
using UIKit;
/// <summary>
/// This view is a collection of labels that house the data for each dog.
/// There are 4 properties that each dog has, so 8 labels in total: a title label for what the data is
/// and then a content label for the value.
/// </summary>
public partial class DogStatsView : UIView, IUIAccessibilityContainer
{
private Dog dog;
public DogStatsView(IntPtr handle) : base(handle) { }
public Dog Dog
{
get => this.dog;
set
{
this.dog = value;
if (this.dog != null)
{
this.nameLabel.Text = dog.Name;
this.breedLabel.Text = dog.Breed;
this.ageLabel.Text = $"{dog.Age} years";
this.weightLabel.Text = $"{dog.Weight} lbs";
this.SetAccessibilityElements(NSArray.FromNSObjects(this.GetAccessibilityElements().ToArray()));
}
}
}
#region Accessibility Logic
/*
VoiceOver relies on `accessibilityElements` returning an array of consistent objects that persist
as the user swipes through an app. We therefore have to cache our array of computed `accessibilityElements`
so that we don't get into an infinite loop of swiping. We reset this cached array whenever a new dog object is set
so that `accessibilityElements` can be recomputed.
*/
private List<UIAccessibilityElement> GetAccessibilityElements()
{
/*
We want to create a custom accessibility element that represents a grouping of each
title and content label pair so that the VoiceOver user can interact with them as a unified element.
This is important because it reduces the amount of times the user has to swipe through the display
to find the information they're looking for, and because without grouping the labels,
the content labels lose the context of what they represent.
*/
var elements = new List<UIAccessibilityElement>();
var nameElement = new UIAccessibilityElement(this);
nameElement.AccessibilityLabel = $"{this.nameTitleLabel.Text}, {this.nameLabel.Text}";
/*
This tells VoiceOver where the object should be onscreen. As the user
touches around with their finger, we can determine if an element is below
their finger.
*/
nameElement.AccessibilityFrameInContainerSpace = CGRect.Union(this.nameTitleLabel.Frame, this.nameLabel.Frame);
elements.Add(nameElement);
var ageElement = new UIAccessibilityElement(this);
ageElement.AccessibilityLabel = $"{this.ageTitleLabel.Text}, {this.ageLabel.Text}";
ageElement.AccessibilityFrameInContainerSpace = CGRect.Union(this.ageTitleLabel.Frame, this.ageLabel.Frame);
elements.Add(ageElement);
var breedElement = new UIAccessibilityElement(this);
breedElement.AccessibilityLabel = $"{this.breedTitleLabel.Text}, {this.breedLabel.Text}";
breedElement.AccessibilityFrameInContainerSpace = CGRect.Union(this.breedTitleLabel.Frame, this.breedLabel.Frame);
elements.Add(breedElement);
var weightElement = new UIAccessibilityElement(this);
weightElement.AccessibilityLabel = $"{this.weightTitleLabel.Text}, {this.weightLabel.Text}";
weightElement.AccessibilityFrameInContainerSpace = CGRect.Union(this.weightTitleLabel.Frame, this.weightLabel.Frame);
elements.Add(weightElement);
return elements;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,92 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace ExceptionalAccessibility
{
[Register ("DogStatsView")]
partial class DogStatsView
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel ageLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel ageTitleLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel breedLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel breedTitleLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel nameLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel nameTitleLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel weightLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel weightTitleLabel { get; set; }
void ReleaseDesignerOutlets ()
{
if (ageLabel != null) {
ageLabel.Dispose ();
ageLabel = null;
}
if (ageTitleLabel != null) {
ageTitleLabel.Dispose ();
ageTitleLabel = null;
}
if (breedLabel != null) {
breedLabel.Dispose ();
breedLabel = null;
}
if (breedTitleLabel != null) {
breedTitleLabel.Dispose ();
breedTitleLabel = null;
}
if (nameLabel != null) {
nameLabel.Dispose ();
nameLabel = null;
}
if (nameTitleLabel != null) {
nameTitleLabel.Dispose ();
nameTitleLabel = null;
}
if (weightLabel != null) {
weightLabel.Dispose ();
weightLabel = null;
}
if (weightTitleLabel != null) {
weightTitleLabel.Dispose ();
weightTitleLabel = null;
}
}
}
}

Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{B3EAC7D0-3DA4-470A-9790-55F48D48C8FC}</ProjectGuid>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<RootNamespace>ExceptionalAccessibility</RootNamespace>
<AssemblyName>ExceptionalAccessibility</AssemblyName>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
<DefineConstants>DEBUG;ENABLE_TEST_CLOUD;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
<MtouchFastDev>true</MtouchFastDev>
<MtouchProfiling>true</MtouchProfiling>
<IOSDebuggerPort>50826</IOSDebuggerPort>
<MtouchLink>None</MtouchLink>
<MtouchArch>x86_64</MtouchArch>
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchUseLlvm>true</MtouchUseLlvm>
<MtouchFloat32>true</MtouchFloat32>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchArch>ARM64</MtouchArch>
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
<MtouchLink>None</MtouchLink>
<MtouchArch>x86_64</MtouchArch>
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG;ENABLE_TEST_CLOUD;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignKey>iPhone Developer</CodesignKey>
<DeviceSpecificBuild>true</DeviceSpecificBuild>
<MtouchDebug>true</MtouchDebug>
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
<MtouchFastDev>true</MtouchFastDev>
<MtouchProfiling>true</MtouchProfiling>
<MtouchFloat32>true</MtouchFloat32>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<IOSDebuggerPort>40129</IOSDebuggerPort>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchArch>ARM64</MtouchArch>
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Contents.json" />
<ImageAsset Include="Assets.xcassets\Contents.json" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\app-store-logo.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-app-83.5%402x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-app-76.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-app-76%402x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-40.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-40%402x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-app-60%402x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-app-60%403x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-40%403x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-29%402x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\icon-spotlight-29%403x.png" />
<ImageAsset Include="Assets.xcassets\husky.imageset\Contents.json" />
<ImageAsset Include="Assets.xcassets\husky.imageset\husky.jpg" />
</ItemGroup>
<ItemGroup>
<Folder Include="Assets.xcassets\husky.imageset\" />
<Folder Include="Helpers\" />
<Folder Include="Controls\" />
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="LaunchScreen.storyboard" />
<InterfaceDefinition Include="Main.storyboard" />
</ItemGroup>
<ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="ViewController.cs" />
<Compile Include="ViewController.designer.cs">
<DependentUpon>ViewController.cs</DependentUpon>
</Compile>
<Compile Include="Helpers\Dog.cs" />
<Compile Include="Helpers\CarouselAccessibilityElement.cs" />
<Compile Include="Controls\DogCarouselContainerView.cs" />
<Compile Include="Controls\DogCarouselContainerView.designer.cs">
<DependentUpon>DogCarouselContainerView.cs</DependentUpon>
</Compile>
<Compile Include="Controls\DogCollectionViewCell.cs" />
<Compile Include="Controls\DogCollectionViewCell.designer.cs">
<DependentUpon>DogCollectionViewCell.cs</DependentUpon>
</Compile>
<Compile Include="Controls\DogModalView.cs" />
<Compile Include="Controls\DogModalView.designer.cs">
<DependentUpon>DogModalView.cs</DependentUpon>
</Compile>
<Compile Include="Controls\DogStatsView.cs" />
<Compile Include="Controls\DogStatsView.designer.cs">
<DependentUpon>DogStatsView.cs</DependentUpon>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,147 @@

namespace ExceptionalAccessibility
{
using Foundation;
using UIKit;
/// <summary>
/// The custom carousel accessibility element is a core part of this sample.
/// It is illustrating a way in which we choose to tweak the accessibility experience in a unique and interesting way.
/// If we leave the collection view as is, then the VoiceOver user has to swipe to the end of the carousel
/// before they can reach either button or the data for the dogs, meaning that they will only ever be able to
/// get to the data for the last dog in the carousel through swiping alone.We instead create this custom element,
/// and make it an adjustable element that responds to `accessibilityIncrement` and `accessibilityDecrement`,
/// so that when a user swipes from it, they swipe immediately to the favorite and gallery buttons, then on to the data,
/// for the specific dog.In some ways, we've transformed the collection view into acting more like a picker.
/// </summary>
public class CarouselAccessibilityElement : UIAccessibilityElement
{
public CarouselAccessibilityElement(NSObject accessibilityContainer, Dog dog) : base(accessibilityContainer)
{
this.CurrentDog = dog;
}
public Dog CurrentDog { get; set; }
/// <summary>
/// This indicates to the user what exactly this element is supposed to be.
/// </summary>
public override string AccessibilityLabel
{
get => "Dog Picker";
set => base.AccessibilityLabel = value;
}
public override string AccessibilityValue
{
get => this.CurrentDog != null ? this.CurrentDog.Name : base.AccessibilityValue;
set => base.AccessibilityValue = value;
}
/// <summary>
/// This tells VoiceOver that our element will support the increment and decrement callbacks.
/// </summary>
public override ulong AccessibilityTraits { get => (ulong)UIAccessibilityTrait.Adjustable; set => base.AccessibilityTraits = value; }
/// <summary>
/// A convenience for forward scrolling in both `accessibilityIncrement` and `accessibilityScroll`.
/// It returns a `Bool` because `accessibilityScroll` needs to know if the scroll was successful.
/// </summary>
private bool AccessibilityScrollForward()
{
var result = false;
// Initialize the container view which will house the collection view.
if (this.AccessibilityContainer is DogCarouselContainerView containerView)
{
// Store the currently focused dog and the list of all dogs.
if (this.CurrentDog != null && containerView.Dogs != null)
{
// Get the index of the currently focused dog from the list of dogs (if it's a valid index).
var index = containerView.Dogs.IndexOf(this.CurrentDog);
if (index > -1 && index < containerView.Dogs.Count - 1)
{
// Scroll the collection view to the currently focused dog.
containerView.dogCollectionView.ScrollToItem(NSIndexPath.FromRowSection(index + 1, 0),
UICollectionViewScrollPosition.CenteredHorizontally,
true);
result = true;
}
}
}
return result;
}
/// <summary>
/// A convenience for backward scrolling in both `accessibilityIncrement` and `accessibilityScroll`.
/// It returns a `Bool` because `accessibilityScroll` needs to know if the scroll was successful.
/// </summary>
private bool AccessibilityScrollBackward()
{
var result = false;
if (this.AccessibilityContainer is DogCarouselContainerView containerView)
{
if (this.CurrentDog != null && containerView.Dogs != null)
{
var index = containerView.Dogs.IndexOf(this.CurrentDog);
if (index != -1)
{
containerView.dogCollectionView.ScrollToItem(NSIndexPath.FromRowSection(index - 1, 0),
UICollectionViewScrollPosition.CenteredHorizontally,
true);
result = true;
}
}
}
return result;
}
#region Accessibility
/*
Overriding the following two methods allows the user to perform increment and decrement actions
(done by swiping up or down).
*/
[Export("accessibilityIncrement")]
public void AccessibilityIncrement()
{
// This causes the picker to move forward one if the user swipes up.
_ = this.AccessibilityScrollForward();
}
[Export("accessibilityDecrement")]
public void AccessibilityDecrement()
{
// This causes the picker to move back one if the user swipes down.
_ = this.AccessibilityScrollBackward();
}
/// <summary>
/// This will cause the picker to move forward or backwards on when the user does a 3-finger swipe,
/// depending on the direction of the swipe.The return value indicates whether or not the scroll was successful,
/// so that VoiceOver can alert the user if it was not.
/// </summary>
[Export("accessibilityScroll:")]
public bool AccessibilityScroll(UIAccessibilityScrollDirection direction)
{
var result = false;
if (direction == UIAccessibilityScrollDirection.Left)
{
result = this.AccessibilityScrollForward();
}
else if (direction == UIAccessibilityScrollDirection.Right)
{
result = this.AccessibilityScrollBackward();
}
return result;
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,61 @@

namespace ExceptionalAccessibility
{
using System;
using System.Collections.Generic;
using System.Linq;
using UIKit;
/// <summary>
/// Dog model housing all of the data for each different dog up for adoption.
/// </summary>
public class Dog : IEquatable<Dog>
{
private readonly static UIImage DefaultImage = UIImage.FromBundle("husky");
public Dog(string name, List<UIImage> images, string breed, float age, float weight, string shelterName)
{
this.Name = name;
this.Images = images;
this.Breed = breed;
this.Age = age;
this.Weight = weight;
this.ShelterName = shelterName;
}
public UIImage FeaturedImage => this.Images.FirstOrDefault();
public string Name { get; private set; }
public List<UIImage> Images { get; set; }
public string Breed { get; private set; }
public float Age { get; private set; }
public float Weight { get; private set; }
public string ShelterName { get; private set; }
/// <summary>
/// Convenience initializer for faked data
/// </summary>
public static List<Dog> All { get; } = new List<Dog>
{
new Dog("Lilly", new List<UIImage> { DefaultImage, DefaultImage, DefaultImage }, "Corgi", 5, 26, "Cupertino Animal Shelter"),
new Dog("Mr. Hammond", new List<UIImage> { DefaultImage }, "Pug", 2, 23, "Cupertino Animal Shelter"),
new Dog("Bubbles", new List<UIImage> { DefaultImage, DefaultImage, DefaultImage }, "Golden Retriever", 8, 65, "Cupertino Animal Shelter"),
new Dog("Pinky", new List<UIImage> { DefaultImage }, "Maltese", 4, 28, "Cupertino Animal Shelter")
};
public bool Equals(Dog other)
{
return this.Name == other.Name &&
this.Images == other.Images &&
this.Breed == other.Breed &&
this.Age == other.Age &&
this.Weight == other.Weight &&
this.ShelterName == other.ShelterName;
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>ExceptionalAccessibility</string>
<key>CFBundleIdentifier</key>
<string>org.xamarin.exceptional-accessibility</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>12.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/AppIcon.appiconset</string>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<subviews>
<view contentMode="scaleToFill" id="197" translatesAutoresizingMaskIntoConstraints="NO" misplaced="YES">
<rect key="frame" x="0.0" y="259" width="375" height="168"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="202" translatesAutoresizingMaskIntoConstraints="NO" image="husky">
<rect key="frame" x="67.5" y="0.0" width="240" height="128"/>
<constraints>
<constraint id="204" firstAttribute="width" constant="240"/>
<constraint id="207" firstAttribute="height" constant="128"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Exceptional Accessibility" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="203" translatesAutoresizingMaskIntoConstraints="NO">
<rect key="frame" x="49.5" y="136" width="276" height="31.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint id="205" firstItem="202" firstAttribute="centerX" secondItem="197" secondAttribute="centerX"/>
<constraint id="206" firstItem="202" firstAttribute="top" secondItem="197" secondAttribute="top"/>
<constraint id="214" firstItem="203" firstAttribute="top" secondItem="202" secondAttribute="bottom" constant="8"/>
<constraint id="215" firstItem="203" firstAttribute="bottom" secondItem="197" secondAttribute="bottom"/>
<constraint id="216" firstItem="203" firstAttribute="centerX" secondItem="202" secondAttribute="centerX"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint id="219" firstItem="197" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX"/>
<constraint id="220" firstItem="197" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY"/>
<constraint id="221" firstAttribute="leadingMargin" secondItem="197" secondAttribute="leading" constant="16"/>
<constraint id="222" firstItem="197" firstAttribute="trailing" secondItem="Ze5-6b-2t3" secondAttribute="trailingMargin" constant="16"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="husky" width="1616" height="2124"/>
</resources>
</document>

Просмотреть файл

@ -0,0 +1,16 @@

namespace ExceptionalAccessibility
{
using UIKit;
public class Application
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, "AppDelegate");
}
}
}

Просмотреть файл

@ -0,0 +1,376 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="208" customClass="DogCarouselContainerView">
<rect key="frame" x="16" y="36" width="343" height="250"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="2002">
<rect key="frame" x="0.0" y="0.0" width="343" height="250"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="2004">
<size key="itemSize" width="300" height="225"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="dog collection view cell" id="2003" customClass="DogCollectionViewCell">
<rect key="frame" x="20" y="12.5" width="300" height="225"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="300" height="225"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="2211">
<rect key="frame" x="0.0" y="0.0" width="300" height="225"/>
</imageView>
</subviews>
</view>
<constraints>
<constraint firstItem="2211" firstAttribute="top" secondItem="2003" secondAttribute="top" id="2313"/>
<constraint firstItem="2211" firstAttribute="trailing" secondItem="2003" secondAttribute="trailing" id="2314"/>
<constraint firstItem="2211" firstAttribute="bottom" secondItem="2003" secondAttribute="bottom" id="2315"/>
<constraint firstItem="2211" firstAttribute="leading" secondItem="2003" secondAttribute="leading" id="2316"/>
</constraints>
<connections>
<outlet property="dogImageView" destination="2211" id="name-outlet-2211"/>
</connections>
</collectionViewCell>
</cells>
<connections>
<outlet property="dataSource" destination="BYZ-38-t0r" id="erX-qN-hIZ"/>
<outlet property="delegate" destination="BYZ-38-t0r" id="nuD-SC-K72"/>
</connections>
</collectionView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2742">
<rect key="frame" x="267" y="185" width="45" height="45"/>
<color key="backgroundColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="45" id="2849"/>
<constraint firstAttribute="width" constant="45" id="2850"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<state key="normal" title="G">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="galleryButtonPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="Fpr-EG-xlI"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="250" id="209"/>
<constraint firstItem="2002" firstAttribute="top" secondItem="208" secondAttribute="top" id="2005"/>
<constraint firstItem="2002" firstAttribute="leading" secondItem="208" secondAttribute="leading" id="2006"/>
<constraint firstItem="2002" firstAttribute="bottom" secondItem="208" secondAttribute="bottom" id="2007"/>
<constraint firstItem="2002" firstAttribute="trailing" secondItem="208" secondAttribute="trailing" id="2008"/>
<constraint firstItem="2002" firstAttribute="bottom" secondItem="2742" secondAttribute="bottom" constant="20" id="2851"/>
<constraint firstItem="2002" firstAttribute="trailing" secondItem="2742" secondAttribute="trailing" constant="31" id="2852"/>
</constraints>
<connections>
<outlet property="dogCollectionView" destination="2002" id="rVq-zI-1AP"/>
<outlet property="galleryButton" destination="2742" id="seQ-Ic-Kgf"/>
</connections>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="219">
<rect key="frame" x="0.0" y="302" width="375" height="0.5"/>
<color key="backgroundColor" white="0.66666666666666696" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="0.5" id="233"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="224" customClass="DogStatsView">
<rect key="frame" x="0.0" y="302.5" width="375" height="161"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Name label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="236">
<rect key="frame" x="14" y="36" width="159.5" height="29"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NAME" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="237">
<rect key="frame" x="14" y="16" width="159.5" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.33333333333333298" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Breed label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="252">
<rect key="frame" x="14" y="116" width="159.5" height="29"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="BREED" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="253">
<rect key="frame" x="14" y="96" width="159.5" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.33333333333333298" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Age label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="254">
<rect key="frame" x="202" y="36" width="165" height="29"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="AGE" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="255">
<rect key="frame" x="202" y="16" width="28" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.33333333333333298" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Weight label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="256">
<rect key="frame" x="202" y="117" width="159" height="29"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="24"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="WEIGHT" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="257">
<rect key="frame" x="202" y="97" width="55" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.33333333333333298" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="258">
<rect key="frame" x="14" y="81" width="347" height="0.5"/>
<color key="backgroundColor" white="0.66666666666666696" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="0.5" id="381"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="260">
<rect key="frame" x="187.5" y="-8" width="0.5" height="177"/>
<color key="backgroundColor" white="0.66666666666666696" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="0.5" id="382"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="237" firstAttribute="leading" secondItem="224" secondAttribute="leading" constant="14" id="281"/>
<constraint firstItem="237" firstAttribute="top" secondItem="224" secondAttribute="top" constant="16" id="282"/>
<constraint firstItem="236" firstAttribute="top" secondItem="237" secondAttribute="bottom" constant="3" id="284"/>
<constraint firstItem="236" firstAttribute="leading" secondItem="224" secondAttribute="leading" constant="14" id="285"/>
<constraint firstItem="258" firstAttribute="centerY" secondItem="236" secondAttribute="bottom" constant="16" id="286"/>
<constraint firstItem="255" firstAttribute="top" secondItem="224" secondAttribute="top" constant="16" id="288"/>
<constraint firstItem="254" firstAttribute="top" secondItem="255" secondAttribute="bottom" constant="3" id="290"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="255" secondAttribute="trailing" constant="20" symbolic="YES" id="291"/>
<constraint firstItem="253" firstAttribute="leading" secondItem="224" secondAttribute="leading" constant="14" id="323"/>
<constraint firstItem="253" firstAttribute="top" secondItem="258" secondAttribute="centerY" constant="15" id="324"/>
<constraint firstItem="252" firstAttribute="top" secondItem="253" secondAttribute="bottom" constant="3" id="326"/>
<constraint firstItem="252" firstAttribute="leading" secondItem="224" secondAttribute="leading" constant="14" id="327"/>
<constraint firstItem="260" firstAttribute="leading" secondItem="252" secondAttribute="trailing" constant="14" id="328"/>
<constraint firstItem="257" firstAttribute="top" secondItem="224" secondAttribute="top" constant="97" id="330"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="257" secondAttribute="trailing" constant="20" symbolic="YES" id="333"/>
<constraint firstItem="256" firstAttribute="top" secondItem="257" secondAttribute="bottom" constant="3" id="376"/>
<constraint firstAttribute="trailing" secondItem="256" secondAttribute="trailing" constant="14" id="378"/>
<constraint firstItem="258" firstAttribute="leading" secondItem="224" secondAttribute="leading" constant="14" id="379"/>
<constraint firstAttribute="trailing" secondItem="258" secondAttribute="trailing" constant="14" id="380"/>
<constraint firstItem="260" firstAttribute="leading" secondItem="236" secondAttribute="trailing" constant="14" id="3303"/>
<constraint firstItem="260" firstAttribute="leading" secondItem="237" secondAttribute="trailing" constant="14" id="3420"/>
<constraint firstItem="260" firstAttribute="leading" secondItem="253" secondAttribute="trailing" constant="14" id="3643"/>
<constraint firstItem="255" firstAttribute="leading" secondItem="260" secondAttribute="trailing" constant="14" id="3756"/>
<constraint firstItem="254" firstAttribute="leading" secondItem="260" secondAttribute="trailing" constant="14" id="3870"/>
<constraint firstItem="257" firstAttribute="leading" secondItem="260" secondAttribute="trailing" constant="14" id="3871"/>
<constraint firstItem="256" firstAttribute="leading" secondItem="260" secondAttribute="trailing" constant="14" id="3987"/>
<constraint firstItem="260" firstAttribute="centerY" secondItem="224" secondAttribute="centerY" id="AIO-27-W6a"/>
<constraint firstAttribute="trailing" secondItem="254" secondAttribute="trailing" constant="8" id="B8c-4M-yQU"/>
<constraint firstItem="260" firstAttribute="centerX" secondItem="224" secondAttribute="centerX" id="Vec-cW-9oU"/>
<constraint firstAttribute="bottom" secondItem="252" secondAttribute="bottom" constant="16" id="cZq-En-vXw"/>
<constraint firstItem="260" firstAttribute="top" secondItem="224" secondAttribute="top" constant="-8" id="wKv-V7-BXI"/>
</constraints>
<connections>
<outlet property="ageLabel" destination="254" id="name-outlet-254"/>
<outlet property="ageTitleLabel" destination="255" id="name-outlet-255"/>
<outlet property="breedLabel" destination="252" id="name-outlet-252"/>
<outlet property="breedTitleLabel" destination="253" id="name-outlet-253"/>
<outlet property="nameLabel" destination="236" id="name-outlet-236"/>
<outlet property="nameTitleLabel" destination="237" id="name-outlet-237"/>
<outlet property="weightLabel" destination="256" id="name-outlet-256"/>
<outlet property="weightTitleLabel" destination="257" id="name-outlet-257"/>
</connections>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="388">
<rect key="frame" x="0.0" y="463.5" width="375" height="0.5"/>
<color key="backgroundColor" white="0.66666666666666696" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="0.5" id="389"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="393">
<rect key="frame" x="0.0" y="464" width="375" height="72"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Shelter Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="452">
<rect key="frame" x="14" y="23" width="239" height="26"/>
<fontDescription key="fontDescription" type="system" pointSize="22"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="454">
<rect key="frame" x="267" y="16" width="40" height="40"/>
<color key="backgroundColor" name="systemBlueColor" catalog="System" colorSpace="catalog"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="520"/>
<constraint firstAttribute="height" constant="40" id="521"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<state key="normal" title="L">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="517">
<rect key="frame" x="321" y="16" width="40" height="40"/>
<color key="backgroundColor" name="systemBlueColor" catalog="System" colorSpace="catalog"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="523"/>
<constraint firstAttribute="height" constant="40" id="524"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<state key="normal" title="C">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="452" firstAttribute="leading" secondItem="393" secondAttribute="leading" constant="14" id="455"/>
<constraint firstItem="454" firstAttribute="leading" secondItem="452" secondAttribute="trailing" constant="14" id="518"/>
<constraint firstItem="452" firstAttribute="centerY" secondItem="454" secondAttribute="centerY" id="519"/>
<constraint firstItem="517" firstAttribute="leading" secondItem="454" secondAttribute="trailing" constant="14" id="522"/>
<constraint firstAttribute="trailing" secondItem="517" secondAttribute="trailing" constant="14" id="525"/>
<constraint firstItem="517" firstAttribute="centerY" secondItem="454" secondAttribute="centerY" id="526"/>
<constraint firstItem="517" firstAttribute="top" secondItem="393" secondAttribute="top" constant="16" id="527"/>
<constraint firstAttribute="bottom" secondItem="517" secondAttribute="bottom" constant="16" id="xtx-Ky-eSi"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="601">
<rect key="frame" x="0.0" y="536" width="375" height="0.5"/>
<color key="backgroundColor" white="0.66666666666666696" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="0.5" id="602"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="208" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="210"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="208" secondAttribute="trailing" constant="16" id="211"/>
<constraint firstItem="208" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="16" id="213"/>
<constraint firstItem="224" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="229"/>
<constraint firstItem="224" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="230"/>
<constraint firstItem="219" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="231"/>
<constraint firstItem="219" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="232"/>
<constraint firstItem="219" firstAttribute="top" secondItem="208" secondAttribute="bottom" constant="16" id="234"/>
<constraint firstItem="219" firstAttribute="bottom" secondItem="224" secondAttribute="top" id="235"/>
<constraint firstItem="393" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="450"/>
<constraint firstItem="393" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="451"/>
<constraint firstItem="601" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="603"/>
<constraint firstItem="601" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="604"/>
<constraint firstItem="601" firstAttribute="top" secondItem="393" secondAttribute="bottom" id="605"/>
<constraint firstItem="388" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" id="5156"/>
<constraint firstItem="388" firstAttribute="trailing" secondItem="6Tk-OE-BBY" secondAttribute="trailing" id="5157"/>
<constraint firstItem="393" firstAttribute="top" secondItem="388" secondAttribute="bottom" id="5601"/>
<constraint firstItem="388" firstAttribute="top" secondItem="224" secondAttribute="bottom" id="5681"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="callButton" destination="517" id="name-outlet-517"/>
<outlet property="carouselContainerView" destination="208" id="name-outlet-208"/>
<outlet property="dogCollectionView" destination="2002" id="rVq-zI-1FP"/>
<outlet property="dogStatsView" destination="224" id="name-outlet-224"/>
<outlet property="galleryButton" destination="2742" id="seQ-Ic-Ktf"/>
<outlet property="locationButton" destination="454" id="name-outlet-454"/>
<outlet property="shelterInfoView" destination="393" id="name-outlet-393"/>
<outlet property="shelterNameLabel" destination="452" id="name-outlet-452"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="0.0" y="0.0"/>
</scene>
<!--View Controller-->
<scene sceneID="683">
<objects>
<viewController storyboardIdentifier="DogModalViewController" id="684" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="685" customClass="DogModalView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view alpha="0.84999999999999998" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1586">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="770">
<rect key="frame" x="8" y="110" width="359" height="270"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="773">
<rect key="frame" x="8" y="388" width="359" height="270"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="769">
<rect key="frame" x="315" y="50" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="783"/>
<constraint firstAttribute="width" constant="40" id="784"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<state key="normal" title="X">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="closeButtonTapped:" destination="685" eventType="touchUpInside" id="oiS-rc-vDC"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="773" secondAttribute="bottom" constant="9" id="1593"/>
<constraint firstAttribute="trailing" secondItem="773" secondAttribute="trailing" constant="8" id="1594"/>
<constraint firstItem="773" firstAttribute="leading" secondItem="1586" secondAttribute="leading" constant="8" id="1595"/>
<constraint firstItem="773" firstAttribute="top" secondItem="770" secondAttribute="bottom" constant="8" id="1596"/>
<constraint firstItem="770" firstAttribute="leading" secondItem="1586" secondAttribute="leading" constant="8" id="1597"/>
<constraint firstAttribute="trailing" secondItem="770" secondAttribute="trailing" constant="8" id="1598"/>
<constraint firstItem="770" firstAttribute="top" secondItem="769" secondAttribute="bottom" constant="20" id="1599"/>
<constraint firstAttribute="trailing" secondItem="769" secondAttribute="trailing" constant="20" id="1600"/>
<constraint firstItem="769" firstAttribute="top" secondItem="1586" secondAttribute="top" constant="50" id="1601"/>
<constraint firstItem="773" firstAttribute="height" secondItem="770" secondAttribute="height" id="1602"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="1586" firstAttribute="leading" secondItem="682" secondAttribute="leading" id="1589"/>
<constraint firstItem="1586" firstAttribute="bottom" secondItem="682" secondAttribute="bottom" id="1590"/>
<constraint firstItem="1586" firstAttribute="trailing" secondItem="682" secondAttribute="trailing" id="1591"/>
<constraint firstItem="1586" firstAttribute="top" secondItem="685" secondAttribute="top" id="1592"/>
</constraints>
<viewLayoutGuide key="safeArea" id="682"/>
<connections>
<outlet property="closeButton" destination="769" id="name-outlet-769"/>
<outlet property="firstImageView" destination="770" id="name-outlet-770"/>
<outlet property="secondImageView" destination="773" id="name-outlet-773"/>
</connections>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="686" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="714" y="0.0"/>
</scene>
</scenes>
</document>

Просмотреть файл

@ -0,0 +1,228 @@

namespace ExceptionalAccessibility
{
using CoreGraphics;
using Foundation;
using System;
using System.Collections.Generic;
using System.Linq;
using UIKit;
/// <summary>
/// This view controller manages the displayed views, links up the data by syncing dog objects
/// across the relevant views, handles displaying and removing the modal view as well as responding
/// to button presses in the carousel.It also serves as the data source for the collection view.
/// </summary>
public partial class ViewController : UIViewController, IUICollectionViewDataSource, IUICollectionViewDelegateFlowLayout
{
private const string CellIdentifier = "dog collection view cell";
private readonly List<Dog> dogs = Dog.All;
private Dog currentlyFocusedDog;
protected ViewController(IntPtr handle) : base(handle) { }
protected Dog CurrentlyFocusedDog
{
get => this.currentlyFocusedDog;
// Every time we update our Dog object, we need to relay that change to all the views that care.
set
{
if (this.currentlyFocusedDog != value)
{
this.currentlyFocusedDog = value;
if (this.dogStatsView != null)
{
this.dogStatsView.Dog = this.currentlyFocusedDog;
}
this.carouselContainerView.CurrentDog = this.currentlyFocusedDog;
this.shelterNameLabel.Text = this.currentlyFocusedDog?.ShelterName;
this.shelterInfoView.AccessibilityLabel = this.currentlyFocusedDog?.ShelterName;
}
}
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (this.dogs.Any())
{
this.CurrentlyFocusedDog = this.dogs.First();
this.carouselContainerView.Dogs = this.dogs;
}
this.galleryButton.AccessibilityLabel = "Show Gallery";
this.shelterInfoView.IsAccessibilityElement = true;
this.shelterInfoView.AccessibilityCustomActions = new UIAccessibilityCustomAction[]
{
new UIAccessibilityCustomAction("Call", this.ActivateCallButton),
new UIAccessibilityCustomAction("Open address in Maps", this.ActivateLocationButton)
};
}
/// <summary>
/// Called as a result of activating the "Call" custom action.
/// </summary>
private bool ActivateCallButton(UIAccessibilityCustomAction arg)
{
return true;
}
/// <summary>
/// Called as a result of activating the "Open address in Maps" custom action.
/// </summary>
private bool ActivateLocationButton(UIAccessibilityCustomAction arg)
{
return true;
}
partial void galleryButtonPressed(UIButton sender)
{
var dogModalViewController = base.Storyboard?.InstantiateViewController("DogModalViewController");
if (dogModalViewController == null)
{
throw new Exception("Could not create a \"DogModalViewController\" from the storyboard.");
}
if (dogModalViewController.View is DogModalView dogModalView)
{
// The gallery button shouldn't do anything if the currently focused dog doesn't have 2 or more images.
if (this.currentlyFocusedDog != null && this.currentlyFocusedDog.Images.Count >= 2)
{
// Make the images of the modal view accessible and add accessibility labels to these images and the close button.
dogModalView.closeButton.AccessibilityLabel = "Close";
dogModalView.firstImageView.IsAccessibilityElement = true;
dogModalView.firstImageView.AccessibilityLabel = "Image 1";
dogModalView.firstImageView.Image = currentlyFocusedDog.Images[0];
dogModalView.secondImageView.IsAccessibilityElement = true;
dogModalView.secondImageView.AccessibilityLabel = "Image 2";
dogModalView.secondImageView.Image = currentlyFocusedDog.Images[1];
dogModalView.Alpha = 0f;
View.AddSubview(dogModalView);
UIView.AnimateNotify(0.25d, () =>
{
dogModalView.Alpha = 1f;
}, (finished) =>
{
if (finished)
{
/*
Once the modal gallery view has been animated in, we need to post a notification
to VoiceOver that the screen has changed so that it knows to update its focus
to the new content now displayed on top of the older content.
*/
UIAccessibility.PostNotification(UIAccessibilityPostNotification.ScreenChanged, null);
}
});
}
else
{
return;
}
}
else
{
throw new Exception("\"DogModalViewController\" not configured with a \"DogModalView\".");
}
}
#region IUICollectionViewDataSource
public UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
if (collectionView.DequeueReusableCell(CellIdentifier, indexPath) is DogCollectionViewCell cell)
{
var dog = this.dogs[(int)indexPath.Item];
cell.dogImageView.Image = dog.FeaturedImage;
cell.IsAccessibilityElement = true;
cell.AccessibilityLabel = dog.Name;
return cell;
}
else
{
throw new Exception($"Expected a `{nameof(DogCollectionViewCell)}` but did not receive one.");
}
}
public nint GetItemsCount(UICollectionView collectionView, nint section)
{
return this.dogs.Count;
}
#endregion
#region IUIScrollViewDelegate
/// <summary>
/// This keeps the cells of the collection view centered.
/// </summary>
[Export("scrollViewWillEndDragging:withVelocity:targetContentOffset:")]
public void WillEndDragging(UIScrollView scrollView, CoreGraphics.CGPoint velocity, ref CoreGraphics.CGPoint targetContentOffset)
{
if (this.dogCollectionView?.CollectionViewLayout is UICollectionViewFlowLayout layout)
{
var cellWidthIncludingSpacing = layout.ItemSize.Width + layout.MinimumLineSpacing;
var offset = targetContentOffset;
var index = Math.Round((offset.X + scrollView.ContentInset.Left) / cellWidthIncludingSpacing);
offset = new CGPoint(index * cellWidthIncludingSpacing - scrollView.ContentInset.Left,
-scrollView.ContentInset.Top);
targetContentOffset = offset;
}
}
/// <summary>
/// In `scrollViewDidScroll`, we calculate our new centered cell's index, then find the corresponding `Dog`
/// in our array and update our current `Dog`. We also animate in or out the gallery button based on
/// whether or not we want to show it for the new dog.
/// </summary>
[Export("scrollViewDidScroll:")]
public void Scrolled(UIScrollView scrollView)
{
if (this.dogCollectionView.CollectionViewLayout is UICollectionViewFlowLayout flowLayout)
{
var itemWidth = flowLayout.ItemSize.Width;
var offset = this.dogCollectionView.ContentOffset.X / itemWidth;
var index = (int)Math.Round(offset);
if (index >= 0 && index < this.dogs.Count)
{
var focusedDog = this.dogs[index];
this.CurrentlyFocusedDog = focusedDog;
if (focusedDog.Images.Count > 1)
{
if (this.galleryButton.Alpha == 0f)
{
UIView.Animate(0.25d, () => this.galleryButton.Alpha = 1f);
}
}
else if (this.galleryButton.Alpha == 1f)
{
UIView.Animate(0.25d, () => this.galleryButton.Alpha = 0f);
}
/*
The information for the dog displayed below the collection view updates as you scroll,
but VoiceOver isn't aware that the views have changed their values. So we need to post
a layout changed notification to let VoiceOver know it needs to update its current
understanding of what's on screen.
*/
UIAccessibility.PostNotification(UIAccessibilityPostNotification.LayoutChanged, null);
}
}
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,95 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
namespace ExceptionalAccessibility
{
[Register ("ViewController")]
partial class ViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIButton callButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
ExceptionalAccessibility.DogCarouselContainerView carouselContainerView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
public UIKit.UICollectionView dogCollectionView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
ExceptionalAccessibility.DogStatsView dogStatsView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIButton galleryButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIButton locationButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIView shelterInfoView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel shelterNameLabel { get; set; }
[Action ("galleryButtonPressed:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void galleryButtonPressed (UIKit.UIButton sender);
void ReleaseDesignerOutlets ()
{
if (callButton != null) {
callButton.Dispose ();
callButton = null;
}
if (carouselContainerView != null) {
carouselContainerView.Dispose ();
carouselContainerView = null;
}
if (dogCollectionView != null) {
dogCollectionView.Dispose ();
dogCollectionView = null;
}
if (dogStatsView != null) {
dogStatsView.Dispose ();
dogStatsView = null;
}
if (galleryButton != null) {
galleryButton.Dispose ();
galleryButton = null;
}
if (locationButton != null) {
locationButton.Dispose ();
locationButton = null;
}
if (shelterInfoView != null) {
shelterInfoView.Dispose ();
shelterInfoView = null;
}
if (shelterNameLabel != null) {
shelterNameLabel.Dispose ();
shelterNameLabel = null;
}
}
}
}

Просмотреть файл

@ -0,0 +1,8 @@
Copyright © 2018 Apple Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<SampleMetadata>
<ID>3d185ba0-19a8-47a0-aba8-a56e7b2242ed</ID>
<IsFullApplication>false</IsFullApplication>
<Brief>Delivering an Exceptional Accessibility Experience</Brief>
<SupportedPlatforms>iOS</SupportedPlatforms>
<Level>Intermediate</Level>
<Tags>Accessibility, VoiceOver, iOS12</Tags>
<Gallery>true</Gallery>
</SampleMetadata>

Просмотреть файл

@ -0,0 +1,29 @@
Delivering an Exceptional Accessibility Experience
============
Make improvements to your apps interaction model to support assistive technologies such as VoiceOver.
To see the sample app in action, use Visual Studio to build and run the app on your iOS device. Be sure to turn on VoiceOver, which you can do from Settings under **General > Accessibility > VoiceOver**.
![Demonstration of accessibility](Screenshots/screenshot-1.png)
Build Requirements
-------
Xamarin.iOS 12.0+ and Xcode 10.0+.
Related Links
-------
- [Original sample](https://developer.apple.com/documentation/uikit/accessibility/delivering_an_exceptional_accessibility_experience).
- [Documentation](https://developer.apple.com/documentation/uikit/accessibility)
License
-------
Xamarin port changes are released under the MIT license.
Author
------
Ported to Xamarin.iOS by Mykyta Bondarenko

Двоичные данные
ios12/ExceptionalAccessibility/Screenshots/screenshot-1.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 190 KiB