Swift ios 不确定如何连接或使用嵌入式 UIPageViewController
Swift ios not sure on how to connect or use an embedded UIPageViewController
我决定放弃我的 UIImageView 并使用 UIPageViewController 以便用户可以在同一页面上的图像之间滑动。
我现在拥有的是一个普通的 UIViewController,因为我添加了一个 containerView,其中嵌入了一个 UIPageViewController。
因此 UIPageViewController 充当 viewcontroller 的子视图。
我有一个 "details" 页面,其中包含有关产品 X 的一些信息。我不希望只显示一张图片,而是希望图片 "swipeable" 以便用户可以看到不同的图片。但我不希望 PageControllerView 充当全尺寸视图,图像应该始终在 viewcontroller 中可见。
我现在的问题是卡住了。我不知道如何连接两个控制器。因此,如果有人有使用 swift 嵌入 segues/child 视图的示例 app/tutorial,请分享。
感谢下面的回答,我找到了解决方案:
在我的故事板中,我有 3 个 VC。 VC1 带有与 UIPageControllerView 有连接的嵌入式容器视图,然后我添加了第三个 VC "content view controller" 来显示 UIPageControllerView 的图像。
ViewController.swift
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageTitles: NSArray!
var pageImages: NSArray!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func restartAction(sender: AnyObject)
{
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
}
func viewControllerAtIndex(index: Int) -> ContentViewController
{
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
return ContentViewController()
}
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.imageFile = self.pageImages[index] as! String
vc.titleText = self.pageTitles[index] as! String
vc.pageIndex = index
return vc
}
// MARK: - Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound)
{
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == NSNotFound)
{
return nil
}
index++
if (index == self.pageTitles.count)
{
return nil
}
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return self.pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 0
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "PageViewControllerFitta" {
self.pageTitles = NSArray(objects: "Explore", "Today Widget")
self.pageImages = NSArray(objects: "page1", "page2")
let toView = segue.destinationViewController as! UIPageViewController
toView.dataSource = self
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
toView.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
toView.view.frame = CGRectMake(0, 30, self.view.frame.width, self.view.frame.size.height - 60)
self.addChildViewController(toView)
self.view.addSubview(toView.view)
toView.didMoveToParentViewController(self)
}
}
}
ContentViewController.Swift
import UIKit
class ContentViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
var pageIndex: Int!
var imageFile: String!
override func viewDidLoad()
{
super.viewDidLoad()
self.imageView.image = UIImage(named: self.imageFile)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
嵌入实际上是一种在实例化包含控制器后立即发生的转场,因此实现此目的的方法是在包含控制器上实现 prepareForSegue
,并捕获对子控制器的引用该方法中的控制器。这是一个改编自我目前正在从事的项目的示例,它可能无法编译,但应该让您了解如何继续进行——如果您需要更多信息,请告诉我。
为了让它工作,你必须给 segue 属性 一个 identifier
。假设您正在使用故事板,这只是 segue 本身的一个 属性(位于连接父和子 UIViewController 的箭头上的小圆形过渡图标)。
class ViewController: UIViewController { // <-- this is the container
var myEmbeddedViewController: MyCustomUIViewController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segue.identifier {
case .Some("the-identifier-i-used-for-the-segue"):
myEmbeddedViewController = segue.destinationViewController as? MyCustomUIViewController
default:
break
}
}
// ... other UIViewController stuff...
}
尽管在 Objective-C 而不是 Swift 中提供,但这是另一个涉及一些细节和文档的答案:iOS Nested View Controllers view inside UIViewController's view?
现在您已经有了对您的 UIPageControllerView
的引用,它需要一种方法来回答一些用于显示目的的问题:有多少页,我们打开到什么页,我什么时候显示什么页用户滑动到下一页和上一页。它使用一个常见的 UIKit/Cocoa 惯用语 dataSource
来做到这一点(很多视图控制器都有这个 属性)。
在您 link 的示例代码中,viewDidLoad
方法包含行 self.pageViewController.dataSource = self
。这是可行的,因为作者声明他的 ViewController
除了扩展 UIViewController
外,还符合 UIPageViewControllerDataSource
协议。然后他实现了所需的方法:
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
(dataSource
不必是您的 ViewController
,您可以创建一个单独的 class 或结构。不过,对于简单的示例,它可能没问题。)
您面临的具体挑战是根据您的应用程序的要求实施这些方法,以便嵌入式 UIPageViewController
可以根据您设置的内容确定要显示的内容 dataSource
。
假设您希望它显示一个 ProductImageViewController
,它只需要一张图片和一个标签。从理论上讲,您可以为每个产品创建故事板,但除非您拥有少量永不更改的产品,否则这不会真正奏效。更有可能的是,您希望根据您的产品列表在代码中创建新实例。
在示例代码中,作者只是设置了一个标签和图像的静态数组,然后 pageViewController
方法只是根据当前位置和滑动方向从数组中抓取适当的一个。您想要设置一个数据源来了解您的产品列表,您可能从一些动态来源(如 CoreData、CloudKit 或其他一些 Web 服务)获得该列表。
作为一个让您自己熟悉的简单实验,请尝试修改该演示的变量 self.pageTitles
和 self.pageImages
,看看这会如何改变嵌入式控制器的行为。尝试修改该代码,而不是使用两个数组,一个文本和一个图像文件名,而是使用包含您定义的结构或 class 的单个数组,它包含标签文本和文件名.一旦它开始工作,改变它而不是对 labels/images 的列表进行硬编码,而是从您的产品目录加载它。那是什么在起作用,你已经完成了,发货:)
我决定放弃我的 UIImageView 并使用 UIPageViewController 以便用户可以在同一页面上的图像之间滑动。
我现在拥有的是一个普通的 UIViewController,因为我添加了一个 containerView,其中嵌入了一个 UIPageViewController。
因此 UIPageViewController 充当 viewcontroller 的子视图。
我有一个 "details" 页面,其中包含有关产品 X 的一些信息。我不希望只显示一张图片,而是希望图片 "swipeable" 以便用户可以看到不同的图片。但我不希望 PageControllerView 充当全尺寸视图,图像应该始终在 viewcontroller 中可见。
我现在的问题是卡住了。我不知道如何连接两个控制器。因此,如果有人有使用 swift 嵌入 segues/child 视图的示例 app/tutorial,请分享。
感谢下面的回答,我找到了解决方案:
在我的故事板中,我有 3 个 VC。 VC1 带有与 UIPageControllerView 有连接的嵌入式容器视图,然后我添加了第三个 VC "content view controller" 来显示 UIPageControllerView 的图像。
ViewController.swift
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageTitles: NSArray!
var pageImages: NSArray!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func restartAction(sender: AnyObject)
{
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
}
func viewControllerAtIndex(index: Int) -> ContentViewController
{
if ((self.pageTitles.count == 0) || (index >= self.pageTitles.count)) {
return ContentViewController()
}
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.imageFile = self.pageImages[index] as! String
vc.titleText = self.pageTitles[index] as! String
vc.pageIndex = index
return vc
}
// MARK: - Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound)
{
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == NSNotFound)
{
return nil
}
index++
if (index == self.pageTitles.count)
{
return nil
}
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
{
return self.pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 0
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "PageViewControllerFitta" {
self.pageTitles = NSArray(objects: "Explore", "Today Widget")
self.pageImages = NSArray(objects: "page1", "page2")
let toView = segue.destinationViewController as! UIPageViewController
toView.dataSource = self
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
toView.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
toView.view.frame = CGRectMake(0, 30, self.view.frame.width, self.view.frame.size.height - 60)
self.addChildViewController(toView)
self.view.addSubview(toView.view)
toView.didMoveToParentViewController(self)
}
}
}
ContentViewController.Swift
import UIKit
class ContentViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
var pageIndex: Int!
var imageFile: String!
override func viewDidLoad()
{
super.viewDidLoad()
self.imageView.image = UIImage(named: self.imageFile)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
嵌入实际上是一种在实例化包含控制器后立即发生的转场,因此实现此目的的方法是在包含控制器上实现 prepareForSegue
,并捕获对子控制器的引用该方法中的控制器。这是一个改编自我目前正在从事的项目的示例,它可能无法编译,但应该让您了解如何继续进行——如果您需要更多信息,请告诉我。
为了让它工作,你必须给 segue 属性 一个 identifier
。假设您正在使用故事板,这只是 segue 本身的一个 属性(位于连接父和子 UIViewController 的箭头上的小圆形过渡图标)。
class ViewController: UIViewController { // <-- this is the container
var myEmbeddedViewController: MyCustomUIViewController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segue.identifier {
case .Some("the-identifier-i-used-for-the-segue"):
myEmbeddedViewController = segue.destinationViewController as? MyCustomUIViewController
default:
break
}
}
// ... other UIViewController stuff...
}
尽管在 Objective-C 而不是 Swift 中提供,但这是另一个涉及一些细节和文档的答案:iOS Nested View Controllers view inside UIViewController's view?
现在您已经有了对您的 UIPageControllerView
的引用,它需要一种方法来回答一些用于显示目的的问题:有多少页,我们打开到什么页,我什么时候显示什么页用户滑动到下一页和上一页。它使用一个常见的 UIKit/Cocoa 惯用语 dataSource
来做到这一点(很多视图控制器都有这个 属性)。
在您 link 的示例代码中,viewDidLoad
方法包含行 self.pageViewController.dataSource = self
。这是可行的,因为作者声明他的 ViewController
除了扩展 UIViewController
外,还符合 UIPageViewControllerDataSource
协议。然后他实现了所需的方法:
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
(dataSource
不必是您的 ViewController
,您可以创建一个单独的 class 或结构。不过,对于简单的示例,它可能没问题。)
您面临的具体挑战是根据您的应用程序的要求实施这些方法,以便嵌入式 UIPageViewController
可以根据您设置的内容确定要显示的内容 dataSource
。
假设您希望它显示一个 ProductImageViewController
,它只需要一张图片和一个标签。从理论上讲,您可以为每个产品创建故事板,但除非您拥有少量永不更改的产品,否则这不会真正奏效。更有可能的是,您希望根据您的产品列表在代码中创建新实例。
在示例代码中,作者只是设置了一个标签和图像的静态数组,然后 pageViewController
方法只是根据当前位置和滑动方向从数组中抓取适当的一个。您想要设置一个数据源来了解您的产品列表,您可能从一些动态来源(如 CoreData、CloudKit 或其他一些 Web 服务)获得该列表。
作为一个让您自己熟悉的简单实验,请尝试修改该演示的变量 self.pageTitles
和 self.pageImages
,看看这会如何改变嵌入式控制器的行为。尝试修改该代码,而不是使用两个数组,一个文本和一个图像文件名,而是使用包含您定义的结构或 class 的单个数组,它包含标签文本和文件名.一旦它开始工作,改变它而不是对 labels/images 的列表进行硬编码,而是从您的产品目录加载它。那是什么在起作用,你已经完成了,发货:)