struct SocialMenuView: View {
    @State private var pressing: Bool = false
    @State private var pressed: Bool = true

    private var spring: Animation = Animation
        .spring(response: 0.55, dampingFraction: 0.45, blendDuration: 0)
        .speed(2)
    
    var body: some View {
        VStack {
            ZStack {
                Circle()
                    .fill(Color.black.opacity(0.05))
                    .frame(width: 80, height: 80)
                    .opacity(self.pressed || !self.pressing ? 0 : 1)
                    .scaleEffect(self.pressed ? 20 : (self.pressing ? 2 : 0.9))
                    .animation(.easeIn)

                HStack {
                    Image(systemName: "square.and.arrow.up")
                        .font(Font.title.weight(.light))
                        .foregroundColor(Color.white)
                }
                .frame(width: 80, height: 80)
                .background(LinearGradient(
                                gradient: Gradient(colors: [Color("SocialGradientTop"), Color("SocialGradientBottom")]),
                                startPoint: .topLeading,
                                endPoint: .bottomTrailing
                            )
                )
                .cornerRadius(100)
                .shadow(color: Color("SocialGradientBottom").opacity(0.2), radius: 10, x: 0, y: 10)
                .scaleEffect(self.pressed || self.pressing ? 0.5 : 1)
                .opacity(self.pressed ? 0 : 1)
                .animation(.easeIn)
                .onLongPressGesture(
                    minimumDuration: 0.3,
                    pressing: { isPressing in
                        self.pressing.toggle()
                    },
                    perform: {
                        withAnimation(self.spring) {
                            self.pressed.toggle()
                        }
                        self.pressing = false
                    }
                )
            
                SocialMenuItemsContainerView(pressed: self.$pressed)
                
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color(UIColor.systemGray5))
        .edgesIgnoringSafeArea(.all)
    }
}

struct SocialMenuItemsContainerView: View {
    @Binding var pressed: Bool
    @Namespace private var animation
    
    var body: some View {
        HStack(spacing: 15) {
            if self.pressed {
                SocialMenuItemsView(pressed: self.$pressed, animation: self.animation)
            } else {
                ZStack {
                    SocialMenuItemsView(pressed: self.$pressed, animation: self.animation)
                }
            }
        }
        .padding(.horizontal, 20)
        .padding(.vertical, 20)
        .background(Color.white.opacity(0.3))
        .cornerRadius(100)
        .opacity(self.pressed ? 1 : 0)
    }
}

struct SocialMenuItemsView: View {
    var pressed: Binding<Bool>
    var animation: Namespace.ID
    
    var body: some View {
        SocialMenuItemView(id: "b1", icon: Image("dribbble").resizable(), color: Color("SocialMenuDrbl"), animation: self.animation)
        SocialMenuItemView(id: "b2", icon: Image("instagram").resizable(), color: Color("SocialMenuInsta"), animation: self.animation)
        SocialMenuItemView(id: "b3", icon: Image(systemName: "xmark"), color: Color.black, animation: self.animation)
            .onTapGesture {
                withAnimation(.spring()) {
                    pressed.wrappedValue.toggle()
                }
            }
        SocialMenuItemView(id: "b4", icon: Image("facebook").resizable(), color: Color("SocialMenuFb"), animation: self.animation)
        SocialMenuItemView(id: "b5", icon: Image("twitter").resizable(), color: Color("SocialMenuTw"), animation: self.animation)
    }
}

struct SocialMenuItemView: View {
    var id: String
    var icon: Image
    var color: Color
    var animation: Namespace.ID
    
    var body: some View {
        HStack {
            self.icon.foregroundColor(Color.white)
                .frame(width: 16, height: 16)
        }.frame(width: 50, height: 50)
        .background(self.color)
        .cornerRadius(100)
        .matchedGeometryEffect(id: self.id, in: animation)
        .shadow(color: self.color.opacity(0.2), radius: 8, x: 0, y: 10)
    }
}

Reveal social sharing menu

by Kane
Experimenting with MatchedGeometryEffect to animate buttons appearing and moving in a way that would be otherwise problematic to implement with normal Animation.