在 UIPageViewController 中获取用户滑动方向
Get user swiping direction in UIPageViewController
UIPageViewControllerDataSource
的viewControllerBeforeViewController
和viewControllerAfterViewController
这两个方法不告诉滑动的方向。
UIPageViewController
委托的方法transitionCompleted
对我们帮助不大。它只是告诉页面是否被完全滑动。
那么我应该使用什么方法来准确检测用户方向(左或右)?
可能这两个属性可能会有所帮助:
let directionForward = UIPageViewControllerNavigationDirection.Forward
let directionReverse = UIPageViewControllerNavigationDirection.Reverse
我的代码如下所示:
import UIKit
class ProView: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pageViewController: UIPageViewController?
let characterImages = ["character1", "character2", "character1", "character2", "character1", "character2", "character1", "character2"]
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControl()
character = 1
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// Forward, check if this IS NOT the last controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
// Check if there is another view
if itemController.itemIndex+1 < characterImages.count {
return getItemController(itemController.itemIndex+1)
}
return nil
}
// Check if this IS NOT the first controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
if itemController.itemIndex < 0 {
return getItemController(itemController.itemIndex-1)
}
return nil
}
private func getItemController(itemIndex: Int) -> PageItemController? {
if itemIndex < characterImages.count {
let pageItemController = self.storyboard!.instantiateViewControllerWithIdentifier("ItemController") as PageItemController
pageItemController.itemIndex = itemIndex
pageItemController.imageName = characterImages[itemIndex]
return pageItemController
}
return nil
}
func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as UIPageViewController
pageController.dataSource = self
pageController.delegate = self
if characterImages.count > 0 {
let firstController = getItemController(0)!
let startingViewControllers: NSArray = [firstController]
pageController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController?.didMoveToParentViewController(self)
}
func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.grayColor()
appearance.currentPageIndicatorTintColor = UIColor.whiteColor()
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return characterImages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
}
// BETA
func pageViewController(PageItemController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers pageViewController: [AnyObject],
transitionCompleted completed: Bool)
{
if (!completed)
{
// You do nothing because whatever page you thought
// the book was on before the gesture started is still the correct page
return;
}
// This is where you would know the page number changed and handle it appropriately
character = workaround
}
}
class PageItemController: UIViewController {
@IBOutlet weak var imageCharacterChoose: UIImageView!
var itemIndex: Int = 0
var imageName: String = "" {
didSet {
if let imageView = imageCharacterChoose {imageCharacterChoose.image = UIImage(named: imageName)
}
}
}
}
根据pbasdf所说,
var currentIndex: Int = 0
var nextIndex: Int = 0
func pageViewController(PageItemController: UIPageViewController,
willTransitionToViewControllers pageViewController: [AnyObject]) {
// pageViewController is the pending View Controller
nextIndex = currentIndex
// pageViewController...... how do I get its index?
}
无法确定用户是向前滑动还是向后滑动。
您需要使用这两种方法来提供前后的视图:
pageViewController:viewControllerBeforeViewController:
pageViewController:viewControllerAfterViewController:
您可以通过向 viewControllers 添加 属性 来跟踪索引,然后您可以比较 pageIndexes。
您应该同时使用 willTransitionToViewControllers:
和 didFinishAnimating:
委托方法来确定过渡是向前还是向后。在 class 的开头声明几个索引变量,例如 currentIndex
和 nextIndex
(均为 Int
)。
在willTransitionToViewControllers
方法中,设置nextIndex
等于挂起视图控制器的itemIndex
:
编辑
func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [AnyObject]) {
// the page view controller is about to transition to a new page, so take note
// of the index of the page it will display. (We can't update our currentIndex
// yet, because the transition might not be completed - we will check in didFinishAnimating:)
if let itemController = pendingViewControllers[0] as? PageItemController {
nextIndex = itemController.itemIndex
}
}
编辑结束
此时您可以确定过渡是向前还是向后:如果 nextIndex
> currentIndex
,则向前;如果 nextIndex
< currentIndex
则向后。然后在 didFinishAnimating
中,如果 completed
为真(所以它完成了到下一个视图控制器的转换),设置 currentIndex
等于 nextIndex
这样你就可以使用 currentIndex
无论您需要指出哪个页面当前在屏幕上:
编辑
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool) {
if (completed) {
// the page view controller has successfully transitioned to the next view
// controller, so we can update our currentIndex to the value we obtained
// in the willTransitionToViewControllers method:
currentIndex = nextIndex
}
}
请注意,这些方法的第一个参数是 pageViewController(UIPageViewController 的实例),而不是您当前代码中的 pageItemController
。
最后,请注意:您引用的枚举 (UIPageViewControllerNavigationDirection
) 仅在 setViewControllers(_, direction:)
方法中使用。
编辑结束
假设您有一个 ViewController 的本地数组,您可以使用这个 delegate
方法:
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let current = self.viewControllers?.first else {
// TODO handle error
return
}
guard let index = self.pages.firstIndex(of: current) else {
// TODO handle error
return
}
self.currentIndex = index
}
guard
语句不应失败,但以防万一....
就我而言,我只能使用 func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool)
来确定滑动方向。
方法如下。假设您已经有办法引用名为 currentIndex
的当前索引。在上面的方法中,你已经有了 previousViewControllers
的信息,所以你可以得到以前的索引 previousIndex
.
基本上,这是委托方法内部的实现:
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool) {
if completed {
if previousIndex < currentIndex { // forward
}
else if previousIndex > currentIndex { // reverse
}
}
}
首先在您的 viewControllerForIndex 方法中分配 viewcontroller.view.tag = index 以便每个 viewcontroller.view 都有一个分配值
有一个 currentIndex 属性,它最初分配给您在 pageviewcontroller
中实例化的 viewcontroller 的那个索引
现在在 didFinishAnimating 中执行以下操作
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if !completed {
return // return if animation is not completed
}
let pCont = bottomDataContainer?.viewControllers?[0] as? CustomVC //take
out current VC
if currentIndex < pCont?.view.tag ?? 0 {
topNavView.goToNextDate()
}else{
topNavView.goToPrevDate()
}
selectedIndex = pCont?.view.tag ?? 0
}
您可以通过实施 UIScrollViewDelegate
:
var lastScrollDirection: UIPageViewController.NavigationDirection?
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)
self.lastScrollDirection = translation.x < 0 ? .forward : .reverse
}
分配委托可能比较棘手。这是我在一个项目中使用的:
func assignScrollDelegate(rootView: UIView) {
for view in rootView.subviews {
if let scrollView = view as? UIScrollView {
scrollView.delegate = self
}
else {
assignScrollDelegate(rootView: view)
}
}
}
当您引用 UIPageViewController
时调用此方法。例如,当您的控制器加载时:
override func viewDidLoad() {
self.assignScrollDelegate(rootView: self.pageController.view)
}
UIPageViewControllerDataSource
的viewControllerBeforeViewController
和viewControllerAfterViewController
这两个方法不告诉滑动的方向。
UIPageViewController
委托的方法transitionCompleted
对我们帮助不大。它只是告诉页面是否被完全滑动。
那么我应该使用什么方法来准确检测用户方向(左或右)?
可能这两个属性可能会有所帮助:
let directionForward = UIPageViewControllerNavigationDirection.Forward
let directionReverse = UIPageViewControllerNavigationDirection.Reverse
我的代码如下所示:
import UIKit
class ProView: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pageViewController: UIPageViewController?
let characterImages = ["character1", "character2", "character1", "character2", "character1", "character2", "character1", "character2"]
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControl()
character = 1
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// Forward, check if this IS NOT the last controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
// Check if there is another view
if itemController.itemIndex+1 < characterImages.count {
return getItemController(itemController.itemIndex+1)
}
return nil
}
// Check if this IS NOT the first controller
func pageViewController(pageViewController: UIPageViewController,
viewControllerBeforeViewController ProView: UIViewController) -> UIViewController? {
let itemController = ProView as PageItemController
if itemController.itemIndex < 0 {
return getItemController(itemController.itemIndex-1)
}
return nil
}
private func getItemController(itemIndex: Int) -> PageItemController? {
if itemIndex < characterImages.count {
let pageItemController = self.storyboard!.instantiateViewControllerWithIdentifier("ItemController") as PageItemController
pageItemController.itemIndex = itemIndex
pageItemController.imageName = characterImages[itemIndex]
return pageItemController
}
return nil
}
func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as UIPageViewController
pageController.dataSource = self
pageController.delegate = self
if characterImages.count > 0 {
let firstController = getItemController(0)!
let startingViewControllers: NSArray = [firstController]
pageController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController?.didMoveToParentViewController(self)
}
func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.grayColor()
appearance.currentPageIndicatorTintColor = UIColor.whiteColor()
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return characterImages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
}
// BETA
func pageViewController(PageItemController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers pageViewController: [AnyObject],
transitionCompleted completed: Bool)
{
if (!completed)
{
// You do nothing because whatever page you thought
// the book was on before the gesture started is still the correct page
return;
}
// This is where you would know the page number changed and handle it appropriately
character = workaround
}
}
class PageItemController: UIViewController {
@IBOutlet weak var imageCharacterChoose: UIImageView!
var itemIndex: Int = 0
var imageName: String = "" {
didSet {
if let imageView = imageCharacterChoose {imageCharacterChoose.image = UIImage(named: imageName)
}
}
}
}
根据pbasdf所说,
var currentIndex: Int = 0
var nextIndex: Int = 0
func pageViewController(PageItemController: UIPageViewController,
willTransitionToViewControllers pageViewController: [AnyObject]) {
// pageViewController is the pending View Controller
nextIndex = currentIndex
// pageViewController...... how do I get its index?
}
无法确定用户是向前滑动还是向后滑动。
您需要使用这两种方法来提供前后的视图:
pageViewController:viewControllerBeforeViewController:
pageViewController:viewControllerAfterViewController:
您可以通过向 viewControllers 添加 属性 来跟踪索引,然后您可以比较 pageIndexes。
您应该同时使用 willTransitionToViewControllers:
和 didFinishAnimating:
委托方法来确定过渡是向前还是向后。在 class 的开头声明几个索引变量,例如 currentIndex
和 nextIndex
(均为 Int
)。
在willTransitionToViewControllers
方法中,设置nextIndex
等于挂起视图控制器的itemIndex
:
编辑
func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [AnyObject]) {
// the page view controller is about to transition to a new page, so take note
// of the index of the page it will display. (We can't update our currentIndex
// yet, because the transition might not be completed - we will check in didFinishAnimating:)
if let itemController = pendingViewControllers[0] as? PageItemController {
nextIndex = itemController.itemIndex
}
}
编辑结束
此时您可以确定过渡是向前还是向后:如果 nextIndex
> currentIndex
,则向前;如果 nextIndex
< currentIndex
则向后。然后在 didFinishAnimating
中,如果 completed
为真(所以它完成了到下一个视图控制器的转换),设置 currentIndex
等于 nextIndex
这样你就可以使用 currentIndex
无论您需要指出哪个页面当前在屏幕上:
编辑
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool) {
if (completed) {
// the page view controller has successfully transitioned to the next view
// controller, so we can update our currentIndex to the value we obtained
// in the willTransitionToViewControllers method:
currentIndex = nextIndex
}
}
请注意,这些方法的第一个参数是 pageViewController(UIPageViewController 的实例),而不是您当前代码中的 pageItemController
。
最后,请注意:您引用的枚举 (UIPageViewControllerNavigationDirection
) 仅在 setViewControllers(_, direction:)
方法中使用。
编辑结束
假设您有一个 ViewController 的本地数组,您可以使用这个 delegate
方法:
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let current = self.viewControllers?.first else {
// TODO handle error
return
}
guard let index = self.pages.firstIndex(of: current) else {
// TODO handle error
return
}
self.currentIndex = index
}
guard
语句不应失败,但以防万一....
就我而言,我只能使用 func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool)
来确定滑动方向。
方法如下。假设您已经有办法引用名为 currentIndex
的当前索引。在上面的方法中,你已经有了 previousViewControllers
的信息,所以你可以得到以前的索引 previousIndex
.
基本上,这是委托方法内部的实现:
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool) {
if completed {
if previousIndex < currentIndex { // forward
}
else if previousIndex > currentIndex { // reverse
}
}
}
首先在您的 viewControllerForIndex 方法中分配 viewcontroller.view.tag = index 以便每个 viewcontroller.view 都有一个分配值
有一个 currentIndex 属性,它最初分配给您在 pageviewcontroller
中实例化的 viewcontroller 的那个索引现在在 didFinishAnimating 中执行以下操作
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if !completed {
return // return if animation is not completed
}
let pCont = bottomDataContainer?.viewControllers?[0] as? CustomVC //take
out current VC
if currentIndex < pCont?.view.tag ?? 0 {
topNavView.goToNextDate()
}else{
topNavView.goToPrevDate()
}
selectedIndex = pCont?.view.tag ?? 0
}
您可以通过实施 UIScrollViewDelegate
:
var lastScrollDirection: UIPageViewController.NavigationDirection?
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)
self.lastScrollDirection = translation.x < 0 ? .forward : .reverse
}
分配委托可能比较棘手。这是我在一个项目中使用的:
func assignScrollDelegate(rootView: UIView) {
for view in rootView.subviews {
if let scrollView = view as? UIScrollView {
scrollView.delegate = self
}
else {
assignScrollDelegate(rootView: view)
}
}
}
当您引用 UIPageViewController
时调用此方法。例如,当您的控制器加载时:
override func viewDidLoad() {
self.assignScrollDelegate(rootView: self.pageController.view)
}