SwiftUI基础篇Accessibility

Accessibility

概述

文章主要分享SwiftUI Modifier的学习过程,将使用案例的方式进行说明。内容浅显易懂,Accessibility未调试,不过测试代码是齐全的。如果想要运行结果,可以移步Github下载code -> github案例链接

1、关于SwiftUI的Accessibility

默认情况下,SwiftUI应用程序具有非常高的可访问性,除非主动更改默认设置。这种行为的关键是SwiftUI布局是基于Stack的方法。将视图放在Hstack和VStack中,一切都有默认的顺序,因此系统可以大致理解布局应该如何设定。相比之下,UIKit和AutoLayout可以将视图放在任何地方,因此系统必须有效的对视图应该如何排序做出最佳猜测。

SwiftUI还建议我们为所有的交互添加标签,明确用途。但是,我们可以屏蔽标签,以及它们被隐藏,系统仍然会使用它们作为屏幕阅读器的音频提示。因此,SwiftUI提供了很多的可访问性。然而,还有额外的工具来更好的体验来解决下面的问题:

  • 屏幕阅读器应该如何阅读内容?
  • 是否所有内容都需要阅读?
  • 如果用户不喜欢复杂的动画怎么办?
  • ...

2、如何将DynamicType与自定义Font一起使用

如果在iOS14或更高的版本,就可以实现自动移Font的自动缩放。但是,如果需要字体相对于特定动态类型进行缩放,则使用relativeTo修饰符。

Swift 复制代码
struct FFFontDynamicType: View {
    var body: some View {
        //使字体从24pt开始,但它会相对于Headline DynamicType字体进行缩放。
        Text("meta BBLv")
            .font(.custom("Georgia", size: 24, relativeTo: .headline))
        //如果想禁用字体的动态类型,使用fixedSize修饰符字体大小,无论动态类型如何设置都不会影响字体大小
        Text("[email protected]")
            .font(.custom("Georgia", fixedSize: 24))
    }
}

3、如何指定视图支持的动态类型的大小

SwiftUI对动态类型的自动支持意味着视图可以根据用户的偏好放大或缩小。但是,一些情况下会超出屏幕导致UI的不一致,可以使用dyanmicTypeSize()来限制。

Swift 复制代码
struct FFSpecifyDynamicType: View {
    var body: some View {
        VStack {
            //与固定值一起使用,意味着视图将忽略所有动态类型的大小
            Text("This will stay small")
                .dynamicTypeSize(.xxLarge)
            //可以指定范围
            Text("This won't go above large")
                .dynamicTypeSize(...DynamicTypeSize.large)
            
            Text("This will scale within a range")
                .dynamicTypeSize(DynamicTypeSize.large...DynamicTypeSize.xxxLarge)
            
            Text("This will scale to any Size")
            //许多用户喜欢以来较大的动态字体来使用App。但是,随着屏幕内信息量的增加,
            //在某些特定的场景下要限制这种特性。
        }
    }
}

4、如何检测"reduce motion"的关联功能设置

随着iOS的发展,越来越多的用户对画面更加敏感、更挑剔,尤其是那些大型或复杂的动画。因此,iOS有一个名为"reduce motion"的内置辅助功能设置,应用程序可以读取该设置并根据需要做出响应。现在由需求来决定"reduce motion"的具体含义:

  • 删除动画?
  • 还是将动画更改为不那么强烈?
  • 是否应该保留一些重要的动画并删除那些视觉吸引力的动画?

例如,如果希望为大多数用户提供弹性动画,但对于想要reduce motion的用户根本不需要动画,如何设定。

Swift 复制代码
struct FFAccessibilityReduceMotion: View {
    //在SwiftUI中,此设置作为环境的Bool值公布,将其属性添加到视图中
    @Environment(\.accessibilityReduceMotion) var reduceMotion
    @State private var scale = 1.0
    
    var body: some View {
        VStack {
            Spacer()
            Circle()
                .frame(width: 20, height: 20)
                .scaleEffect(scale)
                .animation(reduceMotion ? nil : .spring(response: 1, dampingFraction: 0.1), value: scale)
            Spacer()
            Button("Increase scale") {
                scale *= 1.5
            }
        }
    }
}

这会创建一个小圆圈,每次按下按钮时都会通过弹簧动画将其放大。但如果用户启用"reduce motion",动画将被完全删除。

5、如何检测深色模式

SwiftUI可以使用环境检测键(colorScheme)检测当前是否启用了深色模式或浅色模式。如果使用了@environment声明了此属性,就可以在视图中引用它,并且当配色方案更改时将自动加载。

Swift 复制代码
struct FFAccessibilityDarkMode: View {
    @Environment(\.colorScheme) var colorScheme
    var body: some View {
        Text(colorScheme == .dark ? "META BBLV in dark mode" : "META BBLV in light mode")
    }
}

6、如何使用装饰图像来减少屏幕阅读器的混乱

SwiftUI会自动使用图像名称作为屏幕阅读器标签,通常情况下会很有用。然而,有些图像并不适合阅读,因为它只是装饰性的。它们不会传达屏幕其他地方没有的信息,只是为了让用户界面看起来更好看。

Image("star")这种方式创建的图像,则会将她们作为标准UI的一部分读出。更好的方式是使用Image(decorative:)初始化程序来创建它们,它告诉SwiftUI图像不应该暴露给屏幕阅读去器。

Swift 复制代码
struct FFAccessibilityDecorative: View {
    var body: some View {
        Image(decorative: "star")
        //通过这种方式构建的UI,使用VoiceOver检查时,屏幕阅读器是不会读出此标签的。
    }
}

7、如何根据需求减少动画

SwiftUI的withAnimation()修饰符可以轻松的在视图上执行自定义动画,但它不遵守"reduce mode"辅助功能的设置。因此对于有特定需求的人来讲,可能不符合需求了。

Swift 复制代码
struct FFAccessibilityReduceModeRequest: View {
    @State private var scale = 1.0
    
    var body: some View {
        Text("Meta BBLv")
            .scaleEffect(scale)
            .onTapGesture {
                withOptionalAimation {
                    scale *= 1.5
                }
            }
    }
    
    //如果想要在withAnimation()同时遵守辅助功能的设置,添加一个全局函数可能是一个有效的解决方案
    func withOptionalAimation<Result>(_ animation: Animation? = .default, _ body: () throws -> Result) rethrows -> Result {
        if UIAccessibility.isReduceMotionEnabled {
            return try body()
        } else {
            return try withAnimation(animation, body)
        }
    }
    //在每次触发动画是,都会自动检查是否启用了"reduce mode",并为特别请求reducemode的用户来禁用它。
}

8、如何让VoiceOver单独朗读字符

大多数的文本都可以作为单词阅读,但某些特殊文本(例如密码,股票代码和某些特定的数字)必须通过VoiceOver诸葛字母的阅读才能发挥作用。在SwiftUI中,可以使用speechSpellsOutCharacters()修饰符启用此功能。

Swift 复制代码
struct FFAccessibilityVoiceOver: View {
    var body: some View {
        //当对整组元素启用辅助功能时,会有更好的结果
        VStack {
            Text("Your password is")
            Text("abCayer-muQai")
                .font(.title)
                .speechSpellsOutCharacters()
        }
        .accessibilityElement(children: .combine)
        //使用该代码,VoiceOver会自然的自动读出"你的密码是",然后根据要求拼代码部分。
    }
}

调试意外

我目前的设备情况:

  • macOS:Version 13.4.1 、 Version 14.0 beta 2
  • Xcode:Version 15.0 beta 2 (15A5161b)
  • Simulator iPhone 14 Pro:iOS 17.0 beta 2
  • iPhone 14 Pro:iOS 17.0 beta 6

由于是Accessibility部分调试,涉及到调试中修改控制中心设置问题,所以无法使用模拟器来调试,在真机调试中报出下面的问题:

terminal 复制代码
dyld[5218]: Symbol not found: _$s21DeveloperToolsSupport7PreviewV7SwiftUIE_6traits4bodyACSSSg_AA0D5TraitVyAC10ViewTraitsOGdAD0J0_pyctcfC
  Referenced from: <48CEF6E7-4F6C-3597-9EA3-182CF891DB38> /private/var/containers/Bundle/Application/CBD3CB7C-D7CE-472D-B4BC-46DC960A6B06/FFModifier.app/FFModifier
  Expected in:     <FE724F2F-E9CA-3711-BED5-5124FE98C7C0> /System/Library/Frameworks/SwiftUI.framework/SwiftUI

推测是由于我的iPhone版本与Xcode版本不一致导致的SwiftUI版本不一致问题,所以出现了"Symbol not found:"。没过于纠结,后面如果可以调试了,我会将图补充的。

相关推荐
MaoJiu16 小时前
Flutter造轮子系列:flutter_permission_kit
flutter·swiftui
iOS阿玮1 天前
社交的本质是价值交换,请不要浪费别人的时间。
uni-app·app·apple
大熊猫侯佩1 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(三)
数据库·swiftui·swift
大熊猫侯佩1 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(二)
数据库·swiftui·swift
大熊猫侯佩1 天前
用异步序列优雅的监听 SwiftData 2.0 中历史追踪记录(History Trace)的变化
数据库·swiftui·swift
大熊猫侯佩1 天前
由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(一)
数据库·swiftui·swift
iOS阿玮2 天前
苹果2024透明报告看似更加严格的背后是利好!
uni-app·app·apple
大熊猫侯佩2 天前
SwiftUI 中如何花样玩转 SF Symbols 符号动画和过渡特效
swiftui·swift·apple
大熊猫侯佩2 天前
SwiftData 共享数据库在 App 中的改变无法被 Widgets 感知的原因和解决
swiftui·swift·apple
大熊猫侯佩2 天前
使用令牌(Token)进一步优化 SwiftData 2.0 中历史记录追踪(History Trace)的使用
数据库·swift·apple