Skip to content

Instantly share code, notes, and snippets.

@AdamWhitcroft
Created February 6, 2023 02:09
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save AdamWhitcroft/085bf4e81cb08d9528cfcceb7e0fee7c to your computer and use it in GitHub Desktop.
Save AdamWhitcroft/085bf4e81cb08d9528cfcceb7e0fee7c to your computer and use it in GitHub Desktop.
struct Overlay: View {
@State private var showLive = true
@State private var isAnimationSlowed = false
var body: some View {
VStack {
Spacer()
HStack {
Spacer()
Pill(
showLive: $showLive,
isAnimationSlowed: $isAnimationSlowed
)
}
.padding()
Spacer()
Button {
isAnimationSlowed.toggle()
} label: {
Text(isAnimationSlowed ? "Fast animation" : "Slow animation")
.font(.system(.body, design: .rounded).weight(.medium))
}
}
}
}
private struct Pill: View {
@Binding var showLive: Bool
@Binding var isAnimationSlowed: Bool
let debugAnimation: Animation = .easeOut(duration: 2)
let animation: Animation = .interpolatingSpring(mass: 0.5, stiffness: 300, damping: 20)
var body: some View {
ZStack(alignment: .leading) {
HStack {
//
// circle shape
//
Circle()
.fill(.clear)
.frame(width: 30, height: 30)
.overlay {
ZStack {
ViewerCountIcon()
.opacity(showLive ? 1 : 0)
ViewerAvatar()
.opacity(showLive ? 0 : 1)
}
}
.overlay(alignment: .leading) {
//
// these are the visible labels
// we're overlaying on the circle
// because the circle is the only
// thing that's actually changing position
//
ZStack(alignment: .leading) {
TextViewOne()
.opacity(showLive ? 1 : 0)
TextViewTwo()
.opacity(showLive ? 0 : 1)
}
.frame(width: 200, alignment: .leading)
.padding(.leading, 38)
}
//
// we're using these views to give us the correct
// widths for our content in each state, even
// though we're not actually seeing them
//
if showLive {
TextViewOneSizer()
} else {
TextViewTwoSizer()
}
}
.onTapGesture {
withAnimation(isAnimationSlowed ? debugAnimation : animation) {
showLive.toggle()
}
}
}
.padding(8)
.padding(.trailing, 4)
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 24))
}
}
//
// make sure `TextViewOne` and `TextViewOneSizer`
// have the same content, can be generated dynamically
//
private struct TextViewOne: View {
var body: some View {
Text("32")
.font(.system(.body, design: .rounded).weight(.medium))
}
}
private struct TextViewOneSizer: View {
var body: some View {
Text("32")
.font(.system(.body, design: .rounded).weight(.medium))
.foregroundColor(.clear)
}
}
//
// make sure `TextViewTwo` and `TextViewTwoSizer`
// have the same content, can be generated dynamically
//
private struct TextViewTwo: View {
var body: some View {
Group {
Text("Mike is in this ")
.font(.system(.body, design: .rounded).weight(.medium))
+
Text("live!")
.font(.system(.body, design: .rounded).weight(.medium))
.foregroundColor(.green)
}
}
}
private struct TextViewTwoSizer: View {
var body: some View {
Group {
Text("Mike is in this live!")
.font(.system(.body, design: .rounded).weight(.medium))
.foregroundColor(.clear)
}
}
}
private struct ViewerCountIcon: View {
var body: some View {
Circle()
.fill(.red)
.frame(width: 30, height: 30)
.overlay {
Image(systemName: "lines.measurement.horizontal")
.font(.system(.body, design: .rounded).weight(.medium))
.foregroundColor(.white)
}
}
}
private struct ViewerAvatar: View {
var body: some View {
Circle()
.fill(.yellow.opacity(0.5))
.frame(width: 30, height: 30)
.overlay {
Text("😀")
}
}
}
struct Overlay_Previews: PreviewProvider {
static var previews: some View {
Overlay()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment