Skip to content

Commit ff62fb3

Browse files
Improved the popout animation of the reactions overlay
1 parent 461531e commit ff62fb3

File tree

6 files changed

+34
-14
lines changed

6 files changed

+34
-14
lines changed

Sources/StreamChatSwiftUI/ChatChannel/Reactions/ReactionsOverlayView.swift

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
1111
@StateObject var viewModel: ReactionsOverlayViewModel
1212

1313
@State private var popIn = false
14+
@State private var willPopOut = false
1415

1516
var factory: Factory
1617
var channel: ChatChannel
@@ -54,11 +55,15 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
5455
public var body: some View {
5556
ZStack(alignment: .topLeading) {
5657
Image(uiImage: currentSnapshot)
57-
.overlay(Color.black.opacity(0.1))
58-
.blur(radius: 4)
58+
.overlay(Color.black.opacity(!popIn ? 0 : 0.1))
59+
.blur(radius: !popIn ? 0 : 4)
5960
.transition(.opacity)
6061
.onTapGesture {
6162
withAnimation {
63+
willPopOut = true
64+
popIn = false
65+
}
66+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
6267
onBackgroundTap()
6368
}
6469
}
@@ -76,6 +81,7 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
7681
x: paddingValue / 2,
7782
y: originY + messageContainerHeight - paddingValue + 2
7883
)
84+
.opacity(willPopOut ? 0 : 1)
7985
}
8086

8187
GeometryReader { reader in
@@ -101,8 +107,8 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
101107
)
102108
}
103109
}
104-
.scaleEffect(popIn ? 1 : 0.95)
105-
.animation(popInAnimation, value: popIn)
110+
.scaleEffect(popIn || willPopOut ? 1 : 0.95)
111+
.animation(willPopOut ? .easeInOut : popInAnimation, value: popIn)
106112
.offset(
107113
x: messageDisplayInfo.frame.origin.x - diffWidth(proxy: reader)
108114
)
@@ -117,7 +123,8 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
117123
}
118124
)
119125
.scaleEffect(popIn ? 1 : 0)
120-
.animation(popInAnimation, value: popIn)
126+
.opacity(willPopOut ? 0 : 1)
127+
.animation(willPopOut ? .easeInOut : popInAnimation, value: popIn)
121128
.offset(
122129
x: messageDisplayInfo.frame.origin.x - diffWidth(proxy: reader),
123130
y: popIn ? -24 : -messageContainerHeight / 2
@@ -142,13 +149,13 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
142149
)
143150
.frame(width: messageActionsWidth)
144151
.offset(
145-
x: popIn ? messageActionsOriginX(availableWidth: reader.size.width) :
146-
(messageDisplayInfo.message.isSentByCurrentUser ? messageActionsWidth : 0),
152+
x: messageActionsOffsetX(reader: reader),
147153
y: popIn ? 0 : -messageActionsSize / 2
148154
)
149155
.padding(.top, paddingValue)
150-
.scaleEffect(popIn ? 1 : 0)
151-
.animation(popInAnimation, value: popIn)
156+
.opacity(willPopOut ? 0 : 1)
157+
.scaleEffect(popIn ? 1 : (willPopOut ? 0.4 : 0))
158+
.animation(willPopOut ? .easeInOut : popInAnimation, value: popIn)
152159
} else {
153160
factory.makeReactionsUsersView(
154161
message: viewModel.message,
@@ -161,10 +168,11 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
161168
.padding(.top, messageDisplayInfo.message.isSentByCurrentUser ? paddingValue : 2 * paddingValue)
162169
.padding(.trailing, paddingValue)
163170
.scaleEffect(popIn ? 1 : 0)
164-
.animation(popInAnimation, value: popIn)
171+
.opacity(willPopOut ? 0 : 1)
172+
.animation(willPopOut ? .easeInOut : popInAnimation, value: popIn)
165173
}
166174
}
167-
.offset(y: originY)
175+
.offset(y: !popIn ? messageDisplayInfo.frame.origin.y : originY)
168176
}
169177
}
170178
.edgesIgnoringSafeArea(.all)
@@ -173,6 +181,18 @@ public struct ReactionsOverlayView<Factory: ViewFactory>: View {
173181
}
174182
}
175183

184+
private func messageActionsOffsetX(reader: GeometryProxy) -> CGFloat {
185+
let originX = messageActionsOriginX(availableWidth: reader.size.width)
186+
let sentByCurrentUser = messageDisplayInfo.message.isSentByCurrentUser
187+
if popIn {
188+
return originX
189+
} else if willPopOut {
190+
return messageDisplayInfo.frame.origin.x - diffWidth(proxy: reader)
191+
} else {
192+
return sentByCurrentUser ? messageActionsWidth : 0
193+
}
194+
}
195+
176196
private var messageContainerHeight: CGFloat {
177197
let screenHeight = UIScreen.main.bounds.size.height
178198
let maxAllowed = screenHeight / 2

StreamChatSwiftUITests/Tests/ChatChannel/ReactionsOverlayView_Tests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ReactionsOverlayView_Tests: StreamChatTestCase {
1919

2020
private let messageDisplayInfo = MessageDisplayInfo(
2121
message: .mock(id: .unique, cid: .unique, text: "test", author: .mock(id: .unique)),
22-
frame: CGRect(x: 44, y: 20, width: 80, height: 50),
22+
frame: CGRect(x: 44, y: 200, width: 80, height: 50),
2323
contentWidth: 200,
2424
isFirst: true
2525
)
@@ -87,7 +87,7 @@ class ReactionsOverlayView_Tests: StreamChatTestCase {
8787
)
8888
let messageDisplayInfo = MessageDisplayInfo(
8989
message: message,
90-
frame: CGRect(x: 44, y: 20, width: 80, height: 50),
90+
frame: CGRect(x: 44, y: 200, width: 80, height: 50),
9191
contentWidth: 200,
9292
isFirst: true,
9393
showsMessageActions: false
@@ -126,7 +126,7 @@ class ReactionsOverlayView_Tests: StreamChatTestCase {
126126
)
127127
let messageDisplayInfo = MessageDisplayInfo(
128128
message: testMessage,
129-
frame: CGRect(x: 44, y: 20, width: defaultScreenSize.width - 60, height: defaultScreenSize.height * 2),
129+
frame: CGRect(x: 44, y: 105, width: defaultScreenSize.width - 60, height: defaultScreenSize.height * 2),
130130
contentWidth: 200,
131131
isFirst: true
132132
)
Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)