Skip to content

Commit

Permalink
Implement customizable Flashcard view
Browse files Browse the repository at this point in the history
  • Loading branch information
mariana0412 committed Nov 16, 2024
1 parent f66a83b commit 9920b8f
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import PackageDescription

let package = Package(
name: "CardFlipster",
platforms: [
.iOS(.v15)
],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
Expand Down
41 changes: 41 additions & 0 deletions Sources/CardFlipster/AnyTransition+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// AnyTransition+Extension.swift
// CardFlipster
//
// Created by Mariana Piz on 16.11.2024.
//

import SwiftUI

// Code snippet sourced from Stack Overflow
// https://stackoverflow.com/a/79162092
// Answer by Robert Dresler
extension AnyTransition {

@MainActor static func flip(side: FlipTransitionViewModifier.Side,
axis: Axis = .horizontal,
perspective: CGFloat = 0,
animationDuration: TimeInterval = 0.25) -> AnyTransition {
.modifier(
active: FlipTransitionViewModifier(
isActive: true,
side: side,
axis: axis,
perspective: perspective
),
identity: FlipTransitionViewModifier(
isActive: false,
side: side,
axis: axis,
perspective: perspective
)
)
.combined(
with: .opacity.animation(
.linear(duration: 0.001).delay(animationDuration / 2)
)
)
.animation(.linear(duration: animationDuration))
}

}
1 change: 0 additions & 1 deletion Sources/CardFlipster/CardFlipster.swift

This file was deleted.

100 changes: 100 additions & 0 deletions Sources/CardFlipster/FlashcardView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// FlashcardView.swift
// CardFlipster
//
// Created by Mariana Piz on 15.11.2024.
//

import SwiftUI

public struct FlashcardView: View {

private enum Constants {
static let cardWidth: CGFloat = 300
static let cardHeight: CGFloat = 450
static let cornerRadius: CGFloat = 10
static let minScaleFactor: CGFloat = 0.8
}

@State private var side: FlipTransitionViewModifier.Side = .front

private var frontText: String = "Front"
private var backText: String = "Back"
private var frontColor: Color = .blue
private var backColor: Color = .yellow
private var font: Font = .title
private var frontFontColor: Color = .white
private var backFontColor: Color = .black
private var axis: Axis = .horizontal
private var animationDuration: TimeInterval = 0.6

public init(
frontText: String = "Front",
backText: String = "Back",
frontColor: Color = .blue,
backColor: Color = .yellow,
font: Font = .title,
frontFontColor: Color = .white,
backFontColor: Color = .black,
axis: Axis = .horizontal,
animationDuration: TimeInterval = 0.6
) {
self.frontText = frontText
self.backText = backText
self.frontColor = frontColor
self.backColor = backColor
self.font = font
self.frontFontColor = frontFontColor
self.backFontColor = backFontColor
self.axis = axis
self.animationDuration = animationDuration
}

public var body: some View {
VStack {
cardView
}
}

private var cardView: some View {
flashcard
.id(side)
.transition(.flip(side: side,
axis: axis,
animationDuration: animationDuration))
.onTapGesture {
withAnimation(.easeInOut(duration: animationDuration)) {
side = side == .front ? .back : .front
}
}
}

private var flashcard: some View {
RoundedRectangle(cornerRadius: Constants.cornerRadius)
.fill(side == .back ? backColor : frontColor)
.overlay(contentOverlay)
.frame(width: Constants.cardWidth, height: Constants.cardHeight)
}

private var contentOverlay: some View {
Text(side == .front ? frontText : backText)
.font(font)
.foregroundColor(side == .front ? frontFontColor : backFontColor)
.padding()
.minimumScaleFactor(Constants.minScaleFactor)
}
}

#Preview {
FlashcardView(
frontText: "Hello, World!",
backText: "Goodbye, World!",
frontColor: .blue,
backColor: .yellow,
font: .largeTitle,
frontFontColor: .white,
backFontColor: .black,
axis: .horizontal,
animationDuration: 0.8
)
}
52 changes: 52 additions & 0 deletions Sources/CardFlipster/FlipTransitionViewModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// FlipTransitionViewModifier.swift
// CardFlipster
//
// Created by Mariana Piz on 16.11.2024.
//

import SwiftUI

// Code snippet sourced from Stack Overflow
// https://stackoverflow.com/a/79162092
// Answer by Robert Dresler
struct FlipTransitionViewModifier: ViewModifier {

public enum Side {
case front
case back
}

let isActive: Bool
let side: Side
let axis: Axis
let perspective: CGFloat

public func body(content: Content) -> some View {
content
.rotation3DEffect(
Angle.degrees(side == .back ? 0 : 180),
axis: resolvedAxis,
perspective: 0
)
.rotation3DEffect(
Angle.degrees(
isActive
? (side == .back ? 180 : 0)
: (side == .back ? 0 : 180)
),
axis: resolvedAxis,
perspective: perspective
)
}

private var resolvedAxis: (CGFloat, CGFloat, CGFloat) {
switch axis {
case .horizontal:
(0, 1, 0)
case .vertical:
(1, 0, 0)
}
}

}

0 comments on commit 9920b8f

Please sign in to comment.