Feel free to use this in your Projects :D
If you like what i do feel free to support me :)
Feel free to use this in your Projects :D
If you like what i do feel free to support me :)
| // | |
| // CoverFlip.swift | |
| // CoverFlip | |
| // | |
| // Created by Tim on 26.02.26. | |
| // | |
| import SwiftUI | |
| struct CoverFlipTransition: AnimatableModifier { | |
| var progress: Double | |
| var isInsertion: Bool | |
| var blurStrength: CGFloat = 3.5 | |
| var animatableData: Double { | |
| get { progress } | |
| set { progress = newValue } | |
| } | |
| func body(content: Content) -> some View { | |
| let angle = isInsertion ? (180 - 180 * progress) : (0 - 180 * progress) | |
| let blurAmount = blurForAngle(angle) | |
| return content | |
| .rotation3DEffect( | |
| .degrees(angle), | |
| axis: (x: 0, y: 1, z: 0), | |
| perspective: 0.7 | |
| ) | |
| .blur(radius: blurAmount) | |
| .opacity(abs(angle) > 90 ? 0 : 1) | |
| } | |
| private func blurForAngle(_ angle: Double) -> CGFloat { | |
| let normalized = min(abs(angle) / 90, 1) | |
| return CGFloat(normalized * blurStrength) | |
| } | |
| } | |
| extension AnyTransition { | |
| static func coverFlip(blurStrength: CGFloat = 3.5) -> AnyTransition { | |
| .asymmetric( | |
| insertion: .modifier( | |
| active: CoverFlipTransition(progress: 0, isInsertion: true, blurStrength: blurStrength), | |
| identity: CoverFlipTransition(progress: 1, isInsertion: true, blurStrength: blurStrength) | |
| ), | |
| removal: .modifier( | |
| active: CoverFlipTransition(progress: 1, isInsertion: false, blurStrength: blurStrength), | |
| identity: CoverFlipTransition(progress: 0, isInsertion: false, blurStrength: blurStrength) | |
| ) | |
| ) | |
| } | |
| } | |
| #Preview { | |
| Showcase() | |
| } | |
| struct Showcase: View { | |
| @State var yes = false | |
| @State var duration: CGFloat = 0.75 | |
| @State var blurStrength: CGFloat = 3.5 | |
| let cornerRadius: CGFloat = 25 | |
| let frame = CGSize(width: 350, height: 350) | |
| var body: some View { | |
| Spacer() | |
| ZStack { | |
| if yes { | |
| Rectangle() | |
| .foregroundStyle(.red) | |
| .cornerRadius(cornerRadius) | |
| .frame(width: frame.width, height: frame.height) | |
| .transition(.coverFlip(blurStrength: blurStrength)) | |
| } else { | |
| Rectangle() | |
| .foregroundStyle(.blue) | |
| .cornerRadius(cornerRadius) | |
| .frame(width: frame.width, height: frame.height) | |
| .transition(.coverFlip(blurStrength: blurStrength)) | |
| } | |
| } | |
| .animation(.easeInOut(duration: duration), value: yes) | |
| Spacer() | |
| VStack { | |
| Toggle("Slow", isOn: Binding(get: { | |
| duration != 0.75 | |
| }, set: { value in | |
| if value { | |
| duration = 7.5 | |
| } else { | |
| duration = 0.75 | |
| } | |
| })) | |
| Slider(value: $blurStrength, in: 0...10) { | |
| Text("Blur Strength") | |
| } | |
| if #available(iOS 26.0, *) { | |
| Button(action: { | |
| yes.toggle() | |
| }) { | |
| HStack { | |
| Spacer() | |
| Text("Toggle") | |
| .bold() | |
| Spacer() | |
| } | |
| .padding(10) | |
| } | |
| .buttonStyle(.glassProminent) | |
| } else { | |
| Button(action: { | |
| yes.toggle() | |
| }) { | |
| HStack { | |
| Spacer() | |
| Text("Toggle") | |
| .bold() | |
| Spacer() | |
| } | |
| .padding(10) | |
| } | |
| .buttonStyle(.borderedProminent) | |
| } | |
| } | |
| .padding() | |
| } | |
| } |