[英]UIPageViewController and storyboard
我正在嘗試從情節提要中特別配置 UIPageViewController:
TutorialPageViewController.h
#import <UIKit/UIKit.h>
@interface TutorialPageViewController : UIPageViewController <UIPageViewControllerDelegate, UIPageViewControllerDataSource>
@end
TutorialPageViewController.m
#import "TutorialPageViewController.h"
@interface TutorialPageViewController ()
@property (assign, nonatomic) NSInteger index;
@end
@implementation TutorialPageViewController
{
NSArray *myViewControllers;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.delegate = self;
self.dataSource = self;
[self didMoveToParentViewController:self];
UIStoryboard *tutorialStoryboard = [UIStoryboard storyboardWithName:@"TutorialStoryboard" bundle:[NSBundle mainBundle]];
UIViewController *tuto1 = [tutorialStoryboard instantiateViewControllerWithIdentifier:@"TutorialPageViewController_1"];
UIViewController *tuto2 = [tutorialStoryboard instantiateViewControllerWithIdentifier:@"TutorialPageViewController_2"];
myViewControllers = @[tuto1, tuto2, tuto1, tuto2];
self.index = 0;
[self setViewControllers:@[tuto1] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index {
return myViewControllers[index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = self.index;
if (index == 0) { return nil; }
// Decrease the index by 1 to return
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = self.index;
index++;
if (index > [myViewControllers count]) { return nil; }
return [self viewControllerAtIndex:index];
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
// The number of items reflected in the page indicator.
return [myViewControllers count];
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
// The selected item reflected in the page indicator.
return 0;
}
@end
如今,只需使用 Storyboard就可以輕松做到這一點。
這種“滑動全屏教程”作為應用程序“介紹”流行了一段時間,所以我將下面的類稱為IntroPages
。
第 1 步,創建一個 UIPageViewController容器視圖。
如果是 iOS 新手,這里有一個容器視圖教程。
(注意:如果您不知道如何將容器視圖“更改”為 UIPageViewController,請向下滾動到該教程中的“如何更改...”部分!
你可以把容器做成任何你想要的形狀。 與任何容器視圖一樣,它可以是全屏或屏幕的一小部分 - 無論您想要什么。
第2步,
制作四個簡單、普通的視圖控制器,它們可以是你想要的任何東西——圖像、文本、表格,任何東西。 (示例中為紫色。)
請注意,它們只是坐在你的故事板上,不要將它們鏈接到任何東西。
第 3 步,您必須設置這四個頁面的 ID 。 “id1”、“id2”、“id3”、“id4”都可以。
第四步,復制粘貼! 這是 IntroPages 類,
class IntroPages: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pages = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
let p1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id1")
let p2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id2")
let p3: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id3")
// etc ...
pages.append(p1)
pages.append(p2)
pages.append(p3)
// etc ...
setViewControllers([p1], direction: UIPageViewController.NavigationDirection.forward, animated: false, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController)-> UIViewController? {
let cur = pages.firstIndex(of: viewController)!
// if you prefer to NOT scroll circularly, simply add here:
// if cur == 0 { return nil }
var prev = (cur - 1) % pages.count
if prev < 0 {
prev = pages.count - 1
}
return pages[prev]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController)-> UIViewController? {
let cur = pages.firstIndex(of: viewController)!
// if you prefer to NOT scroll circularly, simply add here:
// if cur == (pages.count - 1) { return nil }
let nxt = abs((cur + 1) % pages.count)
return pages[nxt]
}
func presentationIndex(for pageViewController: UIPageViewController)-> Int {
return pages.count
}
}
(查看注釋 - 根據您的喜好,有用於循環或線性分頁的代碼。)
在故事板上查看 UIPageViewController。 將類設置為IntroPages
。
這就是它的全部 - 你已經完成了。
您只需在情節提要上設置過渡樣式,
您很可能想要“滾動”,而不是另一個。
你完成了! 現在 ...
在上圖UIPageControl
中的最高級別包裝類“Intro”中添加UIPageControl
。
(因此,令人驚訝的是不在頁面視圖控制器中,不在“IntroPages”中。)
因此,在情節UIPageControl
上,非常簡單地將UIPageControl
拖到“Intro”上。
筆記! 奇怪的是,在故事板中,您不能移動 UIPageControl 。
當您將頁面控件拖到視圖控制器上時,它只是位於固定位置。 這完全是奇怪的,但事情就是這樣。 不要浪費時間試圖移動它:)
從 Intro 開始,您需要以通常的方式訪問頁面視圖控制器 (IntroPages):
class Intro: UIViewController {
@IBOutlet var pageControl: UIPageControl!
var pageViewController: IntroPages!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueIntroPages" {
// if new to container views, identifier explained here:
// https://stackoverflow.com/a/23403979/294884
pageViewController = (segue.destination as! IntroPages)
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.bringSubviewToFront(pageControl)
pageControl.currentPage = 0
}
注意這個關鍵行:
view.bringSubviewToFront(pageControl)
添加這個功能
@IBAction func userDidChangePageControl(_ sender: UIPageControl) {}
然后在情節提要中單擊頁面控件並將valueChanged拖動到該函數。
該函數最簡單的輪廓版本是
@IBAction func userDidChangePageControl(_ sender: UIPageControl) {
pageViewController.setViewControllers(
[pageViewController.pages[newIndex]],
direction: (pageViewController.currentIndex < newIndex)
? .forward : .reverse,
animated: true, completion: nil)
}
但是,請參閱此 QA正確解決新的快速單擊動畫模式/避免新 UIPageControl 的半頁錯誤,按照 Apple進行更全面的調查。
在 IntroPages 添加屬性...
var currentIndex: Int {
if let visibleViewController = viewControllers?.first,
let ci = pages.firstIndex(of: visibleViewController) {
return ci
}
else {
return 0
}
}
在 IntroPages 中還添加以下內容,以便當用戶滑動屏幕時 UIPageControl 知道要做什么:
func pageViewController(_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool) {
(parent as? Intro)?.pageControl.currentPage = currentIndex
}
由於 Apple 錯誤/異常行為,在 IntroPages 中還添加了以下內容:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let subViews = view.subviews
var scrollView: UIScrollView? = nil
var pageControl: UIPageControl? = nil
// maintain this code order...
for view in subViews {
if view.isKind(of: UIScrollView.self) {
scrollView = view as? UIScrollView
}
else if view.isKind(of: UIPageControl.self) {
pageControl = view as? UIPageControl
}
}
// maintain this code order...
if (scrollView != nil && pageControl != nil) {
scrollView?.frame = view.bounds
if let pageControl = pageControl {
view.bringSubviewToFront(pageControl) }
}
}
你在路上。
對於想要查看工作頁面滾動(向前/向后)的人
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger currentIndex = [myViewControllers indexOfObject:viewController];
// get the index of the current view controller on display
if (currentIndex > 0)
{
return [myViewControllers objectAtIndex:currentIndex-1];
// return the previous viewcontroller
} else
{
return nil;
// do nothing
}
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger currentIndex = [myViewControllers indexOfObject:viewController];
// get the index of the current view controller on display
// check if we are at the end and decide if we need to present
// the next viewcontroller
if (currentIndex < [myViewControllers count]-1)
{
return [myViewControllers objectAtIndex:currentIndex+1];
// return the next view controller
} else
{
return nil;
// do nothing
}
}
只是為了添加編輯器的這個很棒的答案,如果您更喜歡“循環”分頁,請執行以下操作:仍然使用相同的編輯器技術
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger currentIndex = [myViewControllers indexOfObject:viewController];
--currentIndex;
currentIndex = currentIndex % (myViewControllers.count);
return [myViewControllers objectAtIndex:currentIndex];
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger currentIndex = [myViewControllers indexOfObject:viewController];
++currentIndex;
currentIndex = currentIndex % (myViewControllers.count);
return [myViewControllers objectAtIndex:currentIndex];
}
使用UIPageViewController
類的 Swift 代碼擴展Joe Blow 的回答:
import UIKit
class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pages = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
let page1: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page1")
let page2: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page2")
pages.append(page1)
pages.append(page2)
setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.indexOf(viewController)!
let previousIndex = abs((currentIndex - 1) % pages.count)
return pages[previousIndex]
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.indexOf(viewController)!
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return pages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
閱讀更多關於使用 UIPageViewController 和帶有故事板設置的容器視圖。
似乎有很多關於 Storyboard 中 UIPageViewController 的問題。
下面是一些演示代碼,向您展示如何將故事板中的 UIPageViewController 作為獨立的全屏視圖或作為 UIContainerView 使用,如果您只想對屏幕的一小部分區域進行分頁。
為 Swift 3 更新:
class YourPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pages = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
let page1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page1")
let page2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page2")
pages.append(page1)
pages.append(page2)
setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
let previousIndex = abs((currentIndex - 1) % pages.count)
return pages[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
let nextIndex = abs((currentIndex + 1) % pages.count)
return pages[nextIndex]
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return pages.count
}
}
要使用@samwize 的答案無限回滾,您需要添加條件以檢查負值。 否則,您只需在第一頁和第二頁之間切換。 僅當您計划擁有兩頁以上的頁面時才需要這樣做。
func pageViewController(_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController? {
let currentIndex = pages.index(of: viewController)!
var previousIndex = (currentIndex - 1) % pages.count
if previousIndex < 0 {previousIndex = pages.count - 1}
return pages[previousIndex]
}
問題是您不正確地重用UIViewController
實例:
myViewControllers = @[tuto1, tuto2, tuto1, tuto2];
我建議你有一個NSMutableSet
作為可重用的UIViewController
實例池。
在viewControllerBeforeViewController:
和viewControllerBeforeViewController:
使用NSPredicate
搜索您的NSMutableSet
以找到parentViewController
等於nil
的UIViewController
。 如果你找到了,就退貨。 如果沒有,則實例化一個新的,將其添加到NSMutableSet
,然后返回它。
當您完成並且您的測試通過后,您可以將池提取到它自己的類中。
Swift 5 的答案:
import UIKit
class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var allViewControllers = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
let vc1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "viewController1")
let vc2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "viewController2")
allViewControllers = [vc1, vc2]
self.setViewControllers([vc1], direction: UIPageViewController.NavigationDirection.forward, animated: false)
}
// UIPageViewControllerDataSource
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?
{
let currentIndex = allViewControllers.firstIndex(of: viewController)!
return currentIndex == 0 ? nil : allViewControllers[currentIndex-1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
{
let currentIndex = allViewControllers.firstIndex(of: viewController)!
return currentIndex == allViewControllers.count-1 ? nil : allViewControllers[currentIndex+1]
}
func presentationCount(for pageViewController: UIPageViewController) -> Int
{
return allViewControllers.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int
{
return 0
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.