Search code examples
swiftuipageviewcontroller

PageViewController shows part of other viewcontroller


I have a UIPageViewController with 2 UIViewControllers which both have a UIImageView as background with content mode set to scaleAspectFill. This causes for some reason that the second UIViewController is partly visible when scrolling first to the second UIViewController and than scrolling back to the first UIViewController.

The reproduction project can be found here: https://github.com/Jasperav/PageViewControllerGlitch. The code is also beneath the photo.

Photo (the space is the first UIViewController, the green part is the second UIViewController): enter image description here

Code (I don't use storyboards, for a reproduction path with the correct window, use the github link):

import UIKit

class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    private let pageViewControllers: [UIViewController] = [VC1(), VC2()]

    init() {
        super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)

        dataSource = self
        delegate = self

        setViewControllers([pageViewControllers.first!], direction: .forward, animated: false, completion: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return pageViewControllers.count
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let index = pageViewControllers.index(of: viewController), index > 0 else { return nil }

        let previousIndex = index - 1

        return pageViewControllers[previousIndex]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let index = pageViewControllers.index(of: viewController), (index + 1) != pageViewControllers.count else { return nil }

        return pageViewControllers[index + 1]
    }
}

class VC: UIViewController {
    init(background: UIImage) {
        super.init(nibName: nil, bundle: nil)

        let backgroundImageView = UIImageView(image: background)

        backgroundImageView.contentMode = .scaleAspectFill

        view.addSubview(backgroundImageView)

        backgroundImageView.translatesAutoresizingMaskIntoConstraints = false

        backgroundImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        backgroundImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        backgroundImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        backgroundImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class VC1: VC {
    init() {
        super.init(background: #imageLiteral(resourceName: "bg1"))
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class VC2: VC {
    init() {
        super.init(background: #imageLiteral(resourceName: "bg2"))
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Solution

  • When you use AspectFill as the content mode, if the image is larger than the UImageView's dimension, the image overflows. To solve this issue you can clip its subviews:

    backgroundImageView.contentMode = .scaleAspectFill
    backgroundImageView.clipsToBounds = true
    

    Another solution is to try another content mode like .scaleToFill. Hope this helps, i just tried this solutions in your demo project and both worked.