how to pass data in UIPageViewController in ios [closed] - ios

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
i am new to Xcode.
i want to create a app with Single view with UIPageViewController .
In this its have 5 screen.
First screen have a textfield and second screen have label and textfield and so on.
when user enter a text in textfield and scroll then textfield data (means text) of first
screen to second screen label and so on.
please tell me the answer i searched every where on next even stackoverflow.com and nothing
found like that.
please help me.

first take a UIPageViewController and set storyboard ID in IB and one otherview controller which is a simple UIViewController but remember its name as a PageContentViewController i.e., this viewcontroller holds the 5 viewcontrollers u want to show.
in PageContentViewController class declare a UIPageViewController *pageViewController;
and in viewWillAppear: initialise it with its storyboardID like this
pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"storyboardID"];
then import the 5 viewcontroller classes into this PageViewController class and create objects for it like this
#import "ViewController1";
#import "ViewController2";
#import "ViewController3";
#import "ViewController4";
#import "ViewController5";
and in interface create objects
ViewController1 *vcObj1;
ViewController2 *vcObj2;
ViewController3 *vcObj3;
ViewController4 *vcObj4;
ViewController5 *vcObj5;
and now in .m file set these 5 viewcontrollers as an objects in an array in viewWillAppear: method like this
[self setViewControllers:#[vcObj1, vcObj2, vcObj3, vcObj4, vcObj5]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
now u have to set pageviewcontroller delegate to the pagecontentviewcontroller class and write the delegate methods in .m file
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;
if ((index == 0) || (index == NSNotFound)) {
return vcObj1;
}
else if(index == 1) {
return vcObj2;
}
else if(index == 2) {
return vcObj3;
}
else if(index == 3){
return vcObj4;
}
else if(index == 4) {
return vcObj5;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;
if (index == NSNotFound) {
return nil;
}
index++;
if (index == 0) {
return vcObj5;
}
else if(index == 1) {
return vcObj4;
}
else if(index == 2) {
return vcObj3;
}
else if(index == 3){
return vcObj2;
}
else if(index == 4) {
return vcObj1;
}
index++;
return [self viewControllerAtIndex:index];
}
if u have written these two delegates method correctly ur functionality is done because these two are the major ones totally controlling the pageviewcontroller. i'm not sure about the indexes, just trial it once and change the indexes according to ur requirement.
Happy coding

Related

How do I create variable number of ViewControllers within a PageViewController?

I have a UIPageViewController in a container that is supposed to show, at minimum, three pages; at maximum, six. The number of pages would be determined by the user before the PageViewController appears.
For a bigger picture, I'm trying to create a project management application that allows for an agile ticket creating and management system. The PageVC is supposed to flip through 3 to 6 columns depending on how many the user wants to go along with that project. As an example, if it's a simple project, they can just have three columns named: "Unassigned", "Assigned", and "Complete". More complex projects could, for example, have 5 columns, named: "Backlog", "Issues", "In Progress", "Review", "Done". Each one of those columns would have its own ViewController within the PageVC.
This is what my data model looks like, to get an idea of where I get the number of columns from (Project attribute named: "projectColumnCount"):-
you can use following ObjectiveC concept in Swift.
It is Advisable to create a content view controller initially and then to use Pageviewcontroller Delegate Methods to create other controllers.
you can check following delegate Methods of Pageviewcontroller for same
Delegate Methods for Pageviewcontroller are as below.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;
if ((index == 0) || (index == NSNotFound)) {
return nil;
} index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;
if (index == NSNotFound)
{
return nil;
}
index++;
if (index == [arrPage count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}
you can use below method to create instance of ContentVC
- (PageContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([arrPage count] == 0) || (index >= [arrPage
count])) {
return nil;
}
// Create a new view controller and pass suitable data.
pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageContentViewController"];
pageContentViewController.delagate = self;
pageContentViewController.pageIndex = index;
pageContentViewController.collselectedType = selectedType;
return pageContentViewController;
}
you can also Create Array of contentVC and can set that in Pageviewcontroller but it will consume more memory
you can set view conteollers array as below.
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];

How to conditionally go to the next page in pageViewController?

I am using pageViewController in my App. I have 9 pages which are created dymanically and each page have 1 question which have 4 multiple choice answers. Each option have custom UIButton with image and its "BlankedCheckBox" when the option is not selected and "MakredCheckBox" when its selected. Right now I can go to next page even if any of the available answer is not selected. I want to go to the next page only if user has selected one of the available option. The transition style of my pageViewController is scroll.
It seems to me like I have to add something in following method. Any help would be greatly appreciated, thanks.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = ((PageContentViewController *) viewController).pageIndex;
if (index == NSNotFound) {
return nil;
}
index ++;
if (index == [self.questionTitles count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}
You should not implement the dataSource methods. If you do the user can scroll using a gesture.
To achieve the behavior you want, set the page in code in the IBAction of the control:
- (IBAction)nextPage {
[self setViewControllers:[nextViewController]
direction: UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
}

UIScrollView contains multiple TableViews with pagingEnabled

I have an UIScrollView that contains 3 UITableView and pagingEnabled = YES.
Users can pan to the left or right to switch between tables (just like the Notification Center of iOS).
I've handled almost all visual bugs (i can help if anyone needed), but the problem is every table have an UISearchBar. Which means in my controller, I've to create 3 UITableView, 3 UISearchBar and 3 UISearchDisplayController.
That will be one of a hell messy controller.
What the best practice in this case ?
It does sound tedious.
Why not create a single UIViewController having a SearchBar and a Tableview. Customize your viewController to be differentiated by tags, add some delegates probably and add it to your UIScrollview? That way, the coding will be structured and clean
I'm just spitballing though.
left or right table will move with your swipe. You have 1 class of your custom UIViewController but different instances.
- (SearchResultViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([[LeboncoinAgent shareAgent].searchConditions count] == 0) || (index >= [[LeboncoinAgent shareAgent].searchConditions count])) {
return nil;
}
// Create a new view controller and pass suitable data.
SearchResultViewController *pageContentViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SearchResultViewControllerId"];
pageContentViewController.pageIndex = index;
pageContentViewController.controller = self;
return pageContentViewController;
}
#pragma mark - Page View Controller Data Source
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = ((SearchResultViewController*) viewController).pageIndex;
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
_currentPageIndex = index;
return [self viewControllerAtIndex:index];
}

UIPageViewController programmatically tell what page is showing

is there a way to programmatically tell when a certain page is showing in a pageviewcontroller? For example, I instantiated it as the following:
if (index == 0) {
pageContentViewController = [storyboard instantiateViewControllerWithIdentifier:#"OnCampusTable"];
}
else if (index == 1) {
pageContentViewController = [storyboard instantiateViewControllerWithIdentifier:#"OffCampusTable"];
}
else if (index == 2) {
pageContentViewController = [storyboard instantiateViewControllerWithIdentifier:#"MyEventsTable"];
}
I want to write something where
if (current page is "OnCampusTable") {
method 1
}
else if (current page is "OffCampusTable") {
method 2
}
else if (current page is "MyEventsTable") {
method 3
}
I've tried using the index, but because of the way that the pages load and stuff, it actually doesn't work properly. I was thinking of trying to access the page indicator thing (the little circle things) to get the page number, but I don't know how to do that.
I've seen a few solutions out there that use an array of ViewControllers and do comparisons, but I don't want to keep a bunch of controllers around (I generate them on the fly from my model), so I found a solution that seems to work.
Basically I point a property to the prior and next controllers in the data source before and after methods. In the delegate's didFinishAnimating call I compare the new controller to these properties to see if we moved forward or backward. One caveat is that before and after don't get called if the controller has already been loaded, so I reassign my property based on didFinishAnimating's previousViewController's value.
Note, I only implemented this for the case where one page is displayed.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
if (self.pageIndex > 0) {
UIViewController *vc = [self genController:self.trackList[self.pageIndex - 1]];
self.priorVC = vc;
return vc;
} else {
self.priorVC = nil;
return nil;
}
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
if (self.pageIndex < self.trackList.count - 1) {
UIViewController *vc = [self genController:self.trackList[self.pageIndex + 1]];
self.nextVC = vc;
return vc;
} else {
self.nextVC = nil;
return nil;
}
}
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
if (finished && completed) {
if (pageViewController.viewControllers.lastObject == self.priorVC) {
NSLog(#"Back");
self.pageIndex--;
self.nextVC = previousViewControllers.lastObject;
} else if (pageViewController.viewControllers.lastObject == self.nextVC) {
NSLog(#"Forward");
self.pageIndex++;
self.priorVC = previousViewControllers.lastObject;
}
NSLog(#"Page: %ld",self.pageIndex);
}
}
You can use the delegate method pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted: to keep track of the current page and then execute some method depending on the page.

Infinite Scroll UIPageViewController

I'm trying to setup a UIPageViewController that can scroll infinitely by dynamically loading page views via an NSUrlConnection data pull. I start with 3 views: prev,curr,next; and load an additional view when the first or last view in the pageData array is reached.
The trouble is that I load the additional views in viewControllerBeforeViewController or viewControllerAfterViewController. It seems that these methods can be called 2-3 times when doing a single swipe between pages. They can also be called during a half-swipe that never finishes the page transition. This can mean that multiple preloads start getting done prematurely. Then somehow the pageViewController forgets the current location.
[self.pageViewController setViewControllers:#[[self.pageData objectAtIndex:gotoIdx]] direction:direction animated:NO completion:nil];
This line is used below to signal an addition of data in the pageViewController, and to go to eh appropriate view. When I start using it, instead of going to the prev/next view with a swipe, it starts jumping to seemingly random views. Once I started going forward when swiping backwards...
Is there a more appropriate place to do the preload that is only called once after the page transition completes? And, does the line above have to be called, or is there a more reliable way to ensure it goes to the correct page? Feeling frustrated trying to wire this up here.
// the init line, somewhere early on. uses Scoll style
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationVertical options:nil];
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger index = [self.pageData indexOfObject:viewController];
if(index == 1){
// if loading last one on start, preload one more in advance
[self loadSecondaryCalData:-1 endView:[[self viewControllerAtIndex:index-1] retain]];
} else if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger index = [self.pageData indexOfObject:viewController];
if(index == [self.pageData count]-2){
[self loadSecondaryCalData:1 endView:[[self viewControllerAtIndex:index+1] retain]]; // if last one, preload one more in advance
} else if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.pageData count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}
- (void)loadSecondaryCalData:(int)howMany endView:(CalendarViewController*)endView {
// initiate NSURLConnection async request for view data
}
- (void)loadSecondaryCals: (NSDictionary*)data {
// callback for async request
// ... process view, cal
// add new object to page data, at start or end
if(next){
[self.pageData addObject:cal];
gotoIdx = [self.pageData count]-2;
direction = UIPageViewControllerNavigationDirectionForward;
} else {
[self.pageData insertObject:cal atIndex:0];
gotoIdx = 1;
direction = UIPageViewControllerNavigationDirectionReverse;
}
[self.pageViewController setViewControllers:#[[self.pageData objectAtIndex:gotoIdx]] direction:direction animated:NO completion:nil];
// alternate method to above line
/*
__block CalendarPageViewViewController *blocksafeSelf = self;
__block int blocksafeGotoIdx = gotoIdx;
__block UIPageViewControllerNavigationDirection blocksafeDirection = direction;
[self.pageViewController setViewControllers:#[[self.pageData objectAtIndex:gotoIdx]] direction:direction animated:YES completion:^(BOOL finished){
if(finished)
{
dispatch_async(dispatch_get_main_queue(), ^{
[blocksafeSelf.pageViewController setViewControllers:#[[blocksafeSelf.pageData objectAtIndex:blocksafeGotoIdx]] direction:blocksafeDirection animated:NO completion:nil];
});
}
}];
*/
}
UPDATE:
Thanks to the fast response below, referencing a great function I wasn't aware of. Here's the preload method that so perfectly only gets called once on completion of the transition. This seems to have greatly cleared up the unreliable jumping around of views and forgetting of location. Thanks #sha!
// function to detect if we've reached the first or last item in views
// detects for full completion only
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
if(completed == TRUE && [previousViewControllers count] > 0 && [pageData count] > 1){
CalendarViewController *firstCal = [pageData objectAtIndex:0];
CalendarViewController *lastCal = [pageData objectAtIndex:([pageData count]-1)];
CalendarViewController *prevCal = [previousViewControllers objectAtIndex:0];
CalendarViewController *currCal = [[pageViewController viewControllers] objectAtIndex:0];
NSLog(#"transition completed: %# -> %#", prevCal.week_day_date, currCal.week_day_date);
if(currCal == firstCal){
// preload on begining
[self loadSecondaryCalData:-1 endView:firstCal];
} else if(currCal == lastCal){
// preload to end
[self loadSecondaryCalData:1 endView:lastCal];
}
}
}
I think you need to do your loading calls inside pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted: handler. This method will be called when animation has finished and new ViewController became an active one.

Resources