You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为UIPageViewController添加视差滚动效果?

为UIPageViewController添加水平视差滚动效果

首先先纠正一个点:UIPageViewController并不是继承自UIScrollView,它本质是一个UIViewController,不过当你使用.scroll过渡样式(这也是水平分页常用的样式)时,它的内部会包含一个UIScrollView作为子视图——这正是我们实现视差的关键。

下面是具体的实现步骤,亲测可行:

1. 获取内部的UIScrollView

因为这个scrollView是UIPageViewController的私有子视图,我们需要遍历它的子视图来找到:

private func findInternalScrollView() -> UIScrollView? {
    return view.subviews.first { $0 is UIScrollView } as? UIScrollView
}

在viewDidLoad里调用这个方法,拿到scrollView后设置它的delegate为当前UIPageViewController(记得要让你的UIPageViewController遵守UIScrollViewDelegate协议):

override func viewDidLoad() {
    super.viewDidLoad()
    // 其他初始化逻辑...
    if let scrollView = findInternalScrollView() {
        scrollView.delegate = self
    }
}

2. 监听滚动事件,实现视差逻辑

在scrollViewDidScroll方法里,我们可以根据滚动的偏移量,计算当前页面的滚动进度,然后调整页面内元素的位置来实现视差。

假设你的每个页面ViewController(比如叫ParallaxPageVC)里有一个需要做视差的背景视图parallaxBackgroundView,可以这样写:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let pageWidth = scrollView.bounds.width
    // 计算滚动的进度:0~1之间,代表从当前页到下一页的滚动比例
    let fractionalOffset = scrollView.contentOffset.x / pageWidth
    let currentPageIndex = Int(floor(fractionalOffset))
    let scrollProgress = fractionalOffset - CGFloat(currentPageIndex)
    
    // 处理当前页面的视差
    if let currentPageVC = viewControllers?.first as? ParallaxPageVC {
        // 让背景反向移动,速度是页面滚动的50%,你可以调整这个系数(0.5)来控制视差强度
        let backgroundOffsetX = -scrollProgress * pageWidth * 0.5
        currentPageVC.parallaxBackgroundView.transform = CGAffineTransform(translationX: backgroundOffsetX, y: 0)
    }
    
    // 处理下一个页面的视差(当正在滚动到下一页时)
    if scrollProgress > 0, 
       let nextPageVC = dataSource?.pageViewController(self, viewControllerAfter: viewControllers?.first) as? ParallaxPageVC {
        let backgroundOffsetX = (1 - scrollProgress) * pageWidth * 0.5
        nextPageVC.parallaxBackgroundView.transform = CGAffineTransform(translationX: backgroundOffsetX, y: 0)
    }
}

3. 页面切换完成后重置视图状态

为了避免下次滚动时出现异常,在页面切换动画完成后,要把视图的transform重置为初始状态:

override func didFinishAnimating(_ finished: Bool, previousViewControllers: [UIViewController], transitionCompleted: Bool) {
    super.didFinishAnimating(finished, previousViewControllers: previousViewControllers, transitionCompleted: transitionCompleted)
    
    guard transitionCompleted else { return }
    
    // 重置当前页面的背景视图
    if let currentVC = viewControllers?.first as? ParallaxPageVC {
        currentVC.parallaxBackgroundView.transform = .identity
    }
    
    // 重置上一个页面的背景视图
    if let previousVC = previousViewControllers.first as? ParallaxPageVC {
        previousVC.parallaxBackgroundView.transform = .identity
    }
}

一些注意事项

  • 这个方案依赖UIPageViewController内部的scrollView结构,虽然目前iOS版本(包括最新的)都保持这个结构,但未来苹果可能会调整,所以需要在不同iOS版本上做兼容性测试。
  • 视差强度可以通过调整代码中的系数(比如0.5)来控制:系数越大,背景移动的距离越大,视差效果越明显;系数越小,效果越平缓。
  • 确保你的页面ViewController的视图已经加载完成,不然parallaxBackgroundView可能为nil——可以在页面VC的viewDidLoad里提前初始化这个视图。
  • 如果你的UIPageViewController用的是pageCurl过渡样式,这个方法不适用,因为内部没有scrollView,不过你说的是水平方向,应该用的是.scroll样式。

总的来说,给UIPageViewController加视差滚动是完全可行的,核心就是利用它内部的scrollView监听滚动,然后动态调整页面内元素的位置~

内容的提问来源于stack exchange,提问作者Moritz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:45:35