You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

watchOS 10+ 无DisclosureGroup时如何实现可折叠列表分区?

watchOS 10+ 无DisclosureGroup时如何实现可折叠列表分区?

太懂这种无奈了!watchOS 10+居然没给我们提供DisclosureGroup,想用iOS那套直接实现可折叠分区根本行不通——不过别慌,咱们自己搭一个完全能达到同样的效果,而且动画丝滑,体验和原生看齐!

核心思路

其实原理很简单:用一个绑定的布尔值控制分区的展开/收起状态,把分区头部改成可点击的按钮,再给内容加上过渡动画就搞定了。

基础实现代码

先从单个可折叠分区开始,代码一目了然:

import SwiftUI

struct CollapsibleSectionView: View {
    // 控制分区展开状态的变量
    @State private var isSectionExpanded = false
    
    var body: some View {
        List {
            Section {
                // 只有展开状态下才渲染内容
                if isSectionExpanded {
                    ForEach(0..<5) { index in
                        Text("列表项 \(index + 1)")
                    }
                    // 给内容加上过渡动画,让展开收起更自然
                    .transition(.opacity.combined(with: .move(edge: .top)))
                }
            } header: {
                // 把头部做成可点击的按钮
                Button(action: {
                    // 包裹在withAnimation里,让状态切换带动画
                    withAnimation(.easeInOut(duration: 0.2)) {
                        isSectionExpanded.toggle()
                    }
                }) {
                    HStack {
                        Text("My Header")
                            .font(.headline)
                        Spacer()
                        // 根据状态切换 Chevron 图标
                        Image(systemName: isSectionExpanded ? "chevron.down" : "chevron.right")
                            .font(.caption)
                    }
                    .foregroundColor(.primary)
                }
                // 用PlainButtonStyle去掉按钮默认的高亮样式,更接近原生分区头
                .buttonStyle(PlainButtonStyle())
            }
        }
    }
}

多分区场景优化

如果你的列表有多个可折叠分区,单独管理每个状态变量会很麻烦,建议把分区数据封装成模型:

// 定义分区模型,管理每个分区的标题、展开状态和子项
struct SectionModel: Identifiable {
    let id = UUID()
    let title: String
    var isExpanded: Bool
    let items: [String]
}

struct MultiCollapsibleSectionsView: View {
    @State private var sections: [SectionModel] = [
        SectionModel(title: "我的收藏", isExpanded: false, items: ["照片", "音乐", "视频"]),
        SectionModel(title: "最近使用", isExpanded: true, items: ["文档A", "笔记B", "链接C"])
    ]
    
    var body: some View {
        List {
            ForEach($sections) { $section in
                Section {
                    if section.isExpanded {
                        ForEach(section.items, id: \.self) { item in
                            Text(item)
                        }
                        .transition(.opacity.combined(with: .move(edge: .top)))
                    }
                } header: {
                    Button(action: {
                        withAnimation {
                            section.isExpanded.toggle()
                        }
                    }) {
                        HStack {
                            Text(section.title)
                                .font(.headline)
                            Spacer()
                            Image(systemName: section.isExpanded ? "chevron.down" : "chevron.right")
                        }
                        .foregroundColor(.primary)
                    }
                    .buttonStyle(PlainButtonStyle())
                }
            }
        }
    }
}

小技巧

  • 动画时长可以根据自己的需求调整,0.2-0.3秒左右的时长在watchOS上会很舒服
  • 如果想让过渡效果更丰富,可以试试其他Transition类型,比如scale或者slide
  • 按钮的样式可以根据你的App设计自定义,比如调整字体、颜色或者间距,完全适配你的UI风格

这样实现出来的效果和iOS上的DisclosureGroup几乎一模一样,点击头部就能切换状态,动画流畅,完全适配watchOS 10+的环境!

内容来源于stack exchange

火山引擎 最新活动