从 UIPageViewController and/or 其数据源获取 TRUE 当前页面
Getting the TRUE current page from a UIPageViewController and/or its DataSource
我在情节提要中创建了一个教程 window,其中包含两个视图,一个用于显示教程,另一个用作每页内容的模板。
某些元素是在 ViewDidLoad 事件中以编程方式编码的。
页面ViewController按要求 100% 工作,它显示了三页内容并允许前后轻扫而不会出现问题。
我已经以编程方式将 UIPageControl 添加到主 ViewController 但是我无法正确更新其 CurrentPage 值。访问数据源 PageIndex 值在来回滑动时给我奇怪的结果。
有没有可靠的方法可以准确知道显示了哪个页面?
或者想知道页面过渡往哪个方向移动,这样我就可以手动更新了?不完全确定如何从 pagecontrollers 'DidFinishAnimating' 事件访问 UIPageViewControllerNavigationDirection。
我的主视图控制器代码如下:
using Foundation;
using System;
using UIKit;
namespace Performance
{
partial class VCOnboardHome : UIViewController
{
const int pageCount = 3;
public UIPageViewController pvcOnboarding{ get; set; }
private OnboardingDataSource onboardDataSource;
UIStoryboard board;
public UIPageControl pgControlIndicator;
public VCOnboardHome (IntPtr handle) : base (handle)
{
}
/// <summary>
/// ViewDidLoad event method
/// </summary>
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
board = UIStoryboard.FromName ("Main", null);
// Programmatically create a PageView controller
pvcOnboarding = new UIPageViewController (UIPageViewControllerTransitionStyle.Scroll,
UIPageViewControllerNavigationOrientation.Horizontal,
UIPageViewControllerSpineLocation.None, 20f);
// PageView Controller datasource
var views = CreateViews ();
onboardDataSource = new OnboardingDataSource (views);
pvcOnboarding.DataSource = onboardDataSource;
pvcOnboarding.SetViewControllers (new UIViewController[] { views [0] },
UIPageViewControllerNavigationDirection.Forward,
false, null);
// Set PageView size
pvcOnboarding.View.Frame = View.Bounds;
// Add the page view control to this view controller
Add (pvcOnboarding.View);
// Create Page Control
var frame = UIScreen.MainScreen.Bounds;
pgControlIndicator = new UIPageControl ();
pgControlIndicator.Frame = new CoreGraphics.CGRect (20f, frame.Height - 60f, frame.Width - 40f, 40f);
pgControlIndicator.Pages = pageCount;
pgControlIndicator.UserInteractionEnabled = false;
Add (pgControlIndicator);
// Update the Page Control to indicate the current page we are showing.
// Only do this if the full page transition happened and not a partial page turn
pvcOnboarding.DidFinishAnimating += (sender, e) => {
foreach(UIViewController u in e.PreviousViewControllers){
// TODO - Not needed, remove once page control working
// u = the previous viewcontroller
}
if(e.Finished && e.Completed){
// Page transition completed
// Update Page Control here
}else{
// Incomplete page transition
}
};
}
// Content for each page
VCOnboardContentNew[] CreateViews ()
{
var pageData = new [] {
new ContentOnBoardData {
headerLblText = @"Page 1",
bodyContentText = @"Page 1 body text blah blah blah blah",
buttonText = @"Ok, next no. 1",
pageImage = UIImage.FromBundle("ios_images_v2/onboarding/icon-qr-code.png"),
currentPage = 1,
totalPages = 3
},
new ContentOnBoardData {
headerLblText = @"Page 2",
bodyContentText = @"Page 2 body text blah blah blah blah",
buttonText = @"Ok, next no. 2",
pageImage = UIImage.FromBundle("ios_images_v2/onboarding/icon-id-check.png"),
currentPage = 2,
totalPages = 3
},
new ContentOnBoardData {
headerLblText = @"Page 3",
bodyContentText = @"Page 3 body text blah blah blah blah",
buttonText = @"Ok, got it",
pageImage = null,
currentPage = 3,
totalPages = 3
}
};
var views = new VCOnboardContentNew[pageData.Length];
for (int i = 0; i < pageCount; i++) {
int pageIndex = i;
views [i] = (VCOnboardContentNew)board.InstantiateViewController ("sbid_onboardcontent");
views [i].PageIndex = pageIndex;
views [i].HeaderText = pageData [i].headerLblText;
views [i].ContentText = pageData [i].bodyContentText;
views [i].PageImage = pageData [i].pageImage;
views [i].CurrentPage = pageData [i].currentPage;
views [i].TotalPages = pageCount;
views [i].ButtonText = pageData [i].buttonText;
views [i].ButtonClicked += (s, e) => {
DismissViewController (true, null);
};
}
return views;
}
}
/// <summary>
/// Onboarding data source.
/// </summary>
class OnboardingDataSource : UIPageViewControllerDataSource
{
readonly VCOnboardContentNew[] _views;
public OnboardingDataSource (VCOnboardContentNew[] views)
{
_views = views;
}
/// <summary>
/// Gets the previous view controller.
/// </summary>
/// <returns>The previous view controller.</returns>
/// <param name="pageViewController">Page view controller.</param>
/// <param name="referenceViewController">Reference view controller.</param>
public override UIViewController GetPreviousViewController (UIPageViewController pageViewController, UIViewController referenceViewController)
{
int index = ((VCOnboardContentNew)referenceViewController).PageIndex;
bool controlCheck = (index <= 0);
UIViewController vcToReturn = controlCheck ? null : (_views [index - 1]);
return vcToReturn;
}
/// <summary>
/// Gets the next view controller.
/// </summary>
/// <returns>The next view controller.</returns>
/// <param name="pageViewController">Page view controller.</param>
/// <param name="referenceViewController">Reference view controller.</param>
public override UIViewController GetNextViewController (UIPageViewController pageViewController, UIViewController referenceViewController)
{
int index = ((VCOnboardContentNew)referenceViewController).PageIndex;
bool controlCheck = index + 1 >= _views.Length;
UIViewController vcToReturn = controlCheck ? null : _views [index + 1];
return vcToReturn;
}
}
/// <summary>
/// Content onboard data.
/// </summary>
struct ContentOnBoardData
{
public string headerLblText;
public string bodyContentText;
public string buttonText;
public UIImage pageImage;
public int currentPage;
public int totalPages;
}
}
我的页面内容代码视图控制器如下:
using System;
using UIKit;
namespace Performance
{
/// <summary>
/// Class: VCOnboardContentNew
/// </summary>
partial class VCOnboardContentNew : UIViewController
{
public EventHandler ButtonClicked;
public int PageIndex { get; set; }
public string HeaderText{ get; set; }
public UIImage PageImage{ get; set; }
public int CurrentPage{ get; set; }
public int TotalPages{ get; set; }
public string ContentText{ get; set; }
public string ButtonText{ get; set; }
public VCOnboardContentNew (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Set text for the main title
lblTest.Font = UIFont.FromName("FreightDispLight", 26f);
lblTest.Text = HeaderText;
// Set text for body content
lblContentBodyText.Font = UIFont.FromName("FreightDispLight", 14f);
lblContentBodyText.Text = ContentText;
pgCtrlWhichPageWeOn.Hidden = true;
if(PageImage != null){
pageContentImage.Hidden = false;
pageContentImage.Image = PageImage;
}else{
pageContentImage.Hidden = true;
}
btnCallToAction.SetTitle (ButtonText, UIControlState.Normal);
btnCallToAction.TouchUpInside += (object sender, EventArgs e) => {
if (ButtonClicked != null) {
ButtonClicked.Invoke (this, null);
}
};
}
}
}
当过渡样式设置为滚动时,页面视图控制器似乎:
- 一旦一个转换完成,它会立即调用下一个(或上一个)View Controller(
GetNextViewController
),即使用户可能没有开始下一次滑动;和
- 它保留从中转换过来的视图控制器,并假定它是之前的 VC,因此它不会调用之前的 VC(使用
GetPreviousViewController
)如果用户向后滑动。
这使得很难跟踪 VC 目前实际上显示的是哪个非常困难。如果发现(参见 )我必须同时使用 WillTransition
事件和 DidFinishAnimating
事件。我不熟悉 Xamarin 和 C#,所以如果这种语法有偏差,请原谅我,但我认为是这样的:
pvcOnboarding.WillTransition += (sender, e) => {
nextVCIndex = ((VCOnboardContentNew)e.PendingViewControllers[0]).PageIndex
}
pvcOnboarding.DidFinishAnimating += (sender, e) => {
if(e.Finished && e.Completed){
// Page transition completed
currentVCIndex = nextVCIndex
// Update Page Control here
}else{
// Incomplete page transition
}
};
您需要添加 currentVCIndex
和 nextVCIndex
作为 class 级变量。
老问题,但最近也在为同样的问题苦苦挣扎。终于找到了一个不错的解决方案。
为您的 UIPageViewController 提供:
public static int pageIndex = 0; // or whatever start index
然后为每个要加载的 UIViewController 覆盖 ViewDidAppear:
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
MyCystomPageViewController.pageIndex = 1; // the index of this viewcontroller
}
这是有效的,因为在将视图添加为子视图时会调用 ViewDidAppear。因此每次滑动时都会被调用。
我在情节提要中创建了一个教程 window,其中包含两个视图,一个用于显示教程,另一个用作每页内容的模板。
某些元素是在 ViewDidLoad 事件中以编程方式编码的。
页面ViewController按要求 100% 工作,它显示了三页内容并允许前后轻扫而不会出现问题。
我已经以编程方式将 UIPageControl 添加到主 ViewController 但是我无法正确更新其 CurrentPage 值。访问数据源 PageIndex 值在来回滑动时给我奇怪的结果。
有没有可靠的方法可以准确知道显示了哪个页面?
或者想知道页面过渡往哪个方向移动,这样我就可以手动更新了?不完全确定如何从 pagecontrollers 'DidFinishAnimating' 事件访问 UIPageViewControllerNavigationDirection。
我的主视图控制器代码如下:
using Foundation;
using System;
using UIKit;
namespace Performance
{
partial class VCOnboardHome : UIViewController
{
const int pageCount = 3;
public UIPageViewController pvcOnboarding{ get; set; }
private OnboardingDataSource onboardDataSource;
UIStoryboard board;
public UIPageControl pgControlIndicator;
public VCOnboardHome (IntPtr handle) : base (handle)
{
}
/// <summary>
/// ViewDidLoad event method
/// </summary>
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
board = UIStoryboard.FromName ("Main", null);
// Programmatically create a PageView controller
pvcOnboarding = new UIPageViewController (UIPageViewControllerTransitionStyle.Scroll,
UIPageViewControllerNavigationOrientation.Horizontal,
UIPageViewControllerSpineLocation.None, 20f);
// PageView Controller datasource
var views = CreateViews ();
onboardDataSource = new OnboardingDataSource (views);
pvcOnboarding.DataSource = onboardDataSource;
pvcOnboarding.SetViewControllers (new UIViewController[] { views [0] },
UIPageViewControllerNavigationDirection.Forward,
false, null);
// Set PageView size
pvcOnboarding.View.Frame = View.Bounds;
// Add the page view control to this view controller
Add (pvcOnboarding.View);
// Create Page Control
var frame = UIScreen.MainScreen.Bounds;
pgControlIndicator = new UIPageControl ();
pgControlIndicator.Frame = new CoreGraphics.CGRect (20f, frame.Height - 60f, frame.Width - 40f, 40f);
pgControlIndicator.Pages = pageCount;
pgControlIndicator.UserInteractionEnabled = false;
Add (pgControlIndicator);
// Update the Page Control to indicate the current page we are showing.
// Only do this if the full page transition happened and not a partial page turn
pvcOnboarding.DidFinishAnimating += (sender, e) => {
foreach(UIViewController u in e.PreviousViewControllers){
// TODO - Not needed, remove once page control working
// u = the previous viewcontroller
}
if(e.Finished && e.Completed){
// Page transition completed
// Update Page Control here
}else{
// Incomplete page transition
}
};
}
// Content for each page
VCOnboardContentNew[] CreateViews ()
{
var pageData = new [] {
new ContentOnBoardData {
headerLblText = @"Page 1",
bodyContentText = @"Page 1 body text blah blah blah blah",
buttonText = @"Ok, next no. 1",
pageImage = UIImage.FromBundle("ios_images_v2/onboarding/icon-qr-code.png"),
currentPage = 1,
totalPages = 3
},
new ContentOnBoardData {
headerLblText = @"Page 2",
bodyContentText = @"Page 2 body text blah blah blah blah",
buttonText = @"Ok, next no. 2",
pageImage = UIImage.FromBundle("ios_images_v2/onboarding/icon-id-check.png"),
currentPage = 2,
totalPages = 3
},
new ContentOnBoardData {
headerLblText = @"Page 3",
bodyContentText = @"Page 3 body text blah blah blah blah",
buttonText = @"Ok, got it",
pageImage = null,
currentPage = 3,
totalPages = 3
}
};
var views = new VCOnboardContentNew[pageData.Length];
for (int i = 0; i < pageCount; i++) {
int pageIndex = i;
views [i] = (VCOnboardContentNew)board.InstantiateViewController ("sbid_onboardcontent");
views [i].PageIndex = pageIndex;
views [i].HeaderText = pageData [i].headerLblText;
views [i].ContentText = pageData [i].bodyContentText;
views [i].PageImage = pageData [i].pageImage;
views [i].CurrentPage = pageData [i].currentPage;
views [i].TotalPages = pageCount;
views [i].ButtonText = pageData [i].buttonText;
views [i].ButtonClicked += (s, e) => {
DismissViewController (true, null);
};
}
return views;
}
}
/// <summary>
/// Onboarding data source.
/// </summary>
class OnboardingDataSource : UIPageViewControllerDataSource
{
readonly VCOnboardContentNew[] _views;
public OnboardingDataSource (VCOnboardContentNew[] views)
{
_views = views;
}
/// <summary>
/// Gets the previous view controller.
/// </summary>
/// <returns>The previous view controller.</returns>
/// <param name="pageViewController">Page view controller.</param>
/// <param name="referenceViewController">Reference view controller.</param>
public override UIViewController GetPreviousViewController (UIPageViewController pageViewController, UIViewController referenceViewController)
{
int index = ((VCOnboardContentNew)referenceViewController).PageIndex;
bool controlCheck = (index <= 0);
UIViewController vcToReturn = controlCheck ? null : (_views [index - 1]);
return vcToReturn;
}
/// <summary>
/// Gets the next view controller.
/// </summary>
/// <returns>The next view controller.</returns>
/// <param name="pageViewController">Page view controller.</param>
/// <param name="referenceViewController">Reference view controller.</param>
public override UIViewController GetNextViewController (UIPageViewController pageViewController, UIViewController referenceViewController)
{
int index = ((VCOnboardContentNew)referenceViewController).PageIndex;
bool controlCheck = index + 1 >= _views.Length;
UIViewController vcToReturn = controlCheck ? null : _views [index + 1];
return vcToReturn;
}
}
/// <summary>
/// Content onboard data.
/// </summary>
struct ContentOnBoardData
{
public string headerLblText;
public string bodyContentText;
public string buttonText;
public UIImage pageImage;
public int currentPage;
public int totalPages;
}
}
我的页面内容代码视图控制器如下:
using System;
using UIKit;
namespace Performance
{
/// <summary>
/// Class: VCOnboardContentNew
/// </summary>
partial class VCOnboardContentNew : UIViewController
{
public EventHandler ButtonClicked;
public int PageIndex { get; set; }
public string HeaderText{ get; set; }
public UIImage PageImage{ get; set; }
public int CurrentPage{ get; set; }
public int TotalPages{ get; set; }
public string ContentText{ get; set; }
public string ButtonText{ get; set; }
public VCOnboardContentNew (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Set text for the main title
lblTest.Font = UIFont.FromName("FreightDispLight", 26f);
lblTest.Text = HeaderText;
// Set text for body content
lblContentBodyText.Font = UIFont.FromName("FreightDispLight", 14f);
lblContentBodyText.Text = ContentText;
pgCtrlWhichPageWeOn.Hidden = true;
if(PageImage != null){
pageContentImage.Hidden = false;
pageContentImage.Image = PageImage;
}else{
pageContentImage.Hidden = true;
}
btnCallToAction.SetTitle (ButtonText, UIControlState.Normal);
btnCallToAction.TouchUpInside += (object sender, EventArgs e) => {
if (ButtonClicked != null) {
ButtonClicked.Invoke (this, null);
}
};
}
}
}
当过渡样式设置为滚动时,页面视图控制器似乎:
- 一旦一个转换完成,它会立即调用下一个(或上一个)View Controller(
GetNextViewController
),即使用户可能没有开始下一次滑动;和 - 它保留从中转换过来的视图控制器,并假定它是之前的 VC,因此它不会调用之前的 VC(使用
GetPreviousViewController
)如果用户向后滑动。
这使得很难跟踪 VC 目前实际上显示的是哪个非常困难。如果发现(参见 WillTransition
事件和 DidFinishAnimating
事件。我不熟悉 Xamarin 和 C#,所以如果这种语法有偏差,请原谅我,但我认为是这样的:
pvcOnboarding.WillTransition += (sender, e) => {
nextVCIndex = ((VCOnboardContentNew)e.PendingViewControllers[0]).PageIndex
}
pvcOnboarding.DidFinishAnimating += (sender, e) => {
if(e.Finished && e.Completed){
// Page transition completed
currentVCIndex = nextVCIndex
// Update Page Control here
}else{
// Incomplete page transition
}
};
您需要添加 currentVCIndex
和 nextVCIndex
作为 class 级变量。
老问题,但最近也在为同样的问题苦苦挣扎。终于找到了一个不错的解决方案。
为您的 UIPageViewController 提供:
public static int pageIndex = 0; // or whatever start index
然后为每个要加载的 UIViewController 覆盖 ViewDidAppear:
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
MyCystomPageViewController.pageIndex = 1; // the index of this viewcontroller
}
这是有效的,因为在将视图添加为子视图时会调用 ViewDidAppear。因此每次滑动时都会被调用。