You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NavigationView中从下级页面返回时如何保留List里的Accessibility焦点

解决方案

核心实现逻辑是记录用户最后选中的章节标识,导航返回列表页时手动将Accessibility焦点移动到对应行。iOS 15及以上版本可直接使用SwiftUI原生的@AccessibilityFocusState实现,代码如下:

import SwiftUI

struct ContentView: View {
    // 存储最后选中的章节索引
    @State private var selectedChapter: Int?
    // 管理辅助功能焦点
    @AccessibilityFocusState private var focusedChapter: Int?
    // 标记当前是否处于详情页,避免首次进入列表时乱跳焦点
    @State private var isInDetailPage = false
    
    var body: some View {
        NavigationView {
            List(1..<10) { row in
                NavigationLink(tag: row, selection: $selectedChapter) {
                    // 详情页逻辑
                    Text("第\(row)章内容")
                        .onAppear {
                            isInDetailPage = true
                        }
                        .onDisappear {
                            isInDetailPage = false
                        }
                } label: {
                    Text("第\(row)章")
                        // 绑定当前行的焦点匹配值
                        .accessibilityFocused($focusedChapter, equals: row)
                }
            }
            .navigationTitle("章节列表")
            .onAppear {
                // 仅从详情页返回时触发焦点移动
                guard !isInDetailPage, let lastSelected = selectedChapter else { return }
                // 等待转场动画、列表渲染完成后移动焦点
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                    focusedChapter = lastSelected
                    // 可选:让VoiceOver直接朗读选中项的内容
                    UIAccessibility.post(notification: .announcement, argument: "第\(lastSelected)章")
                }
            }
        }
        .navigationViewStyle(.stack)
    }
}

兼容iOS 14及更低版本的方案

如果需要适配更低系统版本,替换onAppear中的焦点移动逻辑,通过UIKit的辅助功能通知实现:

.onAppear {
    guard !isInDetailPage, let lastSelected = selectedChapter else { return }
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        // 按标识查找对应章节的控件
        guard let window = UIApplication.shared.connectedScenes
                .filter({ $0.activationState == .foregroundActive })
                .compactMap({ $0 as? UIWindowScene })
                .first?.windows
                .first(where: \.isKeyWindow),
              let targetElement = window.accessibilityElement(withIdentifier: "chapter_\(lastSelected)")
        else { return }
        // 发送通知移动VoiceOver焦点
        UIAccessibility.post(notification: .layoutChanged, argument: targetElement)
    }
}

使用该方案需要提前给每一行的列表元素添加唯一的accessibilityIdentifier:

Text("第\(row)章")
    .accessibilityIdentifier("chapter_\(row)")

注意事项

  • 0.1秒的延迟是为了等待Navigation转场动画结束、列表元素完全渲染,可根据实际情况微调,避免因元素未加载完成导致焦点移动失败。
  • 如果列表内容是动态异步加载的,需要等列表数据加载完成后再触发焦点移动逻辑。
  • 不要省略isInDetailPage的判断,否则首次进入列表页时会跳过默认的焦点位置,影响首次打开的辅助功能体验。

内容的提问来源于stack exchange,提问作者Melodius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 09:06:04