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

UIPageViewController子类滑动至下一页(after)时黑屏问题求助

Fixing Black Screen When Swiping Forward in UIPageViewController Subclass

Hey Kiren, let's figure out why you're hitting that black screen when swiping to the next page. The core issue here is you're updating the currentIndex directly in your UIPageViewController data source methods—that's not the right place to handle state changes for this component.

Here's the breakdown: UIPageViewController calls its data source methods (like viewControllerAfter) to preload adjacent pages before a swipe finishes. When you increment currentIndex in that method, you're altering your coordinator's state prematurely—even if the user cancels the swipe, your index is already out of sync. This leads to incorrect view controllers being returned (or nil when it shouldn't be), which causes the black screen.

Let's Fix This Step by Step

  1. Update your coordinator protocol to add a way to find the index of a given view controller (we'll need this to sync the index correctly after swipes).
  2. Rework the coordinator to remove index updates from the method that returns view controllers, and add the index lookup logic.
  3. Move index updates to the delegate callback—this ensures we only update the index when a swipe fully completes, not during preloading.

Modified Code Snippets

1. Updated Coordinator.swift

import UIKit

enum PageViewDirection {
    case before
    case after
}

typealias Index = Int

protocol PageViewCoordinator: class {
    var currentIndex: Index { get set }
    var viewControllers: [UIViewController] { get set }
    func viewController(for direction: PageViewDirection) -> UIViewController?
    func index(of viewController: UIViewController) -> Index? // New helper method
}

2. Updated OnboardingViewCoordinator.swift

import UIKit

final class OnboardingViewCoordinator: PageViewCoordinator {
    var currentIndex: Index
    var viewControllers: [UIViewController]
    
    init() {
        self.viewControllers = [
            OnboardingTaglineViewController(nibName: nil, bundle: nil),
            OnboardingSourcesViewController(nibName: nil, bundle: nil)
        ]
        self.currentIndex = 0
    }
    
    // Now only returns the correct VC without modifying state
    func viewController(for direction: PageViewDirection) -> UIViewController? {
        switch direction {
        case .before:
            guard currentIndex > 0 else { return nil }
            return viewControllers[currentIndex - 1]
        case .after:
            guard currentIndex < viewControllers.count - 1 else { return nil }
            return viewControllers[currentIndex + 1]
        }
    }
    
    // Finds the index of a VC using reference equality (avoids mismatches)
    func index(of viewController: UIViewController) -> Index? {
        return viewControllers.firstIndex { $0 === viewController }
    }
}

3. Updated WarmPushPageViewController.swift

import UIKit

class WarmPushPageViewController: UIPageViewController {
    var coordinator: PageViewCoordinator? = nil
    
    convenience init(coordinator: PageViewCoordinator) {
        self.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
        self.setViewControllers([coordinator.viewControllers[coordinator.currentIndex]], direction: .forward, animated: true, completion: nil)
        self.coordinator = coordinator
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
        self.dataSource = self
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

extension WarmPushPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let coordinator = self.coordinator else { return nil }
        return coordinator.viewController(for: .before)
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let coordinator = self.coordinator else { return nil }
        return coordinator.viewController(for: .after)
    }
}

// Add this delegate extension to handle index updates ONLY after a full swipe
extension WarmPushPageViewController: UIPageViewControllerDelegate {
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        guard completed,
              let coordinator = coordinator,
              let currentVC = pageViewController.viewControllers?.first,
              let newIndex = coordinator.index(of: currentVC) else {
            return
        }
        // Only update the index if the transition fully completed
        coordinator.currentIndex = newIndex
    }
}

Why This Works

  • State updates only happen on completed swipes: The didFinishAnimating delegate method ensures we only adjust currentIndex when the user has fully moved to a new page, not when the page is just being preloaded.
  • DataSource methods stay stateless: Now, viewControllerBefore and viewControllerAfter simply return the correct adjacent page based on the current index, no state changes included. This eliminates index desync issues.
  • Accurate index lookup: Using reference equality ($0 === viewController) guarantees we find the exact index of the displayed view controller, even if you have multiple instances of the same VC type.

Give this a test run—this should fix the black screen issue when swiping forward. Let me know if you hit any other bumps!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:04:19