Created
July 11, 2022 22:26
-
-
Save ryanlintott/b1ec2e15b4b7e59d4bfd2ad149c1d82e to your computer and use it in GitHub Desktop.
A two sided view that can be flipped with a drag gesture.
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
| // | |
| // TwoSidedView.swift | |
| // FrameUpExample | |
| // | |
| // Created by Ryan Lintott on 2022-07-11. | |
| // | |
| import SwiftUI | |
| struct TwoSidedView<UpContent: View, DownContent: View>: View { | |
| let upContent: () -> UpContent | |
| let downContent: () -> DownContent | |
| @State private var dragOffset: CGFloat = .zero | |
| @State private var predictedDragOffset: CGFloat = .zero | |
| @GestureState private var isDragging: Bool = false | |
| @State private var flips: Int = 0 | |
| var isFaceUp: Bool { (flips % 2) == 0 } | |
| let flipDistance: CGFloat = 200 | |
| var angle: Angle { .radians(CGFloat(flips) * .pi) } | |
| var dragAngle: Angle { | |
| angle + .degrees(CGFloat(180) * min(max(-1, dragOffset / flipDistance), 1)) | |
| } | |
| var showFaceUp: Bool { | |
| switch dragAngle.degrees.truncatingRemainder(dividingBy: 360) { | |
| case 90...270: return false | |
| default: return true | |
| } | |
| } | |
| var body: some View { | |
| VStack { | |
| ZStack { | |
| downContent() | |
| .zIndex(-cos(dragAngle.radians)) | |
| .rotation3DEffect(dragAngle + .degrees(180), axis: (0,1,0)) | |
| upContent() | |
| .zIndex(cos(dragAngle.radians)) | |
| .rotation3DEffect(dragAngle, axis: (0,1,0)) | |
| } | |
| .frame(width: 200, height: 300) | |
| .gesture(drag) | |
| .onChange(of: isDragging) { isDragging in | |
| if !isDragging { onDragEnded() } | |
| } | |
| Text("Face \(isFaceUp ? "Up" : "Down")") | |
| Text("Flips: \(flips)") | |
| Text("DragAngle: \(dragAngle.degrees)") | |
| } | |
| } | |
| var drag: some Gesture { | |
| DragGesture() | |
| .updating($isDragging) { value, gestureState, transaction in | |
| gestureState = true | |
| } | |
| .onChanged { value in | |
| predictedDragOffset = value.predictedEndTranslation.width | |
| dragOffset = value.translation.width | |
| } | |
| } | |
| func onDragEnded() { | |
| withAnimation(.spring()) { | |
| flips += min(max(-1, Int((predictedDragOffset / flipDistance).rounded())), 1) | |
| dragOffset = .zero | |
| } | |
| } | |
| } | |
| struct TwoSidedView_Previews: PreviewProvider { | |
| static var previews: some View { | |
| TwoSidedView { | |
| RoundedRectangle(cornerRadius: 20) | |
| .fill(.blue) | |
| .overlay(Text("Up")) | |
| } downContent: { | |
| RoundedRectangle(cornerRadius: 20) | |
| .fill(.red) | |
| .overlay(Text("Down")) | |
| } | |
| } | |
| } |
ryanlintott
commented
Jul 11, 2022
Author

Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment