SwiftUI 中为何 DisclosureGroup 视图在收缩时没有动画效果?

概览

小伙伴们肯定都惊叹过 SwiftUI 在界面设计中摧枯拉朽和辞简理博的强大,也一定被 SwiftUI 开发中各种"恢诡谲怪"的奇葩问题搞的"五内俱崩"、欲哭无泪。

在这些种种问题中,动画效果不尽如人意是很常见的一类。

如上图所示,我们的 DisclosureGroup 在收缩时并没有对应"可耐"的动画,显得很生硬!这是怎么回事呢?

在本篇博文中,您将学到如下内容:

  1. SwiftUI 中的 DisclosureGroup 视图
  2. "动画是个好东西,可惜你没有"
  3. 彗汜画涂,一发入魂!

在 SwiftUI 中,动画效果不能如愿大概率是秃头码农们自己撸码的不是。我们必须知其然同时知其所以然才能泰然处之的将它们"全面瓦解"。

那还等什么呢?Let's fix it!!!;)


SwiftUI 中的 DisclosureGroup 视图

从 SwiftUI 2.0(iOS 14)开始,苹果增加了全新的 DisclosureGroup 原生视图:

DisclosureGroup 的作用很简单:就是节省空间。我们可以通过它来有的放矢的展开和收起内容视图,从而合理的精简界面布局。

swift 复制代码
struct ContentView: View {
    
    @State var isExpanding = false
    @State var sltingValue: Int?
    
    var body: some View {
        NavigationStack {
            Form {
                DisclosureGroup(
                    isExpanded: $isExpanding.animation(),
                    content: {
                        List {
                            ForEach(0...10, id: \.self) { i in
                                Text("Value \(i)")
                                    .font(.title2.weight(.bold))
                                    .swipeActions(edge: .trailing, allowsFullSwipe: false) {
                                        Button("选择") {
                                            sltingValue = i
                                            isExpanding = false
                                        }
                                        .tint(.green)
                                    }
                            }
                            Button("取消选择") {
                                sltingValue = nil
                                isExpanding = false
                            }
                        }
                    },
                    label: {
                        VStack(alignment: .leading) {
                            if let sltingValue {
                                Text("当前选择值:\(sltingValue)")
                                    .font(.title)
                            }
                            Spacer()
                            Text("当前收展状态:[\(isExpanding ? "展开" : "收起")]")
                        }
                    }
                )
            }
            .toolbar {
                Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))")
                    .font(.headline)
                    .foregroundStyle(.gray)
            }
            .animation(.bouncy, value: isExpanding)
        }
    }
}

在如上代码中,我们利用 DisclosureGroup 视图控制着冗长列表内容的显示和隐藏。可以看到,我们实际是用 isExpanding 状态来反映且驱动 DisclosureGroup 收展状态的。

"动画是个好东西,可惜你没有"

现在我们已初步知晓了 DisclosureGroup 的作用,那么回到博文开头那个例子中去:为什么示例中 DisclosureGroup 的收缩没有动画点缀呢?

要探索和回答这个问题,我们有必要先来看看示例的源代码:

swift 复制代码
DisclosureGroup(isExpanded: $isCheckInSelectPanelExpanding.animation(.bouncy), content: {
    HStack {
        DatePicker("", selection: $pickingTime, displayedComponents: [.hourAndMinute])
        
        Button(action: {
            isCheckInSelectPanelExpanding = false
            
            withAnimation {
                checkInTime = pickingTime
            }
        }) {
            Text("确定")
        }
        .buttonStyle(.borderedProminent)
        
        Button(action: {
            isCheckInSelectPanelExpanding = false
            
            withAnimation {
                checkInTime = nil
            }
        }) {
            Text("取消打卡")
        }
        .buttonStyle(.borderedProminent)
        .tint(Color.red)
        .disabled(checkInTime == nil)
    }
    
}, label: {
    HStack {
        Text("规定打卡时间")
        Spacer()
        Text("\(checkInTime == nil ? "无" : V2_Model.hmFormater.string(from: checkInTime!))")
            .foregroundStyle(checkInTime == nil ? .gray : .primary)
    }
})

可以看到上面代码和之前几乎如出一辙:我们是用 isCheckInSelectPanelExpanding 状态来驱动 DisclosureGroup 的展开和收缩。我们在 DisclosureGroup 展开按钮的 action 中知趣的将 isCheckInSelectPanelExpanding 设置为 false,这样在完成按钮的功能之后立即收起 DisclosureGroup 有助于节省用户宝贵的时间。

但是为什么 DisclosureGroup 收起动画却不翼而飞了呢?

彗汜画涂,一发入魂!

仔细观察示例代码中的逻辑可以发现,当 isCheckInSelectPanelExpanding 被置为 false 时,就会导致 DisclosureGroup 的内容视图(Content View)从界面上被"清除"掉。

如果在一个即将被"删除"的视图上应用动画,那么多半人家不会鸟你。

所以,我们要做的就是让 SwiftUI 系统明确知道我们需要 isCheckInSelectPanelExpanding 状态的改变产生动画效果。

怎么操作呢?其实很简单:我们只需将 isCheckInSelectPanelExpanding 放到显式动画块中即可。

swift 复制代码
DisclosureGroup(isExpanded: $isCheckInSelectPanelExpanding.animation(.bouncy), content: {
    HStack {
        DatePicker("", selection: $pickingTime, displayedComponents: [.hourAndMinute])
        
        Button(action: {
            
            withAnimation {
                checkInTime = pickingTime
                isCheckInSelectPanelExpanding = false
            }
        }) {
            Text("确定")
        }
        .buttonStyle(.borderedProminent)
        
        Button(action: {
            
            withAnimation {
                checkInTime = nil
                isCheckInSelectPanelExpanding = false
            }
        }) {
            Text("取消打卡")
        }
        .buttonStyle(.borderedProminent)
        .tint(Color.red)
        .disabled(checkInTime == nil)
    }
    
}, label: {
    HStack {
        Text("规定打卡时间")
        Spacer()
        Text("\(checkInTime == nil ? "无" : V2_Model.hmFormater.string(from: checkInTime!))")
            .foregroundStyle(checkInTime == nil ? .gray : .primary)
    }
})

更多关于 SwiftUI 显式和隐式动画的介绍,请小伙伴们移步如下链接观赏:


在将 isCheckInSelectPanelExpanding 状态的改变改为显式动画后,我们再来运行看一下效果:

现在丝般顺滑的动画又一次"大圣归来",我们可以继续我们的 App 开发"仲夏夜"之梦啦,棒棒哒!💯

总结

在本篇博文中,我们介绍了 SwiftUI 2.0 新增的 DisclosureGroup 原生视图,并接着讨论了为什么它的收起操作没有动画,并最后给出解决方案。

感谢观赏,再会!8-)

相关推荐
songgeb4 天前
YYImage Bug:一行之差,正常 JPEG 如何在 iOS 被读成条纹
ios·swift
fthux4 天前
Tick Rest,一个休息提醒工具的自我修养
macos·开源·swift
Fatbobman(东坡肘子)5 天前
快速消失的硬盘容量 -- 肘子的 Swift 周报 #160
开发语言·人工智能·redis·ios·swiftui·iphone·swift
chaoahyl5 天前
WaterDiary 喝水记录 App 实战(2)· 实现喝水首页与本地保存
swift
茶底世界之下6 天前
iPhone Duo 适配实战,Awaking 踩过的 8 个坑
ios·swiftui·swift
xbzb6 天前
Swift 对象存储建环实验 + Ceilometer 计量:两个收尾服务
swift·ring·实验·ceilometer·象存储·建环
2501_916008896 天前
iOS App 图标、启动屏和多语言搭配 方法,上架前使用KXBuild一次做好 App 图标
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
2501_915918416 天前
iOS 推送通知怎么实现,从 APNs p8 密钥到 iOS 真机收到第一条推送
vscode·ios·objective-c·个人开发·swift·敏捷流程
东坡肘子6 天前
快速消失的硬盘容量 -- 肘子的 Swift 周报 #160
人工智能·swiftui·swift
2501_916007478 天前
Windows OpenSSL 生成 iOS 证书,制作 p12 与描述文件,KXBuild 签名实操
windows·vscode·ios·objective-c·个人开发·swift·敏捷流程