Skip to content

Instantly share code, notes, and snippets.

@ryanlintott
Created July 11, 2022 22:26
Show Gist options
  • Select an option

  • Save ryanlintott/b1ec2e15b4b7e59d4bfd2ad149c1d82e to your computer and use it in GitHub Desktop.

Select an option

Save ryanlintott/b1ec2e15b4b7e59d4bfd2ad149c1d82e to your computer and use it in GitHub Desktop.
A two sided view that can be flipped with a drag gesture.
//
// 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

Copy link
Copy Markdown
Author

2022-07-11 18 28 31

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