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("metabblv@163.com")
            .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:"。没过于纠结,后面如果可以调试了,我会将图补充的。

相关推荐
小溪彼岸3 天前
【iOS小组件】小组件尺寸及类型适配
swiftui·swift
文件夹__iOS7 天前
[SwiftUI 开发] @dynamicCallable 与 callAsFunction:将类型实例作为函数调用
ios·swiftui·swift
小溪彼岸7 天前
【iOS小组件】iOS17与低版本兼容适配
swiftui·swift
Mamong9 天前
SwiftUI疑难杂症(1):sheet content多次执行
ios·swiftui·swift
AUV110712 天前
Mac剪贴板历史全记录!
macos·swiftui·mac·效率工具·实用工具·剪贴板·clipboard
AUV110713 天前
Mac 上哪个剪切板增强工具比较好用? 好用剪切板工具推荐
macos·swiftui·mac·剪贴板·clipboard·剪贴板增强·app 推荐
多彩电脑14 天前
SwiftUI里的ForEach使用的注意事项
macos·ios·swiftui·swift
Swift社区16 天前
Apple 新品发布会亮点有哪些 | Swift 周报 issue 61
ios·swiftui·swift
humiaor17 天前
Xcode报错:No exact matches in reference to static method ‘buildExpression‘
swiftui·xcode
humiaor25 天前
Xcode报错:Return from initializer without initializing all stored properties
swiftui·binding