I'm trying to implement an questionnaire app.
I have an UIPageViewController attached to the main view controller called QuizPageViewController.m. other controllers such as buttons are placed in content view controller called QuizContentViewController.m.
Now my question is how do I navigate pages of UIPageViewController programmatically from QuizContentViewController.m (eg. when buttonDoneClicked clicked)? I'm already aware of the fact that i can programmatically navigate pages using following command but my problem is I don't have access to its arguments from content view controller (QuizContentViewController).
setViewControllers:direction:animated:completion:.
following is my code.
QuizPageViewController.h
#import <UIKit/UIKit.h>
#import "QuizContentViewController.h"
@class QuizPageViewController;
@protocol QuizPageViewControllerDelegate <NSObject>
@optional // Delegate protocols
- (void)dismissReaderViewController:(QuizPageViewController *)viewController;
@end
@interface QuizPageViewController : UIViewController <UIPageViewControllerDataSource, QuizContentViewControllerDelegate>{
}
- (void)moveForward:(id)sender;
- (void)moveBackwards:(id)sender;
- (void)abort:(id)sender;
@property (nonatomic, unsafe_unretained, readwrite) id <QuizPageViewControllerDelegate> delegate;
@property (nonatomic, strong) UIPageViewController *pageView;
@end
QuizPageViewController.m
#import "QuizPageViewController.h"
@interface QuizPageViewController ()
@end
@implementation QuizPageViewController
@synthesize pageView, delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[self QuestionDet];
[super viewDidLoad];
// Do any additional setup after loading the view.
NSDictionary *option = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin] forKey:UIPageViewControllerOptionSpineLocationKey];
pageView = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:option];
[pageView setDataSource:self];
QuizContentViewController *initialVC = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialVC];
[pageView setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
CGRect viewRect = self.view.bounds;
[[pageView view] setFrame:viewRect];
[self addChildViewController:self.pageView];
[self.view addSubview:[pageView view]];
[pageView didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger) indexOfViewController:(QuizContentViewController *)viewController{
return viewController.dataObjquizNo;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController{
int index = [self indexOfViewController:(QuizContentViewController *)viewController];
if (index == 0 || index == NSNotFound) {
return nil;
}
index --;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController{
int index = [self indexOfViewController:(QuizContentViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
index++;
return [self viewControllerAtIndex:index];
}
- (QuizContentViewController *)viewControllerAtIndex:(NSInteger)index{
if (index > [quizdet count] - 1) {
return nil;
}
QuizContentViewController *cVC = [[QuizContentViewController alloc] init];
cVc.delegate = self;
Questions *quizz = [quizdet objectAtIndex:index];
[cVC setDataObjQuiz:[quizz quiz]];
[cVC setDataObjAns1:[quizz answer1]];
[cVC setDataObjAns2:[quizz answer2]];
[cVC setDataObjAns3:[quizz answer3]];
[cVC setDataObjAns4:[quizz answer4]];
[cVC setDataObjquizNo:index];
[cVC setDataObjtotalNoOfQuiz:[quizdet count]];
return cVC;
}
- (void)moveForward:(id)sender{
// Navigation forward code should goes here...
}
- (void)moveBackwards:(id)sender{
// Navigation backwards code should goes here...
}
- (void)abort:(id)sender{
[delegate dismissReaderViewController:self];
}
}
QuizContentViewController.h
#import <UIKit/UIKit.h>
@class QuizContentViewController;
@protocol QuizContentViewControllerDelegate <NSObject>
- (void)moveForward:(id)sender;
- (void)moveBackwards:(id)sender;
- (void)abort:(id)sender;
@end
@interface QuizContentViewController : UIViewController{
UITextView *txtVwQuiz;
UILabel *lblSummery;
NSString *dataObjQuiz;
NSString *dataObjAns1;
NSString *dataObjAns2;
NSString *dataObjAns3;
NSString *dataObjAns4;
NSInteger dataObjquizNo;
NSInteger dataObjtotalNoOfQuiz;
}
@property(nonatomic, unsafe_unretained, readwrite) id <QuizContentViewControllerDelegate> delegate;
@property (nonatomic, strong) NSString *dataObjQuiz;
@property (nonatomic, strong) NSString *dataObjAns1;
@property (nonatomic, strong) NSString *dataObjAns2;
@property (nonatomic, strong) NSString *dataObjAns3;
@property (nonatomic, strong) NSString *dataObjAns4;
@property NSInteger dataObjquizNo;
@property NSInteger dataObjtotalNoOfQuiz;
@end
QuizContentViewController.m
#import "QuizContentViewController.h
#import <QuartzCore/QuartzCore.h>
#define isPhone568 ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone && [UIScreen mainScreen].bounds.size.height == 568)
#define iPhone568ImageNamed(image) (isPhone568 ? [NSString stringWithFormat:@"%@-568h.%@", [image stringByDeletingPathExtension], [image pathExtension]] : image)
#define iPhone568Image(image) ([UIImage imageNamed:iPhone568ImageNamed(image)])
# define IS_IPAD UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone
@interface QuizContentViewController ()
@end
@implementation QuizContentViewController
@synthesize dataObjQuiz, dataObjAns1, dataObjAns2, dataObjAns3, dataObjAns4, dataObjquizNo, dataObjtotalNoOfQuiz, delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor]; // Transparent
UIImageView *backgroundImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:iPhone568ImageNamed(@"QuizPage.png")]];
[self.view addSubview:backgroundImage];
[self.view sendSubviewToBack:backgroundImage];
// Do any additional setup after loading the view.
// Override point for customization after application launch
CGSize viewSize = self.view.bounds.size;
float boader;
float heightofSubVw;
boader = IS_IPAD?15:37;
heightofSubVw = viewSize.height/10;
lblSummery = [[UILabel alloc] initWithFrame:CGRectMake(boader, 5, viewSize.width-(2*boader), heightofSubVw*1)];
[lblSummery setBackgroundColor:[UIColor clearColor]];
[lblSummery setTextAlignment:UITextAlignmentLeft];
[lblSummery setFont:[UIFont systemFontOfSize:IS_IPAD?14.f:28.f]];
[lblSummery setText:[NSString stringWithFormat:@" Question %d out of %d.",dataObjquizNo+1, dataObjtotalNoOfQuiz]];
[lblSummery setTextColor:[UIColor orangeColor]];
[self.view addSubview:lblSummery];
txtVwQuiz = [[UITextView alloc] initWithFrame:CGRectMake(boader, heightofSubVw, viewSize.width-(2*boader), heightofSubVw*4)];
[txtVwQuiz setText:dataObjQuiz];
[txtVwQuiz setBackgroundColor:[UIColor clearColor]];
[txtVwQuiz setTextAlignment:UITextAlignmentLeft];
[txtVwQuiz setTextColor:[UIColor whiteColor]];
[txtVwQuiz setFont:[UIFont systemFontOfSize:IS_IPAD?15.f:30.f]];
[txtVwQuiz setEditable:NO];
[self.view addSubview:txtVwQuiz];
NSArray *options =[[NSArray alloc]
initWithObjects:dataObjAns1,dataObjAns2,dataObjAns3,dataObjAns4,nil];
MIRadioButtonGroup *group =[[MIRadioButtonGroup alloc]
initWithFrame:CGRectMake(boader, heightofSubVw*4, viewSize.width-(2*boader), heightofSubVw*5)
andOptions:options andColumns:1];
[self.view addSubview:group];
UIButton *btnBack = [[UIButton alloc] initWithFrame:CGRectMake(boader+(IS_IPAD?6:12), heightofSubVw*8.5, IS_IPAD?44:88, IS_IPAD?44:88)];
[btnBack addTarget:self action:
@selector(navButtonBackClicked:)
forControlEvents:UIControlEventTouchUpInside];
btnBack.contentHorizontalAlignment =
UIControlContentHorizontalAlignmentCenter;
[btnBack setBackgroundImage:[UIImage imageNamed:
@"Navigation_Back.png"] forState:UIControlStateNormal];
[self.view addSubview:btnBack];
UIButton *btnDone = [[UIButton alloc] initWithFrame:CGRectMake(viewSize.width - (boader+(IS_IPAD?10 + 44 :20 + 88)), heightofSubVw*8.5, IS_IPAD?44:88, IS_IPAD?44:88)];
[btnDone addTarget:self action:
@selector(navButtonDoneClicked:)
forControlEvents:UIControlEventTouchUpInside];
btnDone.contentHorizontalAlignment =
UIControlContentHorizontalAlignmentCenter;
[btnDone setBackgroundImage:[UIImage imageNamed:
@"Navigation_Done.png"] forState:UIControlStateNormal];
[self.view addSubview:btnDone];
UIButton *btnAbort = [[UIButton alloc] initWithFrame:CGRectMake(viewSize.width - (boader+(IS_IPAD?2*10 + 88:2*20 + 176)), heightofSubVw*8.5, IS_IPAD?44:88, IS_IPAD?44:88)];
[btnAbort addTarget:self action:
@selector(navButtonAbortClicked:)
forControlEvents:UIControlEventTouchUpInside];
btnAbort.contentHorizontalAlignment =
UIControlContentHorizontalAlignmentCenter;
[btnAbort setBackgroundImage:[UIImage imageNamed:
@"Navigation_Abort.png"] forState:UIControlStateNormal];
[self.view addSubview:btnAbort];
}
-(IBAction) navButtonDoneClicked:(UIButton *) sender{
id index;
index = [NSNumber numberWithInt:dataObjquizNo];
if ([delegate respondsToSelector:@selector(moveForward:)]) {
[delegate moveForward:index];
}
}
-(IBAction) navButtonAbortClicked:(UIButton *) sender{
id index;
index = [NSNumber numberWithInt:dataObjquizNo];
if ([self.delegate respondsToSelector:@selector(abort:)]) {
[self.delegate abort:index];
}
}
-(IBAction) navButtonBackClicked:(UIButton *) sender{
id index;
index = [NSNumber numberWithInt:dataObjquizNo];
if ([delegate respondsToSelector:@selector(moveBackwards:)]) {
[delegate moveBackwards:index];
}
}
If I understand your question correctly, the QuizContentViewController's view has buttons that are pressed and once pressed it needs to notify your mainviewcontroller so it can set the view appropriately on QuizPageViewController? If this is the case, protocols are a great way for viewcontrollers to send messages to other viewcontrollers. Here is apple documentation about protocols: Working with Protocols.
Protocols are a little tricky the first time you use them. Once everything is set-up correctly you should be able to do something like this:
-(void)buttonBackClicked:(id)sender
{
[self.delegate shouldMoveBack];
}