Commit ff9da4c6 authored by Aral Balkan's avatar Aral Balkan

Progress towards improving the accessibility of the setup flow.

parent e5613be2
......@@ -41,6 +41,7 @@
A78DAE921A817000009FDB15 /* blackUnderlineTemplate@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A78DAE911A817000009FDB15 /* blackUnderlineTemplate@2x.png */; };
A78DAE941A82320B009FDB15 /* SetupHandleTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = A78DAE931A82320B009FDB15 /* SetupHandleTextField.swift */; };
A7A14C631A82560000586EAD /* StepOneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7A14C621A82560000586EAD /* StepOneView.swift */; };
A7A14C651A8277C300586EAD /* SetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7A14C641A8277C300586EAD /* SetupView.swift */; };
A7CC1FFA1A6C8FEB00B49AA9 /* MASShortcut.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7CC1FF91A6C8FEB00B49AA9 /* MASShortcut.framework */; };
A7CC20071A6C992F00B49AA9 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7CC20061A6C992F00B49AA9 /* SwiftyJSON.swift */; };
A7CD7F121A6FBB2400BE31D6 /* node.js in Resources */ = {isa = PBXBuildFile; fileRef = A7CD7F111A6FBB2400BE31D6 /* node.js */; };
......@@ -121,6 +122,7 @@
A78DAE911A817000009FDB15 /* blackUnderlineTemplate@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "blackUnderlineTemplate@2x.png"; sourceTree = "<group>"; };
A78DAE931A82320B009FDB15 /* SetupHandleTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetupHandleTextField.swift; sourceTree = "<group>"; };
A7A14C621A82560000586EAD /* StepOneView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StepOneView.swift; sourceTree = "<group>"; };
A7A14C641A8277C300586EAD /* SetupView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetupView.swift; sourceTree = "<group>"; };
A7CC1FF91A6C8FEB00B49AA9 /* MASShortcut.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MASShortcut.framework; path = "../../../../../Library/Developer/Xcode/DerivedData/Heartbeat-bwptofoniwaoqzcgkxzhjtbmtjak/Build/Products/Debug/MASShortcut.framework"; sourceTree = "<group>"; };
A7CC1FFB1A6C8FF700B49AA9 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = ../../sandbox/SwiftyJSON/build/Debug/SwiftyJSON.framework; sourceTree = "<group>"; };
A7CC1FFD1A6C901400B49AA9 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = ../../sandbox/SwiftyJSON/build/Debug/SwiftyJSON.framework; sourceTree = "<group>"; };
......@@ -192,6 +194,7 @@
A70634BD1A63114700A75BC0 /* Steps */,
A70634B71A63112600A75BC0 /* SetupStepViewController.swift */,
A70634B51A6310E900A75BC0 /* SetupViewController.swift */,
A7A14C641A8277C300586EAD /* SetupView.swift */,
);
name = Setup;
sourceTree = "<group>";
......@@ -631,6 +634,7 @@
A7E7D7B31A616B0B003501C1 /* HorizontalSplitViewController.swift in Sources */,
A7F0320C1A76F35A00F3A28E /* SetupProgressIndicatorViewController.swift in Sources */,
A78DAE8D1A800177009FDB15 /* ProfileImageComponentControlPanelViewController.swift in Sources */,
A7A14C651A8277C300586EAD /* SetupView.swift in Sources */,
A78DAE941A82320B009FDB15 /* SetupHandleTextField.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
......
......@@ -1033,7 +1033,7 @@
<scene sceneID="Ymg-5u-HY3">
<objects>
<viewController title="ind.ie" id="AU6-3s-RDq" customClass="SetupViewController" customModule="Heartbeat" customModuleProvider="target" sceneMemberID="viewController">
<visualEffectView key="view" appearanceType="vibrantLight" blendingMode="behindWindow" material="light" state="followsWindowActiveState" id="gId-Q7-H7g">
<visualEffectView key="view" appearanceType="vibrantLight" blendingMode="behindWindow" material="light" state="followsWindowActiveState" id="gId-Q7-H7g" customClass="SetupView" customModule="Heartbeat" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="590" height="487"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
......@@ -1065,9 +1065,10 @@
<buttonCell key="cell" type="roundTextured" title="Yes" bezelStyle="texturedRounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="uQp-V1-vRe">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="14" name="AvenirNext-Regular"/>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
<connections>
<action selector="nextStep:" target="AU6-3s-RDq" id="Vmx-Hv-wQw"/>
<action selector="yesButtonPressed:" target="AU6-3s-RDq" id="Igb-2a-Wh6"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jzL-oE-aZr">
......@@ -1080,7 +1081,7 @@
<font key="font" size="14" name="AvenirNext-Regular"/>
</buttonCell>
<connections>
<action selector="previousStep:" target="AU6-3s-RDq" id="jSY-KM-qm4"/>
<action selector="backButtonPressed:" target="AU6-3s-RDq" id="2lf-Uj-Kbg"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="azA-4A-JHj">
......@@ -1107,6 +1108,7 @@
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<accessibility help="Accepts and moves to the next step of the setup process."/>
</textField>
</subviews>
<constraints>
......@@ -1129,6 +1131,7 @@
<constraint firstItem="jzL-oE-aZr" firstAttribute="top" secondItem="ge6-cT-KvW" secondAttribute="bottom" constant="22" id="y1o-fD-8gF"/>
<constraint firstItem="cSn-Q6-cHP" firstAttribute="leading" secondItem="gId-Q7-H7g" secondAttribute="leading" constant="20" id="yWl-ld-8vS"/>
</constraints>
<accessibility description="Account Setup" help="Creates or signs into your account on Indie."/>
</visualEffectView>
<connections>
<outlet property="backButton" destination="jzL-oE-aZr" id="WOc-3Q-Tvh"/>
......@@ -1146,7 +1149,7 @@
<scene sceneID="7ju-kb-m8c">
<objects>
<viewController title="Select your account" storyboardIdentifier="Step1" id="4K3-FT-bB9" customClass="StepOneViewController" customModule="Heartbeat" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="Ys6-dQ-Qf4" customClass="StepOneView" customModule="Heartbeat" customModuleProvider="target">
<view key="view" id="Ys6-dQ-Qf4">
<rect key="frame" x="0.0" y="0.0" width="590" height="487"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
......@@ -1159,14 +1162,14 @@
</textFieldCell>
<accessibility description="Greeting"/>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="RGu-J6-FTj">
<rect key="frame" x="113" y="188" width="364" height="50"/>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RGu-J6-FTj">
<rect key="frame" x="113" y="187" width="364" height="50"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="360" id="27u-28-Wva"/>
<constraint firstAttribute="height" constant="50" id="366-2v-3I9"/>
<constraint firstAttribute="width" priority="250" constant="360" id="c32-WG-mbg"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" alignment="center" placeholderString="(what shall we call you?)" allowsEditingTextAttributes="YES" id="fqq-U0-DME">
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="center" placeholderString="What shall we call you?" allowsEditingTextAttributes="YES" id="fqq-U0-DME">
<customFormatter key="formatter" id="BDp-cE-Jbv" customClass="IndieNameFormatter" customModule="Heartbeat" customModuleProvider="target"/>
<font key="font" size="32" name="AvenirNext-Italic"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
......@@ -1175,6 +1178,7 @@
<accessibility description="Handle" help="Sets the account handle you want to use on Indie."/>
<connections>
<accessibilityConnection property="title" destination="UBx-uQ-GES" id="aCS-60-gxi"/>
<action selector="acceptHandle:" target="4K3-FT-bB9" id="ETj-Jn-qGf"/>
</connections>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="pwd-bE-SCo">
......@@ -1197,7 +1201,7 @@
<constraint firstAttribute="height" constant="102" id="JOV-uv-IrJ"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="indie-logo" id="tX9-lU-26D"/>
<accessibility description="Indie logo"/>
<accessibility description="Indie Logo"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="fJN-Te-9NB">
<rect key="frame" x="115" y="175" width="360" height="5"/>
......@@ -1227,6 +1231,8 @@
</view>
<connections>
<outlet property="greetingLabel" destination="UBx-uQ-GES" id="xdj-Qj-OJK"/>
<outlet property="handleUnderlineImage" destination="fJN-Te-9NB" id="nhx-gP-GgS"/>
<outlet property="indieLogoImage" destination="Q14-lB-euK" id="bRd-Wq-LeB"/>
<outlet property="nameTextField" destination="RGu-J6-FTj" id="gAD-So-D7Q"/>
<outlet property="nameTextFieldCopyForMeasurements" destination="pwd-bE-SCo" id="s9H-Cm-zeB"/>
<outlet property="nameTextFieldWidthConstraint" destination="27u-28-Wva" id="fiN-Uq-hpS"/>
......
......@@ -41,10 +41,10 @@ func delay(delay:Double, closure:()->())
}
//
// Allows graceful overriding of an NSViewController subclass’s initialisation
// Allows graceful overriding of any NSResponder subclass’s initialisation
// using a common initialiser.
//
extension NSViewController {
extension NSResponder {
enum InitMethod {
case Default
case Coder(NSCoder)
......
......@@ -14,6 +14,8 @@ import Cocoa
protocol SetupDelegate
{
func nextStep()
func previousStep()
func showProgress(message:String)
func hideProgress()
func showTip(tip:String)
......
//
// SetupView.swift
// Heartbeat
//
// Created by Aral Balkan on 04/02/2015.
// Copyright (c) 2015 Ind.ie. All rights reserved.
//
import Cocoa
class SetupView: NSVisualEffectView {
//
// MARK: - Override the initialisers gracefully.
//
override convenience init() {
self.init(.Default)
}
required convenience init(coder aDecoder: NSCoder) {
self.init(.Coder(aDecoder))
}
private init(_ initMethod:InitMethod) {
switch initMethod {
case .Default:
super.init()
case let .Coder(coder):
super.init(coder: coder)
}
// Common setup
self.setAccessibilityRole(NSAccessibilityGroupRole)
}
override func accessibilityIsIgnored() -> Bool {
return false
}
}
......@@ -29,7 +29,7 @@ class SetupViewController: NSViewController, SetupDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Required for transitions to work.
self.view.wantsLayer = true
......@@ -89,81 +89,14 @@ class SetupViewController: NSViewController, SetupDelegate {
self.currentViewController.done()
}
@IBAction func nextStep(sender:NSButton)
@IBAction func yesButtonPressed(sender:NSButton)
{
//
// User asked to proceed to the next step.
//
if self.currentStep != self.numberOfSteps
{
let currentViewController:SetupStepViewController = self.viewControllers[currentStep-1]
self.currentStep++
prepareInterfaceForChangeToStep(self.currentStep)
// Get any data the current view controller may want to pass to the next one.
var dataFromCurrentStep:AnyObject? = currentViewController.dataToPassToNextStep()
let nextViewController:SetupStepViewController = self.viewControllers[self.currentStep-1]
self.addChildViewController(nextViewController)
// Pass any data that we got from the current view controller to the next one.
nextViewController.dataReceivedFromPreviousStep(dataFromCurrentStep)
// Transition to the next view controller.
self.transitionFromViewController(self.currentViewController, toViewController: nextViewController, options: .SlideLeft)
{
() -> Void in
// Update the current view controller to point to the new one.
self.currentViewController.removeFromParentViewController()
self.currentViewController = nextViewController
}
}
else
{
println("This should not happen: next step called on the last step. Not navigating.")
}
nextStep()
}
@IBAction func previousStep(sender:NSButton)
@IBAction func backButtonPressed(sender:NSButton)
{
//
// User asked to return to the last step.
//
if currentStep != 1
{
currentStep--
prepareInterfaceForChangeToStep(currentStep)
let nextViewController:SetupStepViewController = self.viewControllers[currentStep-1]
self.addChildViewController(nextViewController)
self.transitionFromViewController(self.currentViewController, toViewController: nextViewController, options: .SlideRight)
{
() -> Void in
// Update the current view controller to point to the new one.
self.currentViewController.removeFromParentViewController()
self.currentViewController = nextViewController
// Give the view controller a change to restore its state for common UI elements.
// (e.g., the message that was displaying)
nextViewController.prepareForDisplayViaBackButton()
}
}
else
{
println("This should not happen: previous step called on the first step. Not navigating.")
}
previousStep()
}
......@@ -171,6 +104,7 @@ class SetupViewController: NSViewController, SetupDelegate {
// MARK: Utilities
//
func prepareInterfaceForChangeToStep(step:Int)
{
hideMessage()
......@@ -238,6 +172,97 @@ class SetupViewController: NSViewController, SetupDelegate {
//
func nextStep() {
//
// User asked to proceed to the next step.
//
if self.currentStep != self.numberOfSteps
{
let currentViewController:SetupStepViewController = self.viewControllers[currentStep-1]
self.currentStep++
prepareInterfaceForChangeToStep(self.currentStep)
// Accessibility — update user on what is happening.
makeAccessibilityAnnouncement("Handle accepted. Transitioning to second step of the setup process: Setup bio.")
// Delay to allow voiceover a chance to read the transition
// TODO: Detect voiceover and only introduce delay if it exists.
delay(2.5)
{
// Get any data the current view controller may want to pass to the next one.
var dataFromCurrentStep:AnyObject? = currentViewController.dataToPassToNextStep()
let nextViewController:SetupStepViewController = self.viewControllers[self.currentStep-1]
self.addChildViewController(nextViewController)
// Pass any data that we got from the current view controller to the next one.
nextViewController.dataReceivedFromPreviousStep(dataFromCurrentStep)
// Transition to the next view controller.
self.transitionFromViewController(self.currentViewController, toViewController: nextViewController, options: .SlideLeft)
{
() -> Void in
// Update the current view controller to point to the new one.
self.currentViewController.removeFromParentViewController()
self.currentViewController = nextViewController
}
}
}
else
{
println("This should not happen: next step called on the last step. Not navigating.")
}
}
func previousStep()
{
//
// User asked to return to the last step.
//
if self.currentStep != 1
{
self.currentStep--
prepareInterfaceForChangeToStep(self.currentStep)
// Accesibility
makeAccessibilityAnnouncement("Returning to first step of the setup process: select handle.")
// Delay to allow voiceover a chance to read the transition
// TODO: Detect voiceover and only introduce delay if it exists.
delay(1.5)
{
let nextViewController:SetupStepViewController = self.viewControllers[self.currentStep-1]
self.addChildViewController(nextViewController)
self.transitionFromViewController(self.currentViewController, toViewController: nextViewController, options: .SlideRight)
{
() -> Void in
// Update the current view controller to point to the new one.
self.currentViewController.removeFromParentViewController()
self.currentViewController = nextViewController
// Give the view controller a change to restore its state for common UI elements.
// (e.g., the message that was displaying)
nextViewController.prepareForDisplayViaBackButton()
}
}
}
else
{
println("This should not happen: previous step called on the first step. Not navigating.")
}
}
func showMessage(title: String, body: String, nextButtonTitle: String)
{
showMessage(title, body: body)
......
......@@ -15,6 +15,8 @@ class StepOneViewController: SetupStepViewController
@IBOutlet weak var nameTextField: NSTextField!
@IBOutlet weak var nameTextFieldCopyForMeasurements: NSTextField!
@IBOutlet weak var nameTextFieldWidthConstraint: NSLayoutConstraint!
@IBOutlet weak var indieLogoImage: NSImageView!
@IBOutlet weak var handleUnderlineImage: NSImageView!
var timer:NSTimer!
var viewIsBeingShownForTheFirstTime:Bool = true
......@@ -23,15 +25,14 @@ class StepOneViewController: SetupStepViewController
{
super.viewDidLoad()
self.view.setAccessibilityLabel("Setup: step one of two. Choose your Indie handle")
self.view.setAccessibilityRole(NSAccessibilityGroupRole)
nameTextField.accessibilitySetOverrideValue(self.greetingLabel, forAttribute: NSAccessibilityTitleUIElementAttribute)
// greetingLabel.setAccessibilityServesAsTitleForUIElements([nameTextField])
// nameTextField.setAccessibilityLabelUIElements([greetingLabel])
nameTextField.focusRingType = NSFocusRingType.None
// Accessibility
// self.view.setAccessibilityLabel("Setup: step one of two. Choose your Indie handle")
// self.view.setAccessibilityRole(NSAccessibilityGroupRole)
// NSNotificationCenter.defaultCenter().when(youGet: NSControlTextDidChangeNotification, call: "textDidChange:", on: self)
self.indieLogoImage.setAccessibilityElement(false)
self.handleUnderlineImage.setAccessibilityElement(false)
nameTextField.focusRingType = NSFocusRingType.None // Instead of the focus ring, we use an under in this.
NSNotificationCenter.defaultCenter().when(youGet: NSControlTextDidChangeNotification, from: nameTextField, call: "textDidChange:", on: self)
......@@ -52,7 +53,15 @@ class StepOneViewController: SetupStepViewController
}
}
// MARK: - Actions
@IBAction func acceptHandle(sender: AnyObject) {
// User pressed enter in the field to accept the handle
self.nameTextField.selectable = false
self.delegate?.nextStep()
}
// MARK: - Pass data to the next step.
override func dataReceivedFromPreviousStep(data: AnyObject?)
......@@ -147,7 +156,7 @@ class StepOneViewController: SetupStepViewController
func showHandleIsAvailableMessage()
{
self.delegate?.showMessage("That handle is available!", body: "Would you like to sign up as ind.ie/\(self.nameTextField.stringValue)?", nextButtonTitle: "Yes")
self.delegate?.showMessage("That handle is available!", body: "Would you like to sign up as ind.ie/\(self.nameTextField.stringValue)? Press enter for yes.", nextButtonTitle: "Yes")
}
//
......@@ -156,6 +165,8 @@ class StepOneViewController: SetupStepViewController
override func prepareForDisplayViaBackButton()
{
self.nameTextField.selectable = true
self.nameTextField.editable = true
checkIfNameExists()
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment