首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >添加子视图时,停止SwiftUI列表行中的视图“弹跳”?

添加子视图时,停止SwiftUI列表行中的视图“弹跳”?
EN

Stack Overflow用户
提问于 2022-01-07 15:28:58
回答 1查看 552关注 0票数 0

我正在构建一个应用程序,其中有关一系列参数的摘要信息和历史细节显示在一个列表中。

用户可以通过单击“公开”按钮查看历史指标,DisclosureGroup可以非常灵活地处理这一问题,但我想添加一个按钮,该按钮可以切换控件的显示,使用户能够输入新数据,而无需使用模式显示或从摘要屏幕导航。

尽管您可以嵌套DisclosureGroups,但这并不是很好的UX (用户需要公开两次才能访问他们想要的信息),所以我想通过有条件地向层次结构中添加视图来复制DisclosureGroup动画行为。

外部列表或表单,这是很容易做到的-确保操作切换您的标志显示是包装在withAnimation和SwiftUI照顾其余。不幸的是,这些容器视图的布局方式导致了一些相当奇怪的行为,当一个新视图被添加到一个单元格中时,这种行为会导致预先存在的视图(不包括文本视图)“弹跳”。这种情况不会发生在DisclosureGroup (不管您是使用文本还是其他视图子类型)中,它在添加到列表或窗体时完全不受影响。

在模拟器中减慢动画速度可以明显地显示父视图(即单元格)在添加新子视图之前更改其大小,但这会导致现有视图的位置也发生变化(试图将其锚定在单元格视图的顶部,方法是在包围的HStack上使用对齐修饰符或直接在视图的框架上使用对齐修饰符,这并不能阻止这种情况发生)。

此外,当视图从单元格的层次结构中移除时,该单元格在视图被移除之前收缩,使离开的视图在相邻单元格上盘旋一段时间,这看起来很混乱。不用说,这在DisclosureGroup中不会发生,所以我认为我可以合理地假设我想要的是完全可能的。

我尝试(但失败)通过向每个视图抛出对齐、HStack和VStack容器以及.matchedGeometryEffect修饰符来解决这个问题(后者似乎是最有希望的,因为这似乎最终是视图转换的协调不良)。这要么不起作用,要么导致了一些非常奇怪的行为。

放慢动画速度很明显,DisclosureGroup可能正在使用ZStack和/或自定义转换,这种转换以渐进的方式抵消子视图。然而,我试图复制这一点的尝试有been...interesting。

一张图片值一千字(所以一段视频值一万?)请参阅所附的行为图解及相关守则。

使用文本视图的

代码语言:javascript
运行
复制
struct DisclosureWithTextView: View {
@Namespace private var nspace

@State private var willDisplayControl = false

var body: some View {
        DisclosureGroup(
            content: {
                ForEach(0..<2) { index in
                    Text("Item")
                        .padding(6)
                        .background {
                            Rectangle()
                                .foregroundColor(.yellow)
                        }
                }
            },
            label: {
                VStack {
                    HStack {
                        Button(action: {
                            willDisplayControl.toggle()
                        })
                        {
                            Image(systemName: "plus.circle")
                        }
                        .buttonStyle(PlainButtonStyle())

                        Text("SUMMARY")
                            .padding(8)
                            .background {
                                Rectangle()
                                    .foregroundColor(.red)
                            }
                    }

                    if willDisplayControl {
                        Text("CONTROL")
                            .padding(8)
                            .background {
                                Rectangle()
                                    .foregroundColor(.green)
                            }
                    }
                }

            })

}

}

使用矩形视图的

代码语言:javascript
运行
复制
struct DisclosureWithGraphics: View {
@Namespace private var nspace

@State private var willDisplayControl = false

var body: some View {
    DisclosureGroup(
        content: {
            ForEach(0..<2) { index in
                Rectangle()
                    .frame(width:80, height: 30)
                    .foregroundColor(.yellow)
            }
        },
        label: {
            VStack {
                HStack {
                    Button(action: {
                        withAnimation { willDisplayControl.toggle() }
                    })
                    {
                        Image(systemName: "plus.circle")
                    }
                    .buttonStyle(PlainButtonStyle())

                    Rectangle()
                        .frame(width: 110, height: 40)
                        .foregroundColor(.red)

                }

                if willDisplayControl {
                    Rectangle()
                        .frame(width: 100, height: 40)
                        .foregroundColor(.green)
                }
            }

        })
}

}

EN

Stack Overflow用户

回答已采纳

发布于 2022-01-10 20:31:29

具有讽刺意味的是,我意识到阿斯佩里把我提到了他的解决方案,因为我在18世纪前提出了一个几乎相同的问题!现在感觉有点傻,但这解释了我以前遇到过这个问题的模糊感觉!

然而,我发现有一个重要的区别。如果在列表或表单中使用“香草”堆栈容器视图,那么调整对齐是您需要做的所有工作,以实现一个视图被另一个视图披露的平滑动画。动画容器视图的框架非常好,但这是不必要的。但是,当您在DisclosureGroup中尝试相同的操作时,形成标签的视图就会开始跳来跳去,而不管您是否动画化了对齐向导、框架等,并且您必须显式地动画父视图的高度。

因此,在一个简单的列表或表单单元格中滑动视图很容易(使用AlignmentGuide),但是当您在一个更专业的视图容器中尝试相同的事情时,您需要为单元格高度添加AnimatableValue。如果我猜的话,我怀疑这是因为与苹果自己的信息披露实现有一些冲突。

所以,感谢阿斯佩里提醒我该怎么做:-)

实现以下效果的两种方法的代码示例(我更喜欢对齐指南动画的外观)

调整待披露视图的框架:

代码语言:javascript
运行
复制
struct DisclosureWithFrameAnimationReveal: View {
@Namespace private var nspace
@State private var willDisplayControl = false

var body: some View {

        DisclosureGroup(
            content: {
                ForEach(0..<2) { index in
                    Text("Item")
                        .padding(6)
                        .background {
                            Rectangle()
                                .foregroundColor(.yellow)
                        }
                }
            },
            label: {
                VStack {
                    HStack {
                        Button(action: {
                            withAnimation(.easeInOut) { willDisplayControl.toggle() }
                        })
                        {
                            Image(systemName: "plus.circle")
                        }
                        .buttonStyle(PlainButtonStyle())

                        Color(.red)
                            .frame(width: 100, height: 40)
                        Spacer()
                    }
                    .padding(.top, 4)
                    .background()

                    HStack {
                        Color(.blue)
                            .frame(width: 100, height: willDisplayControl ? 40 : 0)
                        Spacer()
                    }
                    .opacity(willDisplayControl ? 1 : 0)
                }
                .modifier(AnimatableCellHeight(height: willDisplayControl ? 88 : 44))

            }
        )
}
}

对齐指南动画:

代码语言:javascript
运行
复制
struct DisclosureWithAlignmentReveal: View {
@Namespace private var nspace
@State private var willDisplayControl = false

var body: some View {
        DisclosureGroup(
            content: {
                ForEach(0..<2) { index in
                    Text("Item")
                        .padding(6)
                        .background {
                            Rectangle()
                                .foregroundColor(.yellow)
                        }
                }
            },
            label: {
                ZStack(alignment: .top) {
                    HStack {
                        Button(action: {
                            withAnimation(.easeInOut) { willDisplayControl.toggle() }
                        })
                        {
                            Image(systemName: "plus.circle")
                        }
                        .buttonStyle(PlainButtonStyle())

                        Color(.red)
                            .frame(width: 100, height: 40)
                        Spacer()
                    }
                    .zIndex(1)
                    .padding(.top, 4)
                    .background()

                    HStack {
                        Color(.blue)
                            .frame(width: 100, height: 40)
                        Spacer()
                    }
                    .alignmentGuide(.top, computeValue: { d in d[.top] - (willDisplayControl ? 46 : 0) })
                    .opacity(willDisplayControl ? 1 : 0)
                    .zIndex(0)
                }
                .modifier(AnimatableCellHeight(height: willDisplayControl ? 88 : 44))
            }
        )
}
}

,最后是AnimatableValue实现:

代码语言:javascript
运行
复制
struct AnimatableCellHeight: AnimatableModifier {
var height: CGFloat = 0

var animatableData: CGFloat {
    get { height }
    set { height = newValue }
}

func body(content: Content) -> some View {
    content.frame(height: height)
}
}

票数 1
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70623529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档