PageViewController - 将变量传递给子视图

时间:2016-02-28 10:54:30

标签: ios swift uipageviewcontroller

我拥有什么

我有一个ViewController(TutorialViewController)和一个UIPageViewController(TutorialPageViewController)。使用StoryBoard ID的故事板还有3个额外的视图:

  • GreenViewController
  • BlueViewController
  • RedViewController

我一直在关注tutorial(感谢作者,写得非常好。)

在绿色视图控制器上我定义了一个变量:

var passedVariable = ""

在ViewDidLoad中我打印出来。

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

UIViewController(TutorialViewController):

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)

}

我尝试了什么

我已尝试首先声明View Controller:

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

然后像这样传递数据:

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

控制台中没有任何内容正在打印出来。

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

bundle: NSBundle.mainBundle()

仍然是nada

问题

我计划从alamofire请求加载TutorialViewController上的数据,我想将该数据传递给ViewControllers之一(绿色,蓝色,红色)

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

3 个答案:

答案 0 :(得分:2)

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

其次,我有一个解决方案给你!我继续前进committed the solution to the GitHub repo我在教程中链接了。我也会在这里发布代码。

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

class ColoredViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

}

(2)在Main.storyboard内,在Identity Inspector中将每个UIViewController“页面”的自定义类更改为ColoredViewController

Custom Class Set To ColoredViewController

(3)在每个“页面”中添加UILabel并根据需要对其进行约束。我选择垂直和水平地将它放在容器中。不要忘记将UILabelColoredViewController的{​​{1}}相关联。

UILabel On Each Page

(4)可选:如果我们从未在代码中设置标签的文本,我会删除每个文本中的默认“标签”文本,我们不会向用户显示“标签”。

Deleted Label Default Text

(5)我们需要对@IBOutlet weak var label: UILabel!进行一些TLC,因此它知道TutorialPageViewController现在是orderedViewControllers数组。为了简单起见,我只是要粘贴整个班级:

ColoredViewController

(6)在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) } 内:让我们设置TutorialViewController。我选择使用label.text,但可以随意将此逻辑填入网络请求完成块。

viewDidLoad

希望这有帮助!

答案 1 :(得分:0)

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

我会尝试引入一个方法并解释方式这可能有意义。但请注意,还有一些其他可行的方法可以解决这个问题。

根视图控制器

首先我们来看看" root" controller是TutorialViewController的一个实例。这个负责获取/获取/获取/检索"模型"。该模型是纯数据的实例。必须适合定义和初始化页面视图控制器。由于我们有许多页面,因此这个模型是某种数组或某种对象的列表是有道理的。

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

在这个例子中,字符串碰巧是"颜色"页面视图控制器。我们为类TutorialViewController创建了一个合适的属性:

class TutorialViewController: UIViewController {

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


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

    ...

请注意,该属性具有私有访问权限:除了类本身之外,没有其他人可以使用它。

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

embedded 视图控制器是TutorialPageViewController的实例。

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

  

注意:模型可能有多个方面或视图。由根视图控制器初始化的模型可能不适合传递给任何呈现的视图控制器。因此,根视图控制器可以首先对其模型进行过滤,复制,重新排序或转换,以使其适合于所呈现的视图控制器。

在这个例子中,我们将模型原样

在课程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
class TutorialPageViewController: UIPageViewController {

    internal var model: [String]?

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

将模型从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($0)
        }
    }()

重要的部分在这里:

        return self.model!.map {
            self.newColoredViewController($0)
        }

在这里,我们创建N个视图控制器,在其工厂函数中传递模型(String)。

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

实现此功能后,该示例的工作方式与其原始形式相同。

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

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

此处,再次label是"模型"视图控制器。它完全取决于视图控制器如何呈现label - 如果有的话。

答案 2 :(得分:0)

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

话虽如此,我要回避他的回答。例如,假设我们要从ColoredViewController更改TutorialPageViewController内标签的文本。我们将文本值设置为该特定视图控制器的背景色。

1)首先在ColoredViewController类中定义变量,然后将标签文本设置为该值。

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)在我们已经在labelText类内部创建的newColoredViewController方法中设置TutorialPageViewController的值

private func newColoredViewController(color: String) -> ColoredViewController {
        let newController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "\(color)ViewController") as! ColoredViewController
        newController.labelText = label
        return newController
}

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

  

注意:对于本示例,您不必具有3个单独的视图控制器即可应用到您的特定应用程序。您可能有1个视图控制器作为页面视图控制器中每个页面的模板。在这种情况下,您不会在newColoredViewController方法中使用变量值引用新的视图控制器,而是使用您要使用的一个内容视图控制器的静态名称。