A custom UICollectionView layout that creates a realistic book page-turning user interface effect.
This project contains a standalone implementation of a book-like page layout for iOS. The layout creates a 3D page-turning effect where pages flip like a real book, with proper shadows and perspective transforms.
A custom UICollectionViewFlowLayout that handles the 3D transforms and page positioning.
Features:
- Realistic 3D page turning effect
- Configurable layout styles (centered or equal margins)
- Automatic page sizing based on collection view bounds
- Smooth scrolling with paging enabled
- Left and right pages with proper anchor points
- Customizable edge margins
A data model representing a single page in the book.
Page Types:
.invisible- Hidden pages (used for proper book opening).blank- Empty white pages.cover- Book cover with title and subtitle.content(String)- Page with text content.colored(UIColor)- Page with custom background color.decorative(iconNames: [String], iconSize: CGFloat, spacing: CGFloat)- Decorative pattern page with SF Symbols arranged in a staggered diagonal grid
A UICollectionViewCell that displays individual pages with:
- Rounded corners
- Dynamic shadows based on page position
- Support for different page types
- Proper anchor point handling for left/right pages
A view controller that manages the book's pages using the BookLayout.
Properties:
bookLayout: BookLayout?- Access to the underlying BookLayout for configurationcollectionView: UICollectionView- The collection view displaying the pagesdelegate: BookViewControllerDelegate?- Delegate for page selection events
Methods:
setPages(_ pages: [BookPageModel], animated: Bool)- Set the book's pagesopenToPage(at index: Int, animated: Bool)- Open to a specific page
// Create the book view controller
let bookViewController = BookViewController()
bookViewController.delegate = self
// Add it to your view hierarchy
addChild(bookViewController)
view.addSubview(bookViewController.view)
bookViewController.didMove(toParent: self)
// Create pages
let pages: [BookPageModel] = [
BookPageModel(pageType: .invisible),
BookPageModel(pageType: .invisible),
BookPageModel(pageType: .cover, title: "The Hacker's Manifesto", subtitle: "A Demo"),
BookPageModel(
pageType: .decorative(
iconNames: ["star.fill", "heart.fill", "circle.fill", "square.fill"],
iconSize: 32,
spacing: 16
)
),
BookPageModel(pageType: .content("Page 1"), subtitle: "First page content"),
BookPageModel(pageType: .blank),
BookPageModel(pageType: .colored(.systemBlue), title: "Blue Page"),
BookPageModel(pageType: .blank)
]
// Set the pages
bookViewController.setPages(pages, animated: false)
// Open to a specific page
bookViewController.openToPage(at: 4, animated: true)BookLayout supports two layout styles for positioning the book:
Classic Layout (Default)
bookViewController.bookLayout?.layoutStyle = .classic
bookViewController.bookLayout?.edgeMargin = 20 // Default is 20pt- Equal margins on both left and right sides
- Spine positioned 20pt from the left edge
- Right page edge positioned 20pt from the right edge
- Ensures all content remains within screen bounds
- Traditional, balanced book appearance
Centered Layout
bookViewController.bookLayout?.layoutStyle = .centered- Spine centered in the middle of the screen
- Pages may extend beyond the screen edges
- More immersive reading experience
- Natural page-turning feel
Example:
let bookViewController = BookViewController()
// Use classic layout with equal margins (default)
bookViewController.bookLayout?.layoutStyle = .classic
bookViewController.bookLayout?.edgeMargin = 20 // Adjust as needed
// Or use centered layout
bookViewController.bookLayout?.layoutStyle = .centeredBookLayout supports two page turning styles that control how pages animate:
Elevated Style (Default)
bookViewController.bookLayout?.pageTurnStyle = .elevated- Pages rest with a subtle lift on the left edge
- Creates a realistic book appearance where turned pages are slightly elevated
- Smooth interpolation during page turns
- More authentic physical book feel
Standard Style
bookViewController.bookLayout?.pageTurnStyle = .standard- Simple linear page turning animation
- Pages lay completely flat when at rest
- Cleaner, more minimalist appearance
- Faster rendering with simpler transforms
Example:
let bookViewController = BookViewController()
// Configure layout and page turn style
bookViewController.bookLayout?.layoutStyle = .classic
bookViewController.bookLayout?.pageTurnStyle = .elevated // Default
// Or use standard turning for a simpler effect
bookViewController.bookLayout?.pageTurnStyle = .standardThe BookLayout works with a section-based collection view:
- Each page is a separate section with 1 item
- Even sections (0, 2, 4...) are right pages
- Odd sections (1, 3, 5...) are left pages
- Pages should always come in pairs for proper book effect
For a proper book effect, structure your pages like this:
- Two invisible pages (indices 0-1) - allows the book to start "closed"
- Cover page (right side, index 2)
- Blank or inside cover (left side, index 3)
- Content pages in pairs (right, left, right, left...)
- Back inside cover (last page)
For a polished presentation effect, you can automatically open the book after a brief delay:
private var autoOpenTimer: Timer?
private func scheduleAutoOpen() {
// Only auto-open if the book is at the beginning (closed)
guard bookViewController.collectionView.contentOffset.x <= 0.0 else { return }
autoOpenTimer?.invalidate()
autoOpenTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] _ in
self?.autoOpenBook()
}
}
private func autoOpenBook() {
autoOpenTimer?.invalidate()
autoOpenTimer = nil
// Open to the first content page (after invisible pages and cover)
bookViewController.openToPage(at: 4, animated: true)
}
// Call after setting pages
bookViewController.setPages(pages, animated: false)
scheduleAutoOpen()This creates a book-like presentation where the book starts closed and automatically opens after 1 second, similar to opening a physical book.
See Example/BookLayoutExample/ViewController.swift for a complete working example.
Add BookLayout to your project using Swift Package Manager:
- In Xcode, select File > Add Package Dependencies
- Enter the repository URL:
https://github.com/piemonte/BookLayout.git - Select the version you want to use
- Add the
BookLayoutproduct to your target
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/piemonte/BookLayout.git", from: "0.0.2")
]To manually integrate these components into your project:
- Copy all
.swiftfiles from theSourcesdirectory - Add them to your Xcode project
- Use
BookViewControllerin your view controller hierarchy - Customize
BookPageCellto fit your design needs
An example iOS app is available in the Example directory. To run it:
cd Example
./setup.sh
open BookLayoutExample.xcodeprojThe setup script will install XcodeGen if needed and generate the Xcode project.
If you prefer to set up manually:
- Install XcodeGen:
brew install xcodegen - Navigate to the
Exampledirectory - Run
xcodegen generate - Open
BookLayoutExample.xcodeprojin Xcode
See Example/README.md for more details.
Extend BookPageModel.PageType with your own page types:
public enum PageType {
case invisible
case blank
case cover
case content(String)
case colored(UIColor)
case custom(MyCustomData) // Add your own
}Modify BookPageCell.configure(with:) to customize how pages are displayed:
case .custom(let data):
pageView.isHidden = false
// Add your custom UI setup hereAdjust the page size calculation in BookLayout.collectionViewPageSize:
public var collectionViewPageSize: CGSize {
guard let collectionView = collectionView else {
return .zero
}
// Customize these values
return CGSize(
width: (0.5 * collectionView.bounds.width) - 20,
height: collectionView.bounds.height - 50
)
}- Swift 5.0+
- UIKit
See original source files for copyright information.
- Gowalla Passport Interface - my OG implementation (very dated) and inspiration for this project
