Created
April 9, 2023 11:47
-
-
Save maxgribov/99d6778c33df0557b8ff455d1759395d to your computer and use it in GitHub Desktop.
Custom SwiftUI transitions playground.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import PlaygroundSupport | |
struct ContentView: View { | |
@State private var isPresented = false | |
var body: some View { | |
ZStack { | |
Color.blue | |
.frame(width: 375, height: 800) | |
.zIndex(0) | |
if isPresented { | |
// alert transition example | |
VStack { | |
Text("Alert") | |
.font(.title) | |
Text("Some message here...") | |
} | |
.transition(.alert) | |
.zIndex(1) | |
// modal transition example | |
/* | |
Text("Modal view content") | |
.transition(.sheet(onDismiss: { isPresented = false })) | |
.zIndex(1) | |
*/ | |
} | |
} | |
.onTapGesture { | |
withAnimation(.easeInOut) { | |
isPresented.toggle() | |
} | |
} | |
} | |
} | |
extension AnyTransition { | |
static var alert: AnyTransition { | |
.modifier(active: AlertViewModifier(transitionProgress: 0, isPresenting: false), | |
identity: AlertViewModifier(transitionProgress: 1, isPresenting: true)) | |
} | |
static func sheet(onDismiss: @escaping () -> Void) -> AnyTransition { | |
.modifier(active: SheetViewModifier(transitionProgress: 0, onDismiss: onDismiss), | |
identity: SheetViewModifier(transitionProgress: 1, onDismiss: onDismiss)) | |
} | |
} | |
struct AlertViewModifier: ViewModifier { | |
var transitionProgress: Double | |
var isPresenting: Bool | |
func body(content: Content) -> some View { | |
ZStack { | |
// dimm view | |
Color.clear | |
.background(.regularMaterial) | |
.opacity(transitionProgress) | |
.animation(.easeInOut, value: transitionProgress) | |
.ignoresSafeArea() | |
.zIndex(0) | |
// content | |
content | |
.padding(40) | |
.background( | |
RoundedRectangle(cornerRadius: 30) | |
.foregroundColor(.white) | |
) | |
// SwiftUI don't like scale == 0 | |
.scaleEffect(x: transitionProgress + 0.01, y: transitionProgress + 0.01) | |
.animation(isPresenting ? .interactiveSpring(response: 0.5, dampingFraction: 0.5, blendDuration: 0.25) : .easeOut(duration: 0.3), value: transitionProgress) | |
.zIndex(1) | |
} | |
} | |
} | |
struct SheetViewModifier: ViewModifier { | |
var transitionProgress: Double | |
let onDismiss: () -> Void | |
@GestureState private var translation: CGFloat = 0 | |
func body(content: Content) -> some View { | |
GeometryReader { proxy in | |
ZStack { | |
// dimm view | |
Color.gray | |
.opacity(transitionProgress) | |
.animation(.easeInOut, value: transitionProgress) | |
.ignoresSafeArea() | |
.zIndex(0) | |
SheetView(content: content) | |
.padding(.top, 40) | |
.offset(y: proxy.size.height * (1 - transitionProgress)) | |
.offset(y: max(translation, 0)) | |
.animation(.easeInOut, value: transitionProgress) | |
.animation(.easeOut, value: translation) | |
.gesture(DragGesture() | |
.updating($translation) { value, state, transaction in | |
state = value.translation.height | |
} | |
.onEnded { value in | |
if value.translation.height > proxy.size.height / 3 { | |
onDismiss() | |
} | |
}) | |
.zIndex(1) | |
} | |
} | |
} | |
struct SheetView<Content: View>: View { | |
let content: Content | |
var body: some View { | |
VStack(spacing: 0) { | |
// header | |
RoundedRectangle(cornerRadius: 10) | |
.foregroundColor(.white) | |
.frame(height: 20) | |
Spacer() | |
// content | |
content | |
Spacer() | |
} | |
.background(Color.white) | |
} | |
} | |
} | |
PlaygroundPage.current.setLiveView(ContentView()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment