Reputation: 1060
I have created a tutorial window in storyboard with two views, one to hold show the tutorial the other used as a template for each page of content.
Some elements are coded programmatically on the ViewDidLoad event.
The PageViewController is working 100% as required, it shows the three pages of content and allows swiping backwards and forwards without issue.
I've added a UIPageControl programmatically to the main ViewController but for the life of me cannot update its CurrentPage value correctly. Accessing the datasources PageIndex value gives me odd results when swiping back and forth.
Is there a reliable way to know exactly which page is been displayed ?
Or to know which direction the page transition moved, this way I can manually update? Not entirely sure how UIPageViewControllerNavigationDirection is accessed from the pagecontrollers 'DidFinishAnimating' event.
My main view controller code is as follows:
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;
}
}
My Page content code view controller is as follows:
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);
}
};
}
}
}
Upvotes: 1
Views: 629
Reputation: 155
Old question but was recently struggling with the same issue. Finally found a nice solution.
Provide your UIPageViewController with:
public static int pageIndex = 0; // or whatever start index
Then for each of your UIViewControllers to be loaded override ViewDidAppear:
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
MyCystomPageViewController.pageIndex = 1; // the index of this viewcontroller
}
This works since ViewDidAppear gets called when the view is added as subview. And thus will be called everytime you swipe.
Upvotes: 0
Reputation: 21536
When the transition style is set to scroll, the page view controller seems to:
GetNextViewController
), even though the user might not have started the next swipe; andGetPreviousViewController
) if the user swipes back.This makes it pretty difficult to keep track of which VC is actually currently showing pretty difficult. If found (see this answer) that I had to use both the WillTransition
event and the DidFinishAnimating
event. I'm not familiar with Xamarin and C#, so forgive me if this syntax is way off, but I think something like this:
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
}
};
You'll need to add currentVCIndex
and nextVCIndex
as class level variables.
Upvotes: 1