Skip to content

Latest commit

 

History

42 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Foldy

Animate any view with Apple Duo-like fold animation.

Showcase

foldy-demo.mp4

Inspiration

On September 9, 2026 Apple unveiled iPhone Duo, its first foldable iPhone. When the phone opens or closes, iOS does not cut between the cover and inner displays. The interface frosts, blurs, and stretches through the moving half of the screen, driven by the hinge angle, so the change feels continuous. Apple describes a display engine that drives both screens during the transition in its announcement.

Foldy is an independent recreation of that look for any view on iOS, macOS, and watchOS. A Metal shader keeps the interface on a fixed plane and refracts it through a tilting pane of frosted glass. It is not Apple's implementation and needs no folding hardware.

Installation

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/insanearts/foldy.git", from: "0.3.0")
]

Or via Xcode: File → Add Package Dependencies. The Metal shader ships in the package resource bundle; nothing needs to be copied into your app.

Quick Start

Fold one screen into another. Zero shows the source, one the destination:

import SwiftUI
import Foldy

struct Example: View {
    @State private var progress = 0.0

    var body: some View {
        FoldTransition(progress: progress) {
            FirstScreen()
        } destination: {
            SecondScreen()
        }
        .foldSwipe(progress: $progress)
        .onTapGesture { withAnimation(.easeInOut(duration: 0.8)) { progress = 1 } }
    }
}

foldSwipe lets a swipe in any direction drive the fold and settle on the nearer endpoint. Content stays tappable.

Or tilt a single view like a pane of frosted glass:

Card().foldEffect(angle: .degrees(30))

What's in the box

FoldTransition Fold between two SwiftUI views; both keep their state
.foldEffect(angle:) / .foldEffect(tilt:) Tilt one view on one or two axes
.foldSwipe(progress:) Drive any fold with a swipe
FoldPager Page or grid-navigate a list of items with page turns
FoldCutoutPull, FoldCutoutGhost, FoldCutoutSnap Pull rows into the Dynamic Island as they scroll
FoldMotionSource Device tilt and nudge detection
FoldStyle and FoldAppearance Hinge, optics, choreography, and six glass materials
FoldContainerView The UIKit and AppKit core

Glass materials

Six appearances: .frosted (default), .clear, .grain, .gloss, .ink, and .midnight. Every one is invisible at rest.

FoldTransition(progress: progress, style: FoldStyle(appearance: .midnight)) {
    FirstScreen()
} destination: {
    SecondScreen()
}

FoldStyle also picks the hinge edge and the choreography: .reveal folds the source away over a stationary destination, .pageTurn unfolds the destination around the same hinge.

Paging

@State private var index = 0

FoldPager(items: places, columns: 3, selection: $index) { place in
    PlaceCard(place)
}

A swipe turns to the next or previous item. Hand it a FoldMotionSource and a nudge of the phone turns the page too.

Dynamic Island

FoldCutout.detect returns the island frame. FoldCutoutPull folds a view from where it is into that frame:

let cutout = FoldCutout.detect(safeAreaTop: insets.top, screenWidth: width) ?? .placeholder(screenWidth: width)

card.modifier(FoldCutoutPull(progress: pull, start: cardFrame, target: cutout.frame))

Pass liquid: true and the view is swallowed like liquid: the glass ripples, the edge dissolves into black goo, and the island bulges to meet it. The demo's story feed and photo grid show the full scrolling pattern.

UIKit and AppKit

let fold = FoldContainerView(source: firstView, destination: secondView)
view.addSubview(fold)
fold.frame = view.bounds

fold.animate(to: .destination)
// Or drive an interactive gesture:
fold.setProgress(0.4)

Use FoldContainerView(content:) with setAngle(_:) or setTilt(_:) for a single view. On macOS the container is an NSView with the same API, and a click-and-drag through foldSwipe or your own NSPanGestureRecognizer drives it.

Reduce Motion

The system setting skips capture and GPU rendering and shows the nearest endpoint. You can also opt in with reducesMotion: true.

Example app

Open Examples/FoldyDemo/FoldyDemo.xcodeproj. It ships ten screens recreated from familiar apps: a Duo-style folding display, a nudge-to-page atlas, two feeds that melt into the Dynamic Island, six two-screen transitions, and a showcase of all of them at once. Select your own team under Signing & Capabilities for a device run.

Everything about capture, lifecycle, motion sampling, and the style parameters is in the full reference.

Agent skill

The repo ships a skill that teaches Claude Code, Codex, and other agents the Foldy API, its rules, and recipes. Install it into the agent directories you use:

curl -fsSL https://raw.githubusercontent.com/insanearts/foldy/main/skills/install.sh | sh

Or copy skills/foldy/ into ~/.claude/skills/, ~/.codex/skills/, or .claude/skills/ in your project. Then ask the agent for a fold and it reaches for Foldy.

Requirements

  • iOS 17+, macOS 14+, and watchOS 10+
  • Swift 6, Xcode 16 or later
  • The simulator runs every fold; physical tilt and nudges need an iPhone or Apple Watch
  • Apple Watch has no Metal, so watchOS draws folds with SwiftUI's 3D rotation and blur. FoldTransition, foldEffect, foldSwipe, FoldPager, and FoldMotionSource work there; FoldContainerView is iOS and macOS only
  • FoldMotionSource reads sensors on iOS and watchOS; on macOS isAvailable is false

License

MIT. See LICENSE.

About

Animate any view with Apple Duo-like fold animation on Apple platforms.

Topics

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages