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.