以编程方式创建部分屏幕 UIPageViewController

Posted

技术标签:

【中文标题】以编程方式创建部分屏幕 UIPageViewController【英文标题】:Create partial-screen UIPageViewController programmatically 【发布时间】:2019-05-31 18:06:02 【问题描述】:

我正在尝试创建一个只占用部分屏幕的滚动页面视图控制器。我已经使用 Storyboard 和 ContainerView 对象成功完成了这项工作,但我现在需要复制以编程方式创建项目的功能。

似乎 ContainerView 不是我可以使用 swift 创建的东西,所以我尝试使用 UIView 作为容器来执行此操作,我尝试使用 UIPageViewController 执行 addSubview,但我收到错误 cannot convert value of type UIPageViewController to expected argument type UIView

我认为我必须在这里遗漏一些简单的东西,因为正如我所说,我已经使用 Storyboard/Interface Builder 完成了这一点,但是如何在使用 Swift 以编程方式构建时获得类似的功能?

【问题讨论】:

查看我的答案 - 我添加了一个完整的工作示例。 【参考方案1】:

UIContainerView 实际上只是故事板的便利。它添加了一个普通的UIView 并允许您连接一个视图控制器作为它的“嵌入内容”。

但是,在幕后,它正在做你可以通过代码做的事情:

UIView 添加到您将用作“容器”的主视图中 实例化您的页面视图控制器 使用addChild(_ childController: UIViewController) 将该页面视图控制器添加为子控制器 将子 VC 的视图添加到您的“容器”视图中

这是一个完整的例子:

//
//  Created by Don Mag on 5/31/19.
//

import UIKit

// simple example view controller
// has a label 90% of the width, centered X and Y
class ExampleViewController: UIViewController 

    let theLabel: UILabel = 
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .white
        v.textAlignment = .center
        return v
    ()

    override func viewDidLoad() 
        super.viewDidLoad()

        view.addSubview(theLabel)
        NSLayoutConstraint.activate([
            theLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            theLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            theLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
            ])

    



// example Page View Controller
class MyPageViewController: UIPageViewController 

    let colors: [UIColor] = [
        .red,
        .green,
        .blue,
        .cyan,
        .yellow,
        .orange
    ]

    var pages: [UIViewController] = [UIViewController]()

    override init(transitionStyle style: UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [UIPageViewController.OptionsKey : Any]? = nil) 
        super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
    

    required init?(coder: NSCoder) 
        super.init(coder: coder)
    

    override func viewDidLoad() 
        super.viewDidLoad()

        dataSource = self
        delegate = nil

        // instantiate "pages"
        for i in 0..<colors.count 
            let vc = ExampleViewController()
            vc.theLabel.text = "Page: \(i)"
            vc.view.backgroundColor = colors[i]
            pages.append(vc)
        

        setViewControllers([pages[0]], direction: .forward, animated: false, completion: nil)
    



// typical Page View Controller Data Source
extension MyPageViewController: UIPageViewControllerDataSource 
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? 

        guard let viewControllerIndex = pages.index(of: viewController) else  return nil 

        let previousIndex = viewControllerIndex - 1

        guard previousIndex >= 0 else  return pages.last 

        guard pages.count > previousIndex else  return nil 

        return pages[previousIndex]
    

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? 
        guard let viewControllerIndex = pages.index(of: viewController) else  return nil 

        let nextIndex = viewControllerIndex + 1

        guard nextIndex < pages.count else  return pages.first 

        guard pages.count > nextIndex else  return nil 

        return pages[nextIndex]
    


// typical Page View Controller Delegate
extension MyPageViewController: UIPageViewControllerDelegate 

    // if you do NOT want the built-in PageControl (the "dots"), comment-out these funcs

    func presentationCount(for pageViewController: UIPageViewController) -> Int 
        return pages.count
    

    func presentationIndex(for pageViewController: UIPageViewController) -> Int 

        guard let firstVC = pageViewController.viewControllers?.first else 
            return 0
        
        guard let firstVCIndex = pages.index(of: firstVC) else 
            return 0
        

        return firstVCIndex
    


class MyTestViewController: UIViewController 

    let myContainerView: UIView = 
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .gray
        return v
    ()

    var thePageVC: MyPageViewController!

    override func viewDidLoad() 
        super.viewDidLoad()

        // add myContainerView
        view.addSubview(myContainerView)

        // constrain it - here I am setting it to
        //  40-pts top, leading and trailing
        //  and 200-pts height
        NSLayoutConstraint.activate([
            myContainerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40.0),
            myContainerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40.0),
            myContainerView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40.0),
            myContainerView.heightAnchor.constraint(equalToConstant: 200.0),
            ])

        // instantiate MyPageViewController and add it as a Child View Controller
        thePageVC = MyPageViewController()
        addChild(thePageVC)

        // we need to re-size the page view controller's view to fit our container view
        thePageVC.view.translatesAutoresizingMaskIntoConstraints = false

        // add the page VC's view to our container view
        myContainerView.addSubview(thePageVC.view)

        // constrain it to all 4 sides
        NSLayoutConstraint.activate([
            thePageVC.view.topAnchor.constraint(equalTo: myContainerView.topAnchor, constant: 0.0),
            thePageVC.view.bottomAnchor.constraint(equalTo: myContainerView.bottomAnchor, constant: 0.0),
            thePageVC.view.leadingAnchor.constraint(equalTo: myContainerView.leadingAnchor, constant: 0.0),
            thePageVC.view.trailingAnchor.constraint(equalTo: myContainerView.trailingAnchor, constant: 0.0),
            ])

        thePageVC.didMove(toParent: self)
    


结果:

【讨论】:

错误的顺序。在 addSubview 之后调用 didMove,而不是之前。 感谢您提供如此详尽的回答。看起来这正是我需要的。我将对此进行测试并尽快接受答案。

以上是关于以编程方式创建部分屏幕 UIPageViewController的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式检测屏幕撕裂?

如何以编程方式创建给定网站的屏幕截图?

如何在设备旋转后以编程方式创建的 UIButtons 保持在屏幕底部?

尝试以编程方式创建尺寸小于屏幕的 iOS WKWebView

使用自动布局的屏幕外 UIView(以编程方式)

是否可以以编程方式创建展开转场?