SwiftUI содержит геометрические эффекты и анимацию tabBar

Привет, ребята, у меня есть некоторые проблемы с моим кодом. Я только что немного поэкспериментировал с matchedGeometryEffect в SwiftUI, и он отлично работает. Но теперь я столкнулся с некоторыми проблемами:

  1. Я не могу просто деактивировать tabBar, когда DetailView закрывается, потому что представление немного подпрыгивает.

  2. Переход View иногда глючит, и консоль выдает мне (постоянно) вывод

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

Есть ли лучший способ плавно анимировать это и отключить tabBar?

Вот мой код:

      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 ответ

Относительно анимации и предупреждения консоли:

  1. Не накладывайте вид вывода. Показать входной или выходной вид с помощьюif ... else, то можно сделать переход.

  2. Вы должны использовать.matchedGeometryEffectсisSource:указано значение true как для изображения, так и для фона.

  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()
    }
}

Другие вопросы по тегам