SwiftUI库存几何效果和标签栏动画



嘿,伙计们,我的代码有一些问题。我只是在SwiftUI中尝试了一点matchedGeometryEffect,它工作得很好。但是现在我遇到了一些问题:

  1. 我不能在DetailView被驳回时停用标签栏,因为视图会跳起来一点。

  2. 视图转换有时有bug,控制台(不断地)给我输出

Multiple inserted views in matched geometry group Pair<String, ID>(first: "bg", second: SwiftUI.Namespace.ID(id: 415)) have `isSource: true`, results are undefined.

是否有更好的方法来动画这个平滑和禁用标签栏?

下面是我的代码:
struct FullscreenView: View {
@Namespace var animationNamespace

@State var shouldShowFullsceen = false
@State var shouldShowDetails = false

var body: some View {
Input()
.padding()
.onTapGesture {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
)) {
shouldShowFullsceen = true
}
}
.overlay {
if shouldShowFullsceen {
Output()
.onTapGesture {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
)) {
shouldShowFullsceen = false
shouldShowDetails = false
}
}
}
}
}
}
extension FullscreenView {
@ViewBuilder
func Input() -> some View {
Content()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(BackgroundView())
}

@ViewBuilder
func Output() -> some View {
DetailedContent()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(FullscreenBackground())
}
}
extension FullscreenView {
@ViewBuilder
func Content() -> some View {
Image("dog")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxHeight: 300)
.matchedGeometryEffect(id: "content", in: animationNamespace)
}
}
extension FullscreenView {
@ViewBuilder
func DetailedContent() -> some View {
VStack {
Content()

ScrollView(.vertical) {
Text(dummyText)
.padding()
.opacity(shouldShowDetails ? 1 : 0)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
}
.transition(.identity)
.onAppear {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
).delay(0.1)) {
shouldShowDetails = true
}
}
}
}
extension FullscreenView {
@ViewBuilder
func BackgroundView() -> some View {
Color.orange
.clipShape(RoundedRectangle(cornerRadius: 15))
.matchedGeometryEffect(id: "bg", in: animationNamespace)
}
}
extension FullscreenView {
@ViewBuilder
func FullscreenBackground() -> some View {
BackgroundView()
.ignoresSafeArea()
}
}
struct FullscreenView_Previews: PreviewProvider {
static var previews: some View {
FullscreenView()
}
}

关于动画和控制台警告:

  1. 不要覆盖输出视图。用if ... else显示输入视图或输出视图,然后.matchedGeometryEffect可以进行转换。

  2. 您应该使用.matchedGeometryEffectisSource:指定为真,为图像和背景。

  3. 去掉.transition(.identity).

下面是带注释的完整代码:

struct FullscreenView: View {
@Namespace var animationNamespace

@State var shouldShowFullsceen = false
@State var shouldShowDetails = false

var body: some View {

if shouldShowFullsceen == false { // show only one matched view at a time
Input()
.padding()
.onTapGesture {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
)) {
shouldShowFullsceen = true
}
}
} else { // show only one matched view at a time
Output()
.onTapGesture {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
)) {
shouldShowFullsceen = false
shouldShowDetails = false
}
}
}
}

func Input() -> some View {
Content()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(BackgroundView())
}

func Output() -> some View {
DetailedContent()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(FullscreenBackground())
}


func Content() -> some View {
Image(systemName: "tortoise")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxHeight: 300)
.padding()
.matchedGeometryEffect(id: "content", in: animationNamespace, isSource: true) // add isSource
}


func DetailedContent() -> some View {
VStack {
Content()

ScrollView(.vertical) {
Text("dummyText")
.padding()
.opacity(shouldShowDetails ? 1 : 0)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
}
//        .transition(.identity) // take this out
.onAppear {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
).delay(0.1)) {
shouldShowDetails = true
}
}
}

func BackgroundView() -> some View {
Color.orange
.clipShape(RoundedRectangle(cornerRadius: 15))
.matchedGeometryEffect(id: "bg", in: animationNamespace, isSource: true) // add isSource
}

func FullscreenBackground() -> some View {
BackgroundView()
.ignoresSafeArea()
}
}

最新更新