PageViewController - 将变量传递给子视图

PageViewController - Pass variables to child views

我有什么

我有一个ViewController(教程ViewController)和一个UIPageViewController(教程页面ViewController)。故事板上还有 3 个额外的视图,带有故事板 ID:

一直在关注这个tutorial(感谢作者,写的很好)

在 Green View Controller 上我定义了一个变量:

var passedVariable = ""

然后在 ViewDidLoad 中打印出来。

以下是具有代码的两个控制器:

UIViewController(教程ViewController):

class TutorialViewController: UIViewController {

@IBOutlet weak var pageControl: UIPageControl!
@IBOutlet weak var containerView: UIView!


var tutorialPageViewController: TutorialPageViewController? {
    didSet {
        tutorialPageViewController?.tutorialDelegate = self
    }
}


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let tutorialPageViewController = segue.destinationViewController as? TutorialPageViewController {
        self.tutorialPageViewController = tutorialPageViewController
    }
}

@IBAction func didTapNextButton(sender: UIButton) {
    tutorialPageViewController?.scrollToNextViewController()
}
}

extension TutorialViewController: TutorialPageViewControllerDelegate {

func tutorialPageViewController(tutorialPageViewController: TutorialPageViewController,
    didUpdatePageCount count: Int) {
    pageControl.numberOfPages = count
}

func tutorialPageViewController(tutorialPageViewController: TutorialPageViewController,
    didUpdatePageIndex index: Int) {
    pageControl.currentPage = index
}

}

UIPageViewController

class TutorialPageViewController: UIPageViewController {

weak var tutorialDelegate: TutorialPageViewControllerDelegate?

//let vc0 = GreenViewController(nibName: "GreenViewController", bundle: nil)

private(set) lazy var orderedViewControllers: [UIViewController] = {
    // The view controllers will be shown in this order
    return [self.newColoredViewController("Green"),
        self.newColoredViewController("Red"),
        self.newColoredViewController("Blue"), self.newColoredViewController("Pink")]
}()

override func viewDidLoad() {
    super.viewDidLoad()

    //self.vc0.passedVariable = "Passed Data"

    dataSource = self
    delegate = self

    if let initialViewController = orderedViewControllers.first {
        scrollToViewController(initialViewController)
    }

    tutorialDelegate?.tutorialPageViewController(self,
        didUpdatePageCount: orderedViewControllers.count)
}


/**
 Scrolls to the next view controller.
 */
func scrollToNextViewController() {
    if let visibleViewController = viewControllers?.first,
        let nextViewController = pageViewController(self,
            viewControllerAfterViewController: visibleViewController) {
                scrollToViewController(nextViewController)
    }
}

private func newColoredViewController(color: String) -> UIViewController {
    return UIStoryboard(name: "Main", bundle: nil) .
        instantiateViewControllerWithIdentifier("\(color)ViewController")
}

/**
 Scrolls to the given 'viewController' page.

 - parameter viewController: the view controller to show.
 */
private func scrollToViewController(viewController: UIViewController) {
    setViewControllers([viewController],
        direction: .Forward,
        animated: true,
        completion: { (finished) -> Void in
            // Setting the view controller programmatically does not fire
            // any delegate methods, so we have to manually notify the
            // 'tutorialDelegate' of the new index.
            self.notifyTutorialDelegateOfNewIndex()
    })
}

/**
 Notifies '_tutorialDelegate' that the current page index was updated.
 */
private func notifyTutorialDelegateOfNewIndex() {
    if let firstViewController = viewControllers?.first,
        let index = orderedViewControllers.indexOf(firstViewController) {
            tutorialDelegate?.tutorialPageViewController(self,
                didUpdatePageIndex: index)
    }
}

}

// MARK: UIPageViewControllerDataSource

extension TutorialPageViewController: UIPageViewControllerDataSource {

func pageViewController(pageViewController: UIPageViewController,
    viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
            return nil
        }

        let previousIndex = viewControllerIndex - 1

        // User is on the first view controller and swiped left to loop to
        // the last view controller.
        guard previousIndex >= 0 else {
            return orderedViewControllers.last
        }

        guard orderedViewControllers.count > previousIndex else {
            return nil
        }

        return orderedViewControllers[previousIndex]
}

func pageViewController(pageViewController: UIPageViewController,
    viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
            return nil
        }

        let nextIndex = viewControllerIndex + 1
        let orderedViewControllersCount = orderedViewControllers.count

        // User is on the last view controller and swiped right to loop to
        // the first view controller.
        guard orderedViewControllersCount != nextIndex else {
            return orderedViewControllers.first
        }

        guard orderedViewControllersCount > nextIndex else {
            return nil
        }

        return orderedViewControllers[nextIndex]
}

}

extension TutorialPageViewController: UIPageViewControllerDelegate {

func pageViewController(pageViewController: UIPageViewController,
    didFinishAnimating finished: Bool,
    previousViewControllers: [UIViewController],
    transitionCompleted completed: Bool) {
    notifyTutorialDelegateOfNewIndex()
}

}

protocol TutorialPageViewControllerDelegate: class {

/**
 Called when the number of pages is updated.

 - parameter tutorialPageViewController: the TutorialPageViewController instance
 - parameter count: the total number of pages.
 */
func tutorialPageViewController(tutorialPageViewController: TutorialPageViewController,
    didUpdatePageCount count: Int)

/**
 Called when the current index is updated.

 - parameter tutorialPageViewController: the TutorialPageViewController instance
 - parameter index: the index of the currently visible page.
 */
func tutorialPageViewController(tutorialPageViewController: TutorialPageViewController,
    didUpdatePageIndex index: Int)

}

我试过的

我试过像这样首先声明视图控制器:

let vc0 = GreenViewController(nibName: "GreenViewController", bundle: nil)

然后像这样传递数据:

override func viewDidLoad() {
   vc0.passedVariable = "This was passed, Dance with Joy"
}

控制台中未打印任何内容。

我还尝试将上面的包更改为:

bundle: NSBundle.mainBundle()

仍然虚无

问题

我计划通过 alamofire 请求在教程ViewController 上加载数据,我想将该数据传递给 ViewController 之一(绿色、蓝色、红色)

如何将从教程中获取的数据ViewController传递给将加载的子视图之一?

首先,我要感谢您查看我的教程以及您对它说的所有好话。

其次,我有一个解决方案给你!我继续 committed the solution to the GitHub repo 我 link 编写了教程。我也会post这里的代码。

(1) 创建一个 UIViewController subclass 来添加自定义属性。对于此示例,我选择添加一个 UILabel,因为它在 运行 应用程序时最容易查看。

class ColoredViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

}

(2) 在 Main.storyboard 内,在身份检查器中将每个 UIViewController "page" 的自定义 class 更改为 ColoredViewController

(3) 向每个 "page" 添加一个 UILabel 并根据需要对其进行约束。我选择将它垂直和水平居中放置在容器中。不要忘记 link 的 UILabelColoredViewController@IBOutlet weak var label: UILabel!.

(4) 可选:如果我们从不在代码中设置标签的文本,我会以这种方式删除每个文本中的默认 "Label" 文本,我们将不会向用户显示 "Label"。

(5) 我们需要对 TutorialPageViewController 进行一些 TLC,以便它知道 orderedViewControllers 现在是一个 ColoredViewController 数组。为了方便起见,我将粘贴整个 class:

class TutorialPageViewController: UIPageViewController {

    weak var tutorialDelegate: TutorialPageViewControllerDelegate?

    private(set) lazy var orderedViewControllers: [ColoredViewController] = {
        // The view controllers will be shown in this order
        return [self.newColoredViewController("Green"),
            self.newColoredViewController("Red"),
            self.newColoredViewController("Blue")]
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource = self
        delegate = self

        if let initialViewController = orderedViewControllers.first {
            scrollToViewController(initialViewController)
        }

        tutorialDelegate?.tutorialPageViewController(self,
            didUpdatePageCount: orderedViewControllers.count)
    }

    /**
     Scrolls to the next view controller.
     */
    func scrollToNextViewController() {
        if let visibleViewController = viewControllers?.first,
            let nextViewController = pageViewController(self,
                viewControllerAfterViewController: visibleViewController) {
                    scrollToViewController(nextViewController)
        }
    }

    private func newColoredViewController(color: String) -> ColoredViewController {
        return UIStoryboard(name: "Main", bundle: nil) .
            instantiateViewControllerWithIdentifier("\(color)ViewController") as! ColoredViewController
    }

    /**
     Scrolls to the given 'viewController' page.

     - parameter viewController: the view controller to show.
     */
    private func scrollToViewController(viewController: UIViewController) {
        setViewControllers([viewController],
            direction: .Forward,
            animated: true,
            completion: { (finished) -> Void in
                // Setting the view controller programmatically does not fire
                // any delegate methods, so we have to manually notify the
                // 'tutorialDelegate' of the new index.
                self.notifyTutorialDelegateOfNewIndex()
        })
    }

    /**
     Notifies '_tutorialDelegate' that the current page index was updated.
     */
    private func notifyTutorialDelegateOfNewIndex() {
        if let firstViewController = viewControllers?.first as? ColoredViewController,
            let index = orderedViewControllers.indexOf(firstViewController) {
                tutorialDelegate?.tutorialPageViewController(self,
                    didUpdatePageIndex: index)
        }
    }

}

// MARK: UIPageViewControllerDataSource

extension TutorialPageViewController: UIPageViewControllerDataSource {

    func pageViewController(pageViewController: UIPageViewController,
        viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
            guard let coloredViewController = viewController as? ColoredViewController,
                let viewControllerIndex = orderedViewControllers.indexOf(coloredViewController) else {
                return nil
            }

            let previousIndex = viewControllerIndex - 1

            // User is on the first view controller and swiped left to loop to
            // the last view controller.
            guard previousIndex >= 0 else {
                return orderedViewControllers.last
            }

            guard orderedViewControllers.count > previousIndex else {
                return nil
            }

            return orderedViewControllers[previousIndex]
    }

    func pageViewController(pageViewController: UIPageViewController,
        viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
            guard let coloredViewController = viewController as? ColoredViewController,
                let viewControllerIndex = orderedViewControllers.indexOf(coloredViewController) else {
                return nil
            }

            let nextIndex = viewControllerIndex + 1
            let orderedViewControllersCount = orderedViewControllers.count

            // User is on the last view controller and swiped right to loop to
            // the first view controller.
            guard orderedViewControllersCount != nextIndex else {
                return orderedViewControllers.first
            }

            guard orderedViewControllersCount > nextIndex else {
                return nil
            }

            return orderedViewControllers[nextIndex]
    }

}

extension TutorialPageViewController: UIPageViewControllerDelegate {

    func pageViewController(pageViewController: UIPageViewController,
        didFinishAnimating finished: Bool,
        previousViewControllers: [UIViewController],
        transitionCompleted completed: Bool) {
        notifyTutorialDelegateOfNewIndex()
    }

}

protocol TutorialPageViewControllerDelegate: class {

    /**
     Called when the number of pages is updated.

     - parameter tutorialPageViewController: the TutorialPageViewController instance
     - parameter count: the total number of pages.
     */
    func tutorialPageViewController(tutorialPageViewController: TutorialPageViewController,
        didUpdatePageCount count: Int)

    /**
     Called when the current index is updated.

     - parameter tutorialPageViewController: the TutorialPageViewController instance
     - parameter index: the index of the currently visible page.
     */
    func tutorialPageViewController(tutorialPageViewController: TutorialPageViewController,
        didUpdatePageIndex index: Int)

}

(6) 在 TutorialViewController 中:让我们设置 label.text。我选择使用 viewDidLoad,但可以随意将此逻辑填充到网络请求完成块中。

override func viewDidLoad() {
    super.viewDidLoad()

    if let greenColoredViewController = tutorialPageViewController?.orderedViewControllers.first {
        greenColoredViewController.label.text = "Hello world!"
    }
}

希望对您有所帮助!

显然,根据评论,如何解决这个问题仍然很困惑。

我将尝试介绍一种方法并解释方式这可能是有道理的。但请注意,还有一些其他可行的方法可以解决此问题。

根视图控制器

首先我们看一下 "root" 控制器,它是 TutorialViewController 的一个实例。这个负责fetch/get/obtain/retrieve个"model"。该模型是纯数据的实例。必须适当地定义和初始化页面视图控制器。因为我们有很多页面,所以这个模型是某种数组或某种对象的列表是有道理的。

对于这个例子,我使用了一个字符串数组 - 只是为了说明如何实现它。一个真实的例子将获得一组可能更复杂的对象,其中每个对象都将在其自己的页面中呈现。该数组可能是通过网络请求从远程资源中获取的。

在此示例中,字符串恰好是页面视图控制器的 "colour"。我们为 class TutorialViewController:

创建一个合适的 属性
class TutorialViewController: UIViewController {

    @IBOutlet weak var pageControl: UIPageControl!
    @IBOutlet weak var containerView: UIView!


    private let model = ["Red", "Green", "Blue"]

    ...

请注意 属性 具有 私有 访问权限:除了 class 本身,没有其他人应该 fiddle 与它在一起。

将模型从根控制器传递到其嵌入式视图控制器

嵌入式视图控制器是TutorialPageViewController的一个实例。

根视图控制器在方法 prepareForSegue 中将模型传递给嵌入式视图控制器。嵌入式视图控制器必须有一个合适的属性适合它的模型视图。

Note: A model may have several aspects or views. The model which has been initialised by the root view controller may not be appropriate to be passed as is to any of its presented view controllers. Thus, the root view controller may first filter, copy, reorder, or transform its model in order to make it suitable for the presented view controller.

在这里,在这个例子中,我们采用模型原样:

在class TutorialViewController:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let tutorialPageViewController = segue.destinationViewController as? TutorialPageViewController {
        self.tutorialPageViewController = tutorialPageViewController
        self.tutorialPageViewController!.model = self.model
    }
}

请注意,TutorialViewController 本身有一个 属性(这里是 model),它由呈现视图控制器设置。

在这里,模型是一个字符串数组。很明显,数组中元素的数量稍后应该成为页面视图控制器中的页面数量。还应该清楚的是,每个元素都呈现在内容视图控制器中的相应页面上。因此,我们可以说数组中的一个元素作为每个页面的 "model"。

我们需要在TutorialPageViewController中提供属性model:

class TutorialPageViewController: UIPageViewController {

    internal var model: [String]?

请注意,访问权限是 public 或内部访问权限,以便任何呈现视图控制器都可以设置它。

将模型从 TutorialViewController 传递到每个内容视图控制器

页面视图控制器 (TutorialViewController) 负责创建内容视图控制器数组,其视图呈现页面。

使用惰性 属性 创建视图控制器数组的简单方法如下所示:

class TutorialPageViewController: UIPageViewController {

    internal var model: [String]?

    private(set) lazy var orderedViewControllers: [UIViewController] = {
        // The view controllers will be shown in this order
        assert(self.model != nil)
        return self.model!.map {
            self.newColoredViewController([=13=])
        }
    }()

重要的部分在这里:

        return self.model!.map {
            self.newColoredViewController([=14=])
        }

在这里,我们创建 N 视图控制器,在其工厂函数中将模型(字符串)传递给它。

map returns 一组视图控制器 - 适用于页面视图控制器。

实施后,该示例将以其原始形式运行。

您现在可以更改 "factory" 函数,该函数在给定字符串作为参数的情况下创建视图控制器。例如,您可以设置一个标签:

private func newColoredViewController(color: String) -> UIViewController {
    let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("MyContentViewController") as! MyContentViewController
    vc.label = color 
    return vc
}

这里,再次 label 是视图控制器的 "model"。 label 将如何呈现完全取决于视图控制器 - 如果有的话。

根据一些评论,我发现@Jeff 的回答中缺少一个小片段,这有助于阐明如何将数据从 TutorialPageViewController 实际传输到 ColoredViewController。他可能认为这部分答案是推断出来的。但是,如果您不知道从这里做什么,这可能会令人沮丧。

话虽这么说,但我要从他的回答中汲取灵感。比方说,我们想要更改 TutorialPageViewControllerColoredViewController 中的标签文本。我们会将文本值设置为该特定视图控制器的背景颜色。

1) 首先在 ColoredViewController class 中定义变量并将标签文本设置为该值。

class ColoredViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    var labelText: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        if let text = labelText {
            label.text = text
        }

        // Do any additional setup after loading the view.
    }
}

2) 在我们已经在 TutorialPageViewController class

中创建的 newColoredViewController 方法中设置 labelText 的值
private func newColoredViewController(color: String) -> ColoredViewController {
        let newController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "\(color)ViewController") as! ColoredViewController
        newController.labelText = label
        return newController
}

视图控制器中先前为空的标签现在将显示颜色值。

Note: You don't necessarily have to have 3 separate view controllers for this example to apply to your particular application. You could have 1 view controller that is serving as a template for each page within the page view controller. In this case, you would not reference the new view controller in the newColoredViewController method with a variable value but instead with the static name of the one content view controller you want to use.