AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79120166
Accepted
CalebK
CalebK
Asked: 2024-10-24 09:30:40 +0800 CST2024-10-24 09:30:40 +0800 CST 2024-10-24 09:30:40 +0800 CST

Colocar visualizações dentro de uma lista impede que matchedGeometryEffect anime a geometria correspondente entre os estados

  • 772

Tenho uma animação bem simples que estou tentando fazer, onde o item selecionado em uma lista recebe um plano de fundo aplicado. O objetivo é que a caixa cinza atrás do item selecionado se transforme da posição anterior para a próxima suavemente.

Eu consegui isso usando matchedGeometryEffectpara que o SwiftUI pudesse combinar as visualizações de fundo entre os dois estados, embora tecnicamente eles tenham um caminho de ID diferente. No entanto, esse esforço é frustrado porList

Aqui está o projeto de exemplo. Tudo o que você precisa fazer para quebrar a animação é cercar os botões em uma List.

Por que List quebra essa animação? Existe alguma maneira de contornar isso?

struct AnimationButtonStyle: ButtonStyle {
    
    var isCurrent: Bool
    var animationNamespace: Namespace.ID
    
    var backgroundView: some View {
        Color.gray
        .cornerRadius(8)
        .matchedGeometryEffect(id: "Shape", in: animationNamespace)
    }
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .background(
                isCurrent ? backgroundView : nil
            )
            .opacity(configuration.isPressed ? 0.5 : 1.0)
    }
}

struct ContentView: View {
    enum cases: String, CaseIterable {
        case foo = "Foo"
        case bar = "Barrrrrr"
        case bat = "Battttttttttttttt"
    }
    
    @Namespace private var animationNamespace
    @State var animatedCurrentCase: cases = .foo
    @State var currentCase: cases = .foo
    @State var isAnimating: Bool = false
    
    var body: some View {
        VStack {
            // Without the list this animation will work
            List {
                Section {
                    VStack(alignment: .leading, spacing: 0) {
                        ForEach(Array(cases.allCases.enumerated()), id: \.offset) { index, theCase in
                            var isCurrent: Bool { theCase == animatedCurrentCase }
                            Button {
                                isAnimating = true
                                Task { @MainActor in
                                    animatedCurrentCase = theCase
                                    try? await Task.sleep(nanoseconds: 200_000_000)
                                    currentCase = theCase
                                    isAnimating = false
                                }
                            } label: {
                                Label {
                                    Text(theCase.rawValue)
                                } icon: {
                                    VStack(alignment: .leading) {
                                        Text("\(index)")
                                    }
                                    .frame(width: isCurrent ? 16 + 4 : 16)
                                }
                            }
                            .disabled(isAnimating)
                            .buttonStyle(AnimationButtonStyle(isCurrent: isCurrent, animationNamespace: animationNamespace))
                            .animation(.smooth, value: animatedCurrentCase)
                        }
                    }
                }
            }
            Spacer().frame(height: 10)
            Text("Selected: \(currentCase.rawValue)")
                .font(.title)
        }
        .padding()
    }
}

Aplicativo de amostra

animation
  • 2 2 respostas
  • 37 Views

2 respostas

  • Voted
  1. Sweeper
    2024-10-24T15:48:26+08:002024-10-24T15:48:26+08:00

    Quando extraí tudo VStackem um View, a animação simplesmente funciona. Não consigo explicar exatamente por que isso funciona, mas parece que tem algo a ver com como o SwiftUI atualiza as visualizações em um List.

    Em qualquer caso, é quase sempre bom dividir suas opiniões em partes menores.

    Aqui eu extraí um ButtonsView, e usei isso na lista. Eu movi 2 dos 3 estados dentro de ButtonsView, assim como o @Namespace, mas você pode alterá-los totalmente para @Bindings e fazer com que a visualização pai os passe.

    struct AnimationButtonStyle: ButtonStyle {
        
        var isCurrent: Bool
        var animationNamespace: Namespace.ID
        
        var backgroundView: some View {
            Color.gray
                .clipShape(RoundedRectangle(cornerRadius: 8))
                .matchedGeometryEffect(id: "Shape", in: animationNamespace)
        }
        
        func makeBody(configuration: Configuration) -> some View {
            configuration.label
                .background {
                    isCurrent ? backgroundView : nil
                }
                .opacity(configuration.isPressed ? 0.5 : 1.0)
        }
    }
    
    struct ButtonsView: View {
        enum cases: String, CaseIterable {
            case foo = "Foo"
            case bar = "Barrrrrr"
            case bat = "Battttttttttttttt"
        }
        
        @Namespace private var animationNamespace
        @State private var animatedCurrentCase: cases = .foo
        @Binding var currentCase: cases
        @State var isAnimating = false
        
        var body: some View {
            VStack(alignment: .leading, spacing: 0) {
                ForEach(Array(cases.allCases.enumerated()), id: \.offset) { index, theCase in
                    var isCurrent: Bool { theCase == animatedCurrentCase }
                    Button {
                        isAnimating = true
                        Task { @MainActor in
                            animatedCurrentCase = theCase
                            try? await Task.sleep(nanoseconds: 200_000_000)
                            currentCase = theCase
                            isAnimating = false
                        }
                    } label: {
                        Label {
                            Text(theCase.rawValue)
                        } icon: {
                            VStack(alignment: .leading) {
                                Text("\(index)")
                            }
                            .frame(width: isCurrent ? 16 + 4 : 16)
                        }
                    }
                    .disabled(isAnimating)
                    .buttonStyle(AnimationButtonStyle(isCurrent: isCurrent, animationNamespace: animationNamespace))
                    .animation(.smooth, value: animatedCurrentCase)
                }
            }
            .onAppear {
                animatedCurrentCase = currentCase
            }
        }
    }
    
    struct ContentView: View {
        @State var currentCase: ButtonsView.cases = .bar
        
        var body: some View {
            VStack {
                List {
                    Section {
                        ButtonsView(currentCase: $currentCase)
                    }
                }
                Spacer().frame(height: 10)
                Text("Selected: \(currentCase.rawValue)")
                    .font(.title)
            }
            .padding()
        }
    }
    
    • 1
  2. Best Answer
    Benzy Neez
    2024-10-24T16:01:38+08:002024-10-24T16:01:38+08:00

    Eu sugeriria remover todo o código extra para animar o caso selecionado, porque é desnecessário. Especificamente:

    • Remova as variáveis ​​de estado animatedCurrentCasee isAnimating.
    • Remova o Task.
    • Remova o .disabledmodificador.
    • Remova o .animationmodificador.

    Se você então withAnimationexecutar a atualização dentro do fechamento do botão, funcionará.

    Outras sugestões:

    • Use letonde puder, em vez de var. Em particular, as propriedades em AnimationButtonStylecan use let.

    • .cornerRadiusestá obsoleto, use .clipShapecom um RoundedRectangleem vez disso, ou apenas coloque um RoundedRectangleem segundo plano.

    • O .backgroundmodificador que você estava usando também está obsoleto. Sugira uma refatoração usando um trailing closure e if-statement.

    Aqui está o exemplo atualizado:

    struct AnimationButtonStyle: ButtonStyle {
        let isCurrent: Bool
        let animationNamespace: Namespace.ID
    
        var backgroundView: some View {
            RoundedRectangle(cornerRadius: 8)
                .fill(.gray)
                .matchedGeometryEffect(id: "Shape", in: animationNamespace)
        }
    
        func makeBody(configuration: Configuration) -> some View {
            configuration.label
                .background {
                    if isCurrent {
                        backgroundView
                    }
                }
                .opacity(configuration.isPressed ? 0.5 : 1.0)
        }
    }
    
    struct ContentView: View {
        enum cases: String, CaseIterable {
            case foo = "Foo"
            case bar = "Barrrrrr"
            case bat = "Battttttttttttttt"
        }
    
        @Namespace private var animationNamespace
        @State var currentCase: cases = .foo
    
        var body: some View {
            VStack {
                List {
                    Section {
                        VStack(alignment: .leading, spacing: 0) {
                            ForEach(Array(cases.allCases.enumerated()), id: \.offset) { index, theCase in
                                let isCurrent = theCase == currentCase
                                Button {
                                    withAnimation(.smooth) {
                                        currentCase = theCase
                                    }
                                } label: {
                                    Label {
                                        Text(theCase.rawValue)
                                    } icon: {
                                        VStack(alignment: .leading) {
                                            Text("\(index)")
                                        }
                                        .frame(width: isCurrent ? 16 + 4 : 16)
                                    }
                                }
                                .buttonStyle(
                                    AnimationButtonStyle(
                                        isCurrent: isCurrent,
                                        animationNamespace: animationNamespace
                                    )
                                )
                            }
                        }
                    }
                }
                Spacer().frame(height: 10)
                Text("Selected: \(currentCase.rawValue)")
                    .font(.title)
            }
            .padding()
        }
    }
    

    Animação

    • 1

relate perguntas

  • Problema de animação do SwiftUI (após atribuição do texto)

  • Como você anima a inserção de um item em uma lista no Android?

  • Como animar um menu de círculo para oval assim?

  • Animação quebrada dentro do SwiftUI TabView

  • Por que minha animação não funciona para uma bússola feita em SwiftUI

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve