Skip to content

Instantly share code, notes, and snippets.

@dduan

dduan/SpeakBubble.swift

Last active Jun 20, 2020
Embed
What would you like to do?
Twitter's voice tweet UI has an interesting animation on iOS. This is an attempt to recreate that animation with SwiftUI. Looks like this https://youtu.be/I6XZzIgWYAQ
import SwiftUI
struct ChaoticPhoto: View {
let image: Image
let radius: CGFloat
@Binding var activated: Bool
@State var scale: CGFloat = 1
var body: some View {
image
.resizable()
.frame(width: radius, height: radius)
.mask(Circle())
.scaleEffect(activated ? scale : 1)
.onAppear {
Timer.scheduledTimer(
withTimeInterval: 0.05,
repeats: true
) { _ in
self.scale = CGFloat.random(in: 1...1.1)
}
}
}
}
struct ChaoticCircle: View {
let radius: CGFloat
@Binding var activated: Bool
@State var scale: CGFloat = 1
@State var offSet: CGFloat = 0
@State var xDirection: CGFloat = 0
@State var yDirection: CGFloat = 0
var body: some View {
Circle()
.fill(Color.white.opacity(0.15))
.frame(width: radius, height: radius)
.offset(
x: activated ? xDirection * offSet : 0,
y: activated ? yDirection * offSet : 0
)
.scaleEffect(activated ? scale : 1)
.onAppear(perform: {
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
withAnimation(Animation.easeInOut(duration: 0.1)) {
self.xDirection = [CGFloat(0.0), CGFloat(1), CGFloat(-1)].randomElement() ?? CGFloat(0)
self.yDirection = [CGFloat(0.0), CGFloat(1), CGFloat(-1)].randomElement() ?? CGFloat(0)
self.offSet = CGFloat.random(in: 5...8)
self.scale = CGFloat.random(in: CGFloat(1.2)...CGFloat(1.4))
}
}
})
}
}
struct ActivateButton: View {
@Binding var activated: Bool
var body: some View {
Button(action: {
self.activated.toggle()
}) {
Text(activated ? "Stop" : "Speak")
.frame(width: 200, height: 44)
.foregroundColor(Color.white)
.background(
RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color("main"))
)
}
.shadow(color: Color.white, radius: 5)
}
}
struct ContentView: View {
@State var activated = false
var body: some View {
VStack {
Spacer()
ZStack {
ChaoticCircle(radius: 100, activated: self.$activated)
ChaoticCircle(radius: 100, activated: self.$activated)
ChaoticCircle(radius: 100, activated: self.$activated)
ChaoticPhoto(image: Image("profile photo"), radius: 98, activated: self.$activated)
}
Spacer()
ActivateButton(activated: $activated)
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color("main").saturation(0.6))
.edgesIgnoringSafeArea(.all)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.