I can’t get rid of an animation glitch. Here is my code:
// Bubble.swift
import SwiftUI
struct Bubble: Identifiable {
var id = UUID()
var sender: Sender
var text: String
var value: String?
var inputType: String?
}
enum Sender {
case user
case bot
case system
}
struct BubbleView: View {
@State var bubble: Bubble
@State var showBubble = false
var body: some View {
Section {
if self.showBubble {
switch bubble.sender {
case .user, .bot:
Text(bubble.text)
.padding(10)
.foregroundColor(bubble.sender == Sender.user ? .white : .black)
.background(bubble.sender == Sender.user ? .accentColor : Color(UIColor.systemGray5))
.clipShape(RoundedRectangle(cornerRadius: 20))
.frame(maxWidth: .infinity, alignment: bubble.sender == Sender.user ? .trailing : .leading)
.padding(.vertical, bubble.sender == Sender.user ? 10 : 0)
.transition(.move(edge: .bottom))
case .system:
Text(bubble.text.uppercased())
.padding(10)
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.semibold)
.frame(maxWidth: .infinity)
.transition(.move(edge: .bottom))
}
} else {
Text("")
}
}.onAppear {
withAnimation {
self.showBubble.toggle()
}
}
}
}
#Preview {
VStack(alignment: .leading) {
BubbleView(bubble: Bubble(sender: .bot, text: "It's-a me, Mario!"))
BubbleView(bubble: Bubble(sender: .user, text: "And it's-a me, Luigi!"))
BubbleView(bubble: Bubble(sender: .system, text: "10:30"))
}
}
// Dialog.swift
import SwiftUI
struct DialogView: View {
@Binding var dialog: [Bubble]
func scrollDown(proxy: ScrollViewProxy) {
if let lastID = dialog.last?.id {
withAnimation {
proxy.scrollTo(lastID)
}
}
}
var body: some View {
ScrollViewReader { proxy in
ScrollView {
VStack(alignment: .leading) {
ForEach(dialog) { bubble in
BubbleView(bubble: bubble)
}
.onChange(of: dialog.count, initial: false) { _,_ in
scrollDown(proxy: proxy)
}
}
.frame(maxHeight: .infinity)
.padding()
Spacer()
}
}
}
}
#Preview("Dialog view") {
DialogView(dialog: .constant([
Bubble(sender: .bot, text: "Bubble 1"),
Bubble(sender: .bot, text: "Bubble 2"),
Bubble(sender: .bot, text: "Bubble 3")
]))
}
It is a part of a larger project, but the DialogView animation is buggy: on its first appearance, something strange happens. To be able to see it, you can just create a new SwiftUI iOS project, and paste my code into two new files. Then, go to the Dialog.swift
file and see the preview. Nothing stranges happens on the first time but if you edit the DialogView
body (for instance commenting) you will see the strange animation.
It happens constantly on my bigger project, and I don’t know how to remove it. It is possible to test it on the simulator replacing the ContentView.swift
content by the following:
// ContentView.swift
import SwiftUI
struct ContentView: View {
@State var bubbles: [Bubble] = []
var body: some View {
DialogView(dialog: $bubbles)
Button {
if self.bubbles.count == 0 {
self.bubbles = [
Bubble(sender: .bot, text: "Bubble 1"),
Bubble(sender: .bot, text: "Bubble 2"),
Bubble(sender: .bot, text: "Bubble 3")
]
} else {
self.bubbles = []
}
} label: {
Text("Test")
}
}
}
#Preview {
ContentView()
}
Can someone help me? Thanks!
2
Answers
I adapted your example to accept input from a
TextField
. When run in a simulator, I see that the first message has a strange animation that starts in the middle of the screen. Subsequent animations seem to work fine (these being simple "move" transitions).To fix the animation for the first message, I would suggest some small changes:
The
.frame
settingmaxHeight: .infinity
has no effect inside a verticalScrollView
. However, it would be useful to set a maximum width.The
Spacer
is redundant too and can be removed.Here is the adapted
ContentView
that can be used for testing in a simulator:Using withAnimation for a state change will cause animation for all the views that are updated during the state change.
So the solution is to set animation for the specific views.