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 letbookViewController=BookViewController() bookViewController.delegate =self // Add it to your view hierarchy addChild(bookViewController) view.addSubview(bookViewController.view) bookViewController.didMove(toParent:self) // Create pages letpages:[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:
letbookViewController=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:
letbookViewController=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:
privatevarautoOpenTimer:Timer?privatefunc scheduleAutoOpen(){ // Only auto-open if the book is at the beginning (closed) guard bookViewController.collectionView.contentOffset.x <=0.0else{return} autoOpenTimer?.invalidate() autoOpenTimer =Timer.scheduledTimer(withTimeInterval:1.0, repeats:false){[weak self] _ inself?.autoOpenBook()}}privatefunc 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:
publicenumPageType{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:
publicvarcollectionViewPageSize:CGSize{guardlet collectionView = collectionView else{return.zero } // Customize these values returnCGSize( 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
