簡體   English   中英

SwiftUI:在動畫結束時刪除視圖?

[英]SwiftUI: remove view at animation end?

我有以下代碼:

enum AnimationDirection {
    case opening
    case closing
}

struct AnimtionStateMod: AnimatableModifier {
    
    var progress: CGFloat = 0
    private(set) var direction:AnimationDirection = .opening
    
    var animatableData: CGFloat {
        get { progress }
        set {
            direction = progress > newValue ? .opening : .closing
            progress = newValue
            print("progress: \(progress). direction: \(direction)")
        }
    }
    
    func body(content: Content) -> some View {
        content.opacity(1)
    }
    
}



struct PersonDetails: View {
    @State var person:Person
    
    var body: some View {
        ZStack {
            
            Color.yellow
            
            HStack {
                Text("PERSON")
                Spacer()
            }
            
            Text(person.name)
            
            Spacer()
            
        }
        .border(.red)
    }
}


struct Person: Identifiable {
    let id = UUID()
    let name: String
}





struct ContentView: View {

    @State var persons = [Person(name: "A"), Person(name: "B"), Person(name: "C")]
    
    @Namespace private var gridSpace
    @State var selectedPerson:Person?
    @State var expandingPersonDetails:Bool = false
    var matchDetailsToPerson: Bool { !expandingPersonDetails }
    @State var matchSelectionToGridItem = true
    
    let c = GridItem(.adaptive(minimum: 200, maximum: 400), spacing: 20)

    var body: some View {

        ZStack {
            
            ScrollView {
                LazyVGrid(columns: [c]) {
                    ForEach(persons) { person in
                        
                        ZStack {
                            Color.blue
                            Text(person.name)
                                .foregroundColor(.white)
                        }
                        .frame(width: 200, height: 200)
                        .onTapGesture {
                            print(person.name)
                            withAnimation {
                                selectedPerson = person
                            }
                            
                        }
                        .matchedGeometryEffect(id: person.id, in: gridSpace, isSource: true)
                        
                    }
                }
            }
            .zIndex(1)
            
            
            if let person = selectedPerson {
                
                PersonDetails(person: person)
                    .matchedGeometryEffect(id: matchDetailsToPerson ? person.id : UUID() , in: gridSpace, isSource: false)
                    .frame(width: 600, height: 600)
                    .zIndex(2)
                    .onAppear {
                        withAnimation {
                            expandingPersonDetails = true
                        }
                    }
                    .onDisappear {
                        expandingPersonDetails = false
                    }
                    .transition(.identity)
                    .modifier(AnimtionStateMod(progress: expandingPersonDetails ? 1 : 0))
                    .onTapGesture {
                        withAnimation {
//                            selectedPerson = nil
                            expandingPersonDetails = false
                        }
                    }   
            }   
        }
    }

它的行為如下:

在此處輸入圖像描述

我堅持的是:

我希望PersonDetails在網格項目上完成動畫后被刪除。 打開的動畫(從網格到展開PersonDetails )看起來就像我想要的那樣。 關閉動畫(當PersonDetails縮小到相關的網格項目時)看起來就像我想要的那樣。 但是,我希望PersonDetails一旦回到網格項目上就被刪除。

我想我可以在返回網格項目時使用AnimatableModifier跟蹤動畫,然后將selectedPerson觸發為零。 我想我可以使用這種方法,但是,我不確定在這種情況下我是否應該這樣做。

我該怎么辦?

只是添加反饋:

使用asperi的答案我得到了不同的結果。 我也在使用更新版本的 Xcode,這是我現在唯一能想到的,但我想在這里提供反饋以了解上下文:

在此處輸入圖像描述

如果我正確地想象了您的需求(我仍然不確定),則可以以更簡單的方式實現效果 - 通過對選定的人進行動畫處理,並使用疊加匹配的人視圖來保持原點不變。

使用 Xcode 13.4 / iOS 15.5 測試

演示

帶有慢速調試動畫以獲得更好的可見性

演示2

這是主要部分(已修復 iPadOS 更新):

        ForEach(persons) { person in
            ZStack {
                if nil != selectedPerson {
                    PersonView(person: person)
                } else {
                    PersonView(person: person)
                        .matchedGeometryEffect(id: person.id, in: gridSpace)
                        .onTapGesture {
                            if nil == selectedPerson {
                                selectedPerson = person
                            }
                        }}

            }
            .frame(width: 100, height: 100)
        }
    }
}
VStack {   // << to remove fluently
    if let person = selectedPerson {
        PersonDetails(person: person)
            .matchedGeometryEffect(id: person.id, in: gridSpace)
            .transition(.scale(scale: 1))
            .frame(width: 360, height: 360)
            .onTapGesture {
                selectedPerson = nil
            }
    }
}
.animation(.default, value: selectedPerson)

完整的測試模塊在這里

Asperi 的代碼非常簡單有效。 如果您想要一種不同的方法,盡管稍微復雜一點,您可以嘗試為動畫設置持續時間,然后 - 僅在此之后 - 將selectedPerson設置為nil

首先,在ContentView內部為效果持續時間設置一個常量:

    // This is the duration of the animation
    let effectDuration = 0.5

然后,使用基於expandingPersonDetails變量的動畫過渡。 您可以完全刪除AnimatableModifier 在關閉人員詳細信息之前,首先更改變量expandingPersonDetails以將視圖放回其原始位置。 只有在動畫結束后,使用.asyncAfter()才能關閉selectedPerson 這里:

PersonDetails(person: person)

    // Use simple transition
    .transition(.opacity)

    // The duration must match the .asyncAfter method below
    .animation(.easeInOut(duration: effectDuration), value: expandingPersonDetails)

    .matchedGeometryEffect(id: expandingPersonDetails ? UUID() : person.id , in: gridSpace, isSource: false)
    .frame(width: 600, height: 600)
    .zIndex(2)
    .onAppear {
        withAnimation {
            expandingPersonDetails = true
        }
    }
    .onTapGesture {
        withAnimation {
            
            // Change this variable first
            expandingPersonDetails = false
            
            // When the animation is over, un-select the person
            DispatchQueue.main.asyncAfter(deadline: .now() + effectDuration) {
                selectedPerson = nil
            }
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM