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!
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.
var body: some View {
Section {
if self.showBubble {
Group {
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)
case .system:
Text(bubble.text.uppercased())
.padding(10)
.foregroundColor(.secondary)
.font(.caption)
.fontWeight(.semibold)
.frame(maxWidth: .infinity)
}
}
.transition(.move(edge: .bottom).combined(with: .opacity))
.transaction { transaction in
transaction.animation = .default
}
}
else {
Text("")
}
}
.onAppear {
self.showBubble.toggle()
}
}