swiftswiftuimobilewatchkitwatchos

How can I create an animation like WhatsApp archive button spawn animation with SwiftUI on watchOS?


List {
    //I Want This Button To Appear When User Over Scroll To Top
    Button(action: { ... }, label: {
        Text("Archive")
    })
    
    ForEach(0..<5) { item in
        ChatButton()
    }
    .listRowInsets(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
    .listRowPlatterColor(.clear)
    
    Rectangle()
        .frame(width: Sizes.width - 8, height: Sizes.h2)
        .foregroundColor(Color("Background"))
        .listRowInsets(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
        .listRowPlatterColor(.clear)
    
    CenteredTextButton(
        text: "Durumlar", textColor: Color("White"),
        isLink: true, destinationKey: "statuses", buttonAction: {}
    )
    .listRowInsets(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
    .listRowPlatterColor(.clear)
    
}

In the above code, I want the Button that says Archive to appear when user scrolls to top when user already at the top (ex: on negative offset y)

image description


Solution

  • I have found a solution like this, but I don't know it's the best way to do it.

    struct ChatsView: View {
        @State private var showArchived = false
        @State private var scrollAmount: CGPoint = CGPoint(x: 0, y: 0)
        
        var body: some View {
            NavigationView {
                ScrollView {
                    VStack {
                        // show with easeIn only if showArchived == true
                        if showArchived {
                            CenteredIconButton(
                                icon: "ArchiveIcon", text: "Archive",
                                textColor: Color("White"), backgroundColor: Color("Background")
                            )
                            .transition(.move(edge: .top))
                            .animation(.easeIn)
                        }
                        
                        ForEach(0..<5) { item in
                            ChatButton()
                        }
                        .listRowInsets(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
                        .listRowPlatterColor(.clear)
                        
                        ...
                        ...
                    }
                    // observer y offset of VStack compare to ScrollView
                    .background(GeometryReader { geometry in
                        Color.clear
                            .preference(key: ScrollOffsetPreferenceKey.self, value: geometry.frame(in: .named("scroll")).origin)
                    })
                    // set showArchived variable
                    .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
                        scrollAmount = value
                        if value.y > 50 {
                            showArchived = true
                        } else if value.y < 0 {
                            showArchived = false
                        }
                    }
                }
                .coordinateSpace(name: "scroll")
                .navigationTitle("Sohbet")
            }
        }
    }