找回密码
 立即注册
首页 业界区 业界 iOS 实现微信读书的仿真翻页

iOS 实现微信读书的仿真翻页

米嘉怡 7 天前
先看效果

仿真翻页效果:
1.gif

普通翻页效果:
2.gif

实现方案

iOS 中实现翻页效果比较简单,直接使用系统提供的 UIPageViewController 即可做到。
UIPageViewController 是 UIKit 中的分页控制器,它允许用户通过横向或纵向滑动手势在多个页面(ViewController)之间切换,主要配置的两个属性如下:
1)UIPageViewControllerTransitionStyle

  • .pageCurl:仿真翻页
  • .scroll:类似 UIScrollView 自然滑动
2)UIPageViewControllerNavigationOrientation

  • .horizontal:左右翻页
  • .vertical:上下翻页
以仿真翻页配置为例子:
  1. class BookReaderViewController: UIViewController {
  2.     // 模拟书籍数据
  3.     private let bookPages = [
  4.         "第一章:Swift 的起源\n\nSwift 是一种由 Apple 开发的强大且直观的编程语言...",
  5.         "第二章:UIKit 基础\n\nUIKit 提供了构建 iOS 应用程序所需的关键对象...",
  6.         "第三章:动画艺术\n\n核心动画 (Core Animation) 是 iOS 界面流畅的关键...",
  7.         "第四章:高级翻页\n\nUIPageViewController 是实现仿真翻页的神器...",
  8.         "终章:未来展望\n\n随着 SwiftUI 的普及,声明式 UI 正在改变世界..."
  9.     ]
  10.     private var pageViewController: UIPageViewController!
  11.     override func viewDidLoad() {
  12.         super.viewDidLoad()
  13.         setupPageViewController()
  14.     }
  15.     private func setupPageViewController() {
  16.         // 关键设置:transitionStyle = .pageCurl (仿真翻页效果)
  17.         // navigationOrientation = .horizontal (水平翻页)
  18.         pageViewController = UIPageViewController(transitionStyle: .pageCurl,
  19.                                                   navigationOrientation: .horizontal,
  20.                                                   options: nil)
  21.         pageViewController.dataSource = self
  22.         pageViewController.delegate = self
  23.         // 设置初始页面
  24.         if let firstPage = getViewController(at: 0) {
  25.             pageViewController.setViewControllers([firstPage], direction: .forward, animated: false, completion: nil)
  26.         }
  27.         // 将 PageVC 添加到当前 VC
  28.         addChild(pageViewController)
  29.         view.addSubview(pageViewController.view)
  30.         pageViewController.view.frame = view.bounds
  31.         pageViewController.didMove(toParent: self)
  32.         // 解决仿真翻页背面颜色问题 (让背面也是纸张色,而不是默认的半透明或白色)
  33.         // 注意:这是一个比较 Hack 的方法,更完美的做法是自定义背面的 Layer
  34.         pageViewController.view.backgroundColor = UIColor(red: 248/255, green: 241/255, blue: 227/255, alpha: 1.0)
  35.     }
  36.     // 辅助方法:根据索引获取 VC
  37.     private func getViewController(at index: Int) -> BookPageViewController? {
  38.         guard index >= 0 && index < bookPages.count else { return nil }
  39.         return BookPageViewController(index: index, totalPage: bookPages.count, content: bookPages[index])
  40.     }
  41. }
  42. // MARK: - 3. DataSource 实现 (核心逻辑)
  43. extension BookReaderViewController: UIPageViewControllerDataSource {
  44.     // 获取"上一页"
  45.     func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
  46.         guard let currentVC = viewController as? BookPageViewController else { return nil }
  47.         let previousIndex = currentVC.pageIndex - 1
  48.         return getViewController(at: previousIndex)
  49.     }
  50.     // 获取"下一页"
  51.     func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
  52.         guard let currentVC = viewController as? BookPageViewController else { return nil }
  53.         let nextIndex = currentVC.pageIndex + 1
  54.         return getViewController(at: nextIndex)
  55.     }
  56. }
  57. // MARK: - 4. Delegate (可选,用于处理翻页后的状态)
  58. extension BookReaderViewController: UIPageViewControllerDelegate {
  59.     // 这里可以处理 spineLocation,例如横屏时显示双页
  60.     func pageViewController(_ pageViewController: UIPageViewController, spineLocationFor orientation: UIInterfaceOrientation) -> UIPageViewController.SpineLocation {
  61.         // 手机竖屏通常是单页 (.min)
  62.         return .min
  63.     }
  64. }
复制代码
如上不到百行的代码即可实现仿真翻页效果,手势在 UIPageViewController 中会自动处理,外部不用感知。
和 UITableView 使用类似,需要通过 UIPageViewControllerDataSource 来提供上一页和下一页的数据源,通过 UIPageViewControllerDelegate 来感知翻页时机。
UIPageViewControllerDelegate 主要提供三个时机:
1)willTransitionTo:当用户开始滑动翻页的时候触发,系统已经准备好目标页,通过该回调来告诉你将要显示哪个页面(pendingViewControllers)
  1. /// pendingViewControllers: 将要显示的页面
  2. func pageViewController(_ pageViewController: UIPageViewController,
  3.                         willTransitionTo pendingViewControllers: [UIViewController])
复制代码
2)didFinishAnimating:当用户的翻页动画结束时回调
  1. /// finished: 动画是否完成
  2. /// previousViewControllers: 原来显示的ViewController
  3. /// completed: 最终是否翻页成功;比如滑一半又拖回去,不会真正翻页
  4. func pageViewController(_ pageViewController: UIPageViewController,
  5.                         didFinishAnimating finished: Bool,
  6.                         previousViewControllers: [UIViewController],
  7.                         transitionCompleted completed: Bool)
复制代码
3)spineLocationFor:控制仿真书本翻页时 “书脊” 的位置,只在 UIPageViewControllerTransitionStyle 为 pageCurl 时有效
  1. /// SpineLocation:
  2. /// - none: 没书脊
  3. /// - min: 书脊在左边(单页模式)
  4. /// - mid: 书脊在中间(双页模式)
  5. /// - max: 书脊在右边(单页模式)
  6. func pageViewController(_ pageViewController: UIPageViewController,
  7.                         spineLocationFor orientation: UIInterfaceOrientation)
  8. -> UIPageViewController.SpineLocation
复制代码
如果要配置普通翻页效果,只需要修改 UIPageViewController 的配置即可:
  1. // Options: 设置页面之间的间距 (微信读书一般有 10-20pt 的间距)
  2. let options: [UIPageViewController.OptionsKey: Any] = [
  3.     .interPageSpacing: 20
  4. ]
  5. // 核心修改 1: transitionStyle 改为 .scroll
  6. pageViewController = UIPageViewController(transitionStyle: .scroll,
  7.                                           navigationOrientation: .horizontal,
  8.                                           options: options)
复制代码
另外翻页手势通常和系统的侧滑返回手势有冲突,可以手动禁用手势来解决;类似微信读书一样,在导航栏出现时才开启侧滑返回手势,否则禁用侧滑返回:
  1. private func updateGesture() {
  2.     if isNaviBarHidden { // 导航栏隐藏:禁用侧滑,开启翻页手势
  3.         navigationController?.interactivePopGestureRecognizer?.isEnabled = false
  4.         for gesture in pageViewController.gestureRecognizers {
  5.             gesture.isEnabled = true
  6.         }
  7.     } else { // 导航栏显示:开启侧滑,禁用翻页手势
  8.         navigationController?.interactivePopGestureRecognizer?.isEnabled = true
  9.         for gesture in pageViewController.gestureRecognizers {
  10.             gesture.isEnabled = false
  11.         }
  12.     }
  13. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册