I have implemented a niceUIPageViewControl with aPageControl. When swiping the indicator changes and shows the correct current page.
However i noticed that all that it takes for the current page indicator to switch is to start swiping. Meaning if i start swiping but then let go of the finger the current page indicator has switched like the page has been switched however it has not. This is the code that i am using to make the switch:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
NSUInteger index = [self.navigationController.viewControllers indexOfObject:viewController];
self.pageControl.currentPage = index;
}
Another thing i noticed was that when i swipe right and left changing views FAST the page indicator is just stuck and does not move.
So it only works when you are not changing views fast. If you need any additional code let me know. Thank you.
Edit
This is the code i use to implement my UIPageViewController.
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the data model
self.navigationItem.title = #"Tabell";
self.identifiers = [[NSMutableArray alloc] init];
[self.identifiers addObject:#"rankTable"];
[self.identifiers addObject:#"page2"];
// Create page view controller
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
self.pageViewController.delegate = self;
self.navigationController.delegate = self;
CGSize navBarSize = self.navigationController.navigationBar.bounds.size;
CGPoint origin = CGPointMake( navBarSize.width/2, navBarSize.height/2 );
self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(origin.x, origin.y+16,
0, 0)]; //Here added 45 to Y and it did the trick
self.pageControl.pageIndicatorTintColor = navbarColor;
self.pageControl.currentPageIndicatorTintColor = [UIColor lightGrayColor];
[self.pageControl setNumberOfPages:2];
[self.navigationController.navigationBar addSubview:self.pageControl];
UITableViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
CGFloat tabBarHeight = self.tabBarController.tabBar.frame.size.height;
// Change the size of page view controller
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - tabBarHeight);
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
Why don't you implement the UIPageViewControllerDelegate's
- pageViewController: didFinishAnimating: previousViewControllers: transitionCompleted: method? It has a transitionCompleted boolean which tells you if you should update your page control. Your current implementation seems to be buggy because as soon as you star swiping a page, it loads a view controller for the next page and calls the ...didShow... method. Hope this hepls.
Related
i want to create a navigation bar in which I have a pageControl as well as the Title in the navigation bar just like the twitter iOS app contains, I got them both by setting the
self.navigationItem.Title = #"Home";
and achieved the PageControl by creating an instance of UIPageControl and setting its centre to the centre of the navigation bar and achieving its navigation detection by using the UINavigationController's Delegate. But the problem is that the pageControl and the title are overlapping each other.. how can I set it like this that the Title should appear on the top of page indicator. Here is the code of the positioning the pageControl.
self.navigationController.delegate = self;
CGSize navBarSize = self.navigationController.navigationBar.bounds.size;
CGPoint origin = CGPointMake( navBarSize.width/2, navBarSize.height/2 );
self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(origin.x, origin.y,
0, 0)];
[self.pageControl setNumberOfPages:3];
[self.navigationController.navigationBar addSubview:self.pageControl];
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
int index = [self.navigationController.viewControllers indexOfObject:viewController];
self.pageControl.currentPage = index; }
I figured it out, both (the title and the pageControl) were overlapping each other so i just changed the Y-Axis of the pageControl and
self.navigationController.delegate = self;
CGSize navBarSize = self.navigationController.navigationBar.bounds.size;
CGPoint origin = CGPointMake( navBarSize.width/2, navBarSize.height/2 );
self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(origin.x, origin.y+45,
0, 0)]; //Here added 45 to Y and it did the trick
[self.pageControl setNumberOfPages:3];
[self.navigationController.navigationBar addSubview:self.pageControl];
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
int index = [self.navigationController.viewControllers indexOfObject:viewController];
self.pageControl.currentPage = index; }
I create one UIScrollView with programmatically code and I have one button in my view (not exist in UIScrollView).
when I click on this button go to next page with modal transition. I create Cancel button in next page that when I click on it back to main page (this page have UIScrollView).
I want when click on Cancel button and return to main page call one method that is in main page and in this method change ContentOfSet my ScrollView... but not working!!!!
this is my code:
mainView.m
- (void)viewDidLoad{
UIScrollView *scrollbar = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,width, height)];
scrollbar.directionalLockEnabled = YES;
scrollbar.backgroundColor = [UIColor whiteColor];
scrollbar.maximumZoomScale = 1.0;
scrollbar.minimumZoomScale = 1.0;
scrollbar.clipsToBounds = YES;
scrollbar.showsHorizontalScrollIndicator = YES;
scrollbar.pagingEnabled = YES;
[scrollbar setContentSize:CGSizeMake(scrollbar.frame.size.width * 4,scrollbar.frame.size.height)];
scrollbar.contentOffset = CGPointMake(0, 0);
scrollbar.delegate = self;
[self.view addSubview:scrollbar];
[super viewDidLoad];
}
-(void)ChangeMainScrollContentOffset{
scrollbar.contentOffset = CGPointMake(scrollbar.frame.size.width * (3), 0);
}
this button is in root View :
- (IBAction)AddView:(id)sender{
AddStationController *add = [[AddStationController alloc]initWithNibName:#"AddStationController" bundle:nil];
[add setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self.view.window.rootViewController presentViewController:add animated:YES completion:nil];
}
this button is Cancel button for return in my mainView:
- (IBAction)BackView:(id)sender{
mainView *main = [[mainView alloc]init];
[main ChangeMainScrollContentOffset];
[self dismissViewControllerAnimated:YES completion:nil];
}
please guide me about that!!!
I so confused why not working ContentOffSet!!!
I think your problem is that you didn't add ChangeMainScrollContentOffset
method to your ViewController.h file.
Just add it in ViewController.h file and than call it.
You second viewController should have reference to previous view controller.
When you do this:
mainView *main = [[mainView alloc]init];
[main ChangeMainScrollContentOffset];
You create another instance of mainView which not on screen and change contentOffset on ANOTHER scrollView :)
You should add property delegate to your AddStationController
AddStationController.h
#property (nonatomic, weak) mainView* delegate;
mainView:
- (IBAction)AddView:(id)sender{
AddStationController *add = [[AddStationController alloc]initWithNibName:#"AddStationController" bundle:nil];
>> add.delegate = self; <<
[add setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self.view.window.rootViewController presentViewController:add animated:YES completion:nil];
}
Cancel button action
- (IBAction)BackView:(id)sender{
>> [self.delegate ChangeMainScrollContentOffset]; <<
[self dismissViewControllerAnimated:YES completion:nil];
}
I am using UIPageViewController to show images full screen, the UIViewController which is added to UIPageController as a sub view / child has the images being showed using ImageView.
Problem is the images arent comming fullscreen, instead the pagecontrol view's donts are appearing at the bottom and that space is completely wasted. Please check the image attached.
Here is the code
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
NewsItemViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
Here NewsItemViewController is UIViewController showing images and some text and The MainViewController implements UIPageViewControllerDataSource protocol and necessary methods in MainViewController.
I believe there has to be a way to do show the things in full screen.
*** Also the MainViewController is a part of a storyboard if that matters.
Finally got the solution myself I just hide the page control from UIViewPageController and then extended the size of the UIPageViewController to cover up the gap left due to absense of page control.
NSArray *subviews = self.pageController.view.subviews;
UIPageControl *thisControl = nil;
for (int i=0; i<[subviews count]; i++) {
if ([[subviews objectAtIndex:i] isKindOfClass:[UIPageControl class]]) {
thisControl = (UIPageControl *)[subviews objectAtIndex:i];
}
}
thisControl.hidden = true;
self.pageController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height+40);
Curious. The docs say:
If both of the methods in “Supporting a Page Indicator” are
implemented and the page view controller’s transition style is
UIPageViewControllerTransitionStyleScroll, a page indicator is
visible.
Those two methods are:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
}
Are you implementing these two data source methods? If so, perhaps if you remove them you won't have to manually remove the page control (dots)? A quick test would be to change the
UIPageViewControllerTransitionStyleScroll
to
UIPageViewControllerTransitionStylePageCurl
and see if the page control indicator dots go away. (After commenting out your hide method, of course.)
UIPageViewController dots are only shown when you have implemented following method:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController;
check in you code. and also returning back zero in this method will hide the dots (UIPageControl)
I am adding for swift 2.2 compatible code
for view in self.view.subviews {
if view.isKindOfClass(UIScrollView) {
view.frame = UIScreen.mainScreen().bounds
} else if view.isKindOfClass(UIPageControl) {
view.backgroundColor = UIColor.clearColor()
}
}
This is the Swift 4 compatible solution, embedded in viewDidLayoutSubviews
for view in self.view.subviews {
if view.isKind(of:UIScrollView.self) {
view.frame = UIScreen.main.bounds
} else if view.isKind(of:UIPageControl.self) {
view.backgroundColor = UIColor.clear
}
}
Swift version :). Return Zero for below implementation inside UIPageViewController subclass.
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int { return 0 }
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int { return 0 }
Set your pager controller as so
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
And implement,this method should return any value greater than 1
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
// The selected item reflected in the page indicator.
return 2;
}
Now the gap at the bottom space is removed and no page control shown :)
I am creating an app using iOS 5, and I want to work as follows:
MainViewController w/ NavigationBar, and i set on viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
NavViewController *nav = [[NavViewController alloc] init];
[self addChildViewController:nav];
[self.view addSubview:nav.view];
}
Like a "partial view" with some nav controls (my own/custom toolbar), and at NavViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect viewRect = CGRectMake(0, 0, 320, 200);
self.view.frame = viewRect;
self.view.backgroundColor = [UIColor redColor];
}
At this point I have MainViewController with a navigation subview and still have space (in MainViewController view) that another subview can be added using the actions NavViewController.
Is there a serious flaw in this logic? or I can keep developing ?
Thnks a lot.
I m using UIPageViewController on iPad where I need to show a firstviewController in the first page and ContentViewController in the next page in landscape.
If I set the NSArray with two viewControllers the app is crashes at [self.pagviewController setViewController:] with the following exception:
The number of provided view controllers (2) doesn't match the number required (1) for the requested spine location (UIPageViewControllerSpineLocationMin)
Below is the code:
#pragma mark - UIPageViewControllerDataSource Methods
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentViewController *)viewController textContents]];
if(currentIndex == 0)
{
return nil;
}
ContentViewController *contentViewController = [[ContentViewController alloc] init];
contentViewController.textContents = [self.modelArray objectAtIndex:currentIndex - 1];
return contentViewController;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentViewController *)viewController textContents]];
if(currentIndex == self.modelArray.count-1)
{
return nil;
}
ContentViewController *contentViewController = [[ContentViewController alloc] init];
contentViewController.textContents = [self.modelArray objectAtIndex:currentIndex + 1];
return contentViewController;
}
//#pragma mark - UIPageViewControllerDelegate Methods
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
if(UIInterfaceOrientationIsPortrait(orientation))
{
//Set the array with only 1 view controller
UIViewController *currentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:currentViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
//Important- Set the doubleSided property to NO.
self.pageViewController.doubleSided = NO;
//Return the spine location
return UIPageViewControllerSpineLocationMin;
}
else
{
NSArray *viewControllers = nil;
ContentViewController *currentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentViewController *)currentViewController textContents]];
if(currentIndex == 0 || currentIndex %2 == 0)
{
UIViewController *nextViewController = [self pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
viewControllers = [NSArray arrayWithObjects:currentViewController, nextViewController, nil];
}
else
{
UIViewController *previousViewController = [self pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
viewControllers = [NSArray arrayWithObjects:previousViewController, currentViewController, nil];
}
//Now, set the viewControllers property of UIPageViewController
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
return UIPageViewControllerSpineLocationMid;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
//Instantiate the model array
self.modelArray = [[NSMutableArray alloc] init];
self.vcs = [[NSMutableArray alloc]init];
for (int index = 1; index <= 2 ; index++)
{
[self.modelArray addObject:[NSString stringWithFormat:#"Page %d",index]];
}
//Step 1
//Instantiate the UIPageViewController.
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
//Step 2:
//Assign the delegate and datasource as self.
self.pageViewController.delegate = self;
self.pageViewController.dataSource = self;
//Step 3:
//Set the initial view controllers.
appDelegate.contentViewController.textContents = [self.modelArray objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObjects:appDelegate.firstViewController,appDelegate.contentViewController,nil];
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
//Step 4:
//ViewController containment steps
//Add the pageViewController as the childViewController
[self addChildViewController:self.pageViewController];
//Add the view of the pageViewController to the current view
[self.view addSubview:self.pageViewController.view];
//Call didMoveToParentViewController: of the childViewController, the UIPageViewController instance in our case.
[self.pageViewController didMoveToParentViewController:self];
//Step 5:
// set the pageViewController's frame as an inset rect.
CGRect pageViewRect = self.view.bounds;
pageViewRect = CGRectInset(pageViewRect, 40.0, 40.0);
self.pageViewController.view.frame = pageViewRect;
//Step 6:
//Assign the gestureRecognizers property of our pageViewController to our view's gestureRecognizers property.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
}
The problem is you passing an array containing two view controllers to the page view controller while it expects one at a time, change your array to be like this :
NSArray *viewControllers = #[appDelegate.firstViewController];
You will pass one of the views but viewControllerAfterViewController and viewControllerBeforeViewController will handle the rest.
Ah..Finally got solution for this same issue.., it may helps you..
When we set the spine location to UIPageViewControllerSpineLocationMid, the doubleSided property of the pageViewController is automatically set to YES. This means that the content on page front will not partially show through back. But when this property is set to NO, the content on page front will partially show through back, giving the page a translucent kind of effect. So, in the portrait orientation, we have to set the value to NO, otherwise it would result in an exception.
So in your UIPageviewcontroller delegate method, in else part add this doubleSided property as YES when you return spineLocation as UIPageViewControllerSpineLocationMid
self.pageViewController.doubleSided = YES;
return UIPageViewControllerSpineLocationMid;
self.pageViewController.doubleSided = NO;
return UIPageViewControllerSpineLocationMid;
This is the solution for the exception.
In xcode it self you could find this.
Go to the UIPageViewcontroller class there you could see the explanation for this like:
#property (nonatomic, readonly) UIPageViewControllerSpineLocation spineLocation; // If transition style is 'UIPageViewControllerTransitionStylePageCurl', default is 'UIPageViewControllerSpineLocationMin', otherwise 'UIPageViewControllerSpineLocationNone'.
// Whether client content appears on both sides of each page. If 'NO', content on page front will partially show through back.
// If 'UIPageViewControllerSpineLocationMid' is set, 'doubleSided' is set to 'YES'. Setting 'NO' when spine location is mid results in an exception.
#property (nonatomic, getter=isDoubleSided) BOOL doubleSided; // Default is 'NO'.
Instead of implementing a full data source, you can set the PageViewController with one view controller at a time each time the user pushes a next or back button, like
[pageViewController setViewControllers:#[contentViewController]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
This will animate the page transition as you switch.