RTL languages uipageviewcontroller animation - iphone

I know this question is typique and it was asked many times in the forum, but I still cannot solve my problem, so please if any body can help be that would be GREAT :)
I am creating a book application in the arabic languages and I need to perform the transitions of the uipageviewcontroller from right to left. And thats all I have to say.
One more thing (if I hadn't explain very well my self) I have the exact need as this thread: How to change UIPageViewController direction of paging curl animation but I couldn't manage to make the solution they spoke about, so if someone can explain me or give me a link where I can have how to do it that would be more than enough :)

It can be done in this way
Swap the code of pageViewController's datasource from viewControllerBeforeViewController to viewControllerAfterViewController
Change UIPageViewControllerSpineLocationMin to UIPageViewControllerSpineLocationMax
To check that, start Page-Based Application template as Universal and change the following in ModelController.m
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.pageData count]) {
return nil;
}
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(DataViewController *)viewController];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
and change UIPageViewControllerSpineLocationMin to UIPageViewControllerSpineLocationMax and swipe the condition of (indexOfCurrentViewController % 2 == 0) in "RootViewController.m"
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
if (UIInterfaceOrientationIsPortrait(orientation) || ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)) {
UIViewController *currentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:currentViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
self.pageViewController.doubleSided = NO;
return UIPageViewControllerSpineLocationMax;
}
DataViewController *currentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSArray *viewControllers = nil;
NSUInteger indexOfCurrentViewController = [self.modelController indexOfViewController:currentViewController];
if (indexOfCurrentViewController == 0 || indexOfCurrentViewController % 2 == 0) {
UIViewController *previousViewController = [self.modelController pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
viewControllers = [NSArray arrayWithObjects:previousViewController, currentViewController, nil];
} else {
UIViewController *nextViewController = [self.modelController pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
viewControllers = [NSArray arrayWithObjects:currentViewController, nextViewController, nil];
}
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
return UIPageViewControllerSpineLocationMid;
}
Source: PageViewControllers Apple Doc

You should dive into core graphics and core animation.
Make 2 layers (a previous/next and current)
When doing a 'pangesture' (see the Event Handling Guide ) you need to see if it's a swipe left or swipe right (previous/next page)
Then perform a 3D Rotation on the layer(s)
here's a nice example about flipping pages

Madev, Do you not want to use the "PageBased Application" template?
Simply start a new xCode Project:
Under iOS > Application , look for Page-Based Application. Hit Okay, Choose you Options. All you have to do is supply your content (via the "DataSource").
Now that is the complicated part...But here's some tips
http://www.techotopia.com/index.php/Implementing_a_Page_based_iOS_5_iPhone_Application_using_UIPageViewController
http://www.techotopia.com/index.php/An_Example_iOS_5_iPhone_UIPageViewController_Application

You can set the semanticContentAttribute on your UIPageViewController's viewDidLoad like:
if <You app language is Arabic/RTL based> {
self.view.semanticContentAttribute = .forceRightToLeft
}else {
self.view.semanticContentAttribute = .forceLeftToRight
}
Keep coding......... :)

Related

Unloading viewControllers from UIPageViewController

I got a UIPageViewController where I add other viewControllers. Those ViewControllers are in the array viewControllersArray = [[NSMutableArray alloc] init]; I add viewControllers on this array like this:
[viewControllersArray addObject: infoViewController];
After the viewControllers are added on my array:
NSArray *initialViewControllers = [[NSArray alloc] initWithObjects:[viewControllersArray objectAtIndex:0], nil];
[self setViewControllers:initialViewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:^(BOOL finished){
NSLog(#"call back success");}];
This above code is all done in the viewDidLoad from my UIPageViewController.
For loading this array I got those functions form <UIPageViewControllerDataSource>
which I use like this:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
if ([viewControllersArray containsObject:viewController]) {
NSInteger index = [viewControllersArray indexOfObject:viewController];
if (index < [viewControllersArray count] && index > 0) {
return [viewControllersArray objectAtIndex:(index - 1)];
}
}
return nil;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
if ([viewControllersArray containsObject:viewController]) {
NSInteger index = [viewControllersArray indexOfObject:viewController];
if (index < [viewControllersArray count] - 1) {
return [viewControllersArray objectAtIndex:(index + 1)];
}
}
return nil;
}
Now what the problem is, is that when you swipe, the next viewcontrollers gets load, etc. But they don't get unloaded. So I'm searching for a way to unload the viewcontrollers that you already passed to save memory, and when you swipe back they will get reloaded.
your problem is that you hold on to your view controllers in the viewControllersArray. The array holds a strong reference to each object that you add to it. The easiest way to save memory is to just create the requested view controller on the fly in both the pageViewController:viewControllerBeforeViewController: and the pageViewController:viewControllerAfterViewController: data source methods. That way, only the page view controller will have a strong reference to the view controller and will release (dealloc) it once it is moved off-screen.
If it's too expensive to create them on-the-fly, you might consider just keeping 3 view controllers in the array: the one currently displayed and the one to the immediate left and the immediate right.
Example:
- (UIViewController *)pageViewController:(UIPageViewController *)pvc
viewControllerBeforeViewController:(MyGreatViewController *)vc
{
NSUInteger index = vc.position - 1; //custom property on your VC
if (index > 0) {
return [[MyGreatViewController alloc] initWithPosition:index];
}
return nil;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pvc
viewControllerBeforeViewController:(MyGreatViewController *)vc
{
NSUInteger index = vc.position + 1; //custom property on your VC
if (index <= MAX_POSITION) {
return [[MyGreatViewController alloc] initWithPosition:index];
}
return nil;
}
Enjoy!
You should not unload View Controller. If memory is low, controllers unload their views. In code, use -(void)viewDidUnload and -(void)didReceiveMemoryWarning (iOS 6) to free your resources. When UIPageViewController will show some page, controller recreate unloaded view and call -(void)viewDidLoad (if you customize your controllers manually).

How to set text at one page and image at another page in UIPageViewController

In my iPad app. I'm working on UIPageViewController I want to set text at one page and image at another page. I have tried a lot and googled,but I did not find any solution. It's killing my time so if any one have worked on it please guide me and post sample code.
I have used the code which is there in below link
http://www.ioslearner.com/implementing-uipageviewcontroller-programatically-without-storyboarding/
here is the sample code. i used contentviewcontroller.
i want to use two view controllers with UIPageViewController
- (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 labelContents]];
NSUInteger currentIndex2 = [self.imagesArray2 indexOfObject:[(ContentViewController *)currentViewController imageContents]];
if(currentIndex == 0 || currentIndex %2 == 0 || currentIndex2 == 0||currentIndex2 %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;
}
}
If I understood well then add an imageView in ContentPageView.xib file and make an outlet of. Set ImageView and label hidden or visible on your required pages.
Following steps can be helpful
Keep UILabel and and UIImageView in ContentViewController
Add UIImage OR NSString in the modelArray.
When you make a new ContantViewController check if object in model is image or string using following.
if ([myObject class] == [UIImage class])
{
//HERE ASSIGN IT TO UIIMAGEVIEW.image
}
else
{
//HERE ASSIGN IT TO UILABEL.text
}
post if you need more help....
Sorry if this is not what you are looking for.....

How to set a view to a tag set in code not in Interface Builder?

I have an app that uses 4 different xibs, lets call them 1-4
So you start on view 1, if you press the button it takes you to view 2, on view 2, you have a back button (which takes you to 1) and forward button that takes you to 3 etc
Anyway, I am removing the next page buttons, and have added a swipe control instead of pressing a button, you can swipe to the next page.
However, I need to know how I can call a tagged view, using the swipe.
At the moment, the UIButton for next page is set in IB as tag 1
This is my swipe code (this is page 1 so only has a swipe left)
- (IBAction)swipeLeftDetected:(UIGestureRecognizer *)sender {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
Page2ViewController *UIViewController =
[[Page2ViewController alloc] initWithNibName:#"Page2ViewController~ipad" bundle:nil];
[self presentModalViewController:UIViewController animated:YES];
}else{
Page2ViewController *UIViewController =
[[Page2ViewController alloc] initWithNibName:#"Page2ViewController" bundle:nil];
[self presentModalViewController:UIViewController animated:YES];
Page2ViewController *VC = [[Page2ViewController alloc] initWithNibName:#"Page2ViewController" bundle:nil];
[self presentModalViewController:VC animated:YES];
[self.view removeGestureRecognizer:[self.view.gestureRecognizers lastObject]];
[VC release];
}
}
Whereabout in that code, can I tell it to swipe to tag 1?
Would appreciate any help :)
Thanks,
Chris
---- Updated FAO Rob;
In the appdelegate.m
- (void)swicthView:(int)viewControllerIndex :(CGRect)viewRect {
if (viewControllerIndex < 0 || viewControllerIndex > viewControllers.count) {
//invalid index passed to function - do nothing
}else{
if (subViewForceUseNibSize == NO) {
//pass the view frame size at runtime
if (CGRectIsEmpty(viewRect) || viewControllerIndex == 0) {
//no frame size so force full screen
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
viewRect =CGRectMake(0, 0, 320, 480);
}else{
viewRect = CGRectMake(0, 0, 768, 1024);
}
}
}else{
//force use the nib size, so reduce size of NIB to leave display of NIB main nib below
viewRect = ((UIViewController *)[viewControllers objectAtIndex:viewControllerIndex]).view.frame;
}
}
//swicth our view
if (viewControllerIndex == 0) {
/*
for (UIView *subview in window.rootViewController.view.subviews) {
[window.rootViewController.view sendSubviewToBack:subview];
}
*/
for (int x = 1; x<[viewControllers count]; x++) {
if (((UIViewController *)[viewControllers objectAtIndex:x]).view.superview != nil) {
[window.rootViewController.view sendSubviewToBack:((UIViewController *)[viewControllers objectAtIndex:x]).view];
}
}
[window bringSubviewToFront:((UIViewController *)[viewControllers objectAtIndex:0]).view];
return;
}
if (((UIViewController *)[viewControllers objectAtIndex:viewControllerIndex]).view.superview != nil) {
((UIViewController *)[viewControllers objectAtIndex:viewControllerIndex]).view.frame = viewRect;
[window.rootViewController.view bringSubviewToFront:((UIViewController *)[viewControllers objectAtIndex:0]).view];
[window.rootViewController.view bringSubviewToFront:((UIViewController *)[viewControllers objectAtIndex:viewControllerIndex]).view];
}else{
((UIViewController *)[viewControllers objectAtIndex:viewControllerIndex]).view.frame = viewRect;
[window.rootViewController.view bringSubviewToFront:((UIViewController *)[viewControllers objectAtIndex:0]).view];
[window.rootViewController.view addSubview:((UIViewController *)[viewControllers objectAtIndex:viewControllerIndex]).view];
}
}
Looking at the revised code sample, it is clear that there is a UIAppDelegate method called swicthView [sic] that is used for transitioning between five different view controllers, all of which are loaded simultaneously. Given this structure, it is advised that you have a property to keep track of which of the five pages is loaded, and based on the left or right swipe, invoke swicthView to transition to that controller. Thus:
#interface ViewController ()
#property (nonatomic) NSInteger currentPage;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentPage = 0;
UISwipeGestureRecognizer *gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleLeftSwipe:)];
gesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:gesture];
[gesture release];
gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleRightSwipe:)];
gesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:gesture];
[gesture release];
// the rest of the viewDidLoad
}
- (void)handleLeftSwipe:(UISwipeGestureRecognizer *)gesture {
if (self.currentPage < 4)
{
++self.currentPage;
[UIAppDelegate swicthView:self.currentPage :CGRectZero];
}
}
- (void)handleRightSwipe:(UISwipeGestureRecognizer *)gesture {
if (self.currentPage > 0)
{
--self.currentPage;
[UIAppDelegate swicthView:self.currentPage :CGRectZero];
}
}
Frankly, I'd strong advise retiring the swicthView design and rather employing a custom container view controller. If you watch WWDC 2011 - Implementing a UIViewController containment, you'll see a good introduction about the importance of keeping a view controller hierarchy synchronized with a view hierarchy, and see some practical demonstrations of custom containers.
The original answer, provided below, was based upon the original snippet of code that was performing presentViewController. It turns out that a very different solution was called for, outlined above, but I retain the original answer for historical purposes:
Original answer:
I assume you have the following sort of code in viewDidLoad:
UISwipeGestureRecognizer *gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleLeftSwipe:)];
gesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:gesture];
And then you gesture handler could be:
- (void)handleLeftSwipe:(UISwipeGestureRecognizer *)gesture
{
NSString *nibName;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
nibName = #"Page2ViewController~ipad";
else
nibName = #"Page2ViewController";
Page2ViewController *controller = [[Page2ViewController alloc] initWithNibName:nibName bundle:nil];
// if supporting iOS versions earlier than 5.0, then you should use:
//
// [self presentModalViewController:controller animated:YES];
//
// otherwise you should use presentViewController as done below.
[self presentViewController:controller animated:YES completion:nil];
[controller release];
}
Note, I'm don't remove the gesture (unless you really don't want the gesture there when you return back to this view, which is unlikely). Also note, I'm creating controller, presenting, and releasing.
I'm not understanding your repeated reference to tag properties in this context, as numeric tag values are used for identifying subviews of a view, not for identifying view controller or anything like that. So you say "UIButton for next page is set in IB as tag 1" and later you ask "Whereabout ... can I tell it to swipe to tag 1?" It doesn't make sense to "swipe to a button". You could, though, have the two handlers, the button's IBAction (which I'll call onPressNextButton ... I don't know what you called it) and the handleLeftSwipe call the same method, e.g.:
- (void)handleLeftSwipe:(UISwipeGestureRecognizer *)gesture
{
[self goToNextViewController];
}
- (IBAction)onPressNextButton:(id)sender
{
[self goToNextViewController];
}
- (void)goToNextViewController
{
NSString *nibName;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
nibName = #"Page2ViewController~ipad";
else
nibName = #"Page2ViewController";
Page2ViewController *controller = [[Page2ViewController alloc] initWithNibName:nibName bundle:nil];
[self presentViewController:controller animated:YES completion:nil];
[controller release];
}
References:
presentViewController, the preferred method for modal transitions.
presentModalViewController, the now deprecated method that you use if you need backward compatibility for iOS versions prior to 5.0.
Naming basics in the Coding Guidelines for Cocoa, for advice in naming variables and methods. Note variables generally start with lowercase letters and classes generally start with uppercase letters.

UIPageViewController in iOS6

In iOS6 in the methods viewControllerAfterViewController and viewControllerBeforeViewController if I return nil (for block the page navigation when I am in the first or last page) the app crash with this exception:
'The number of view controllers provided (0) doesn't match the number required (1) for the requested transition'
In iOS5 all works good.
I had the same issue. I found that the cause was replacing the delegate on the UIPanGestureRecognizer of the UIPageViewController, a no-no really. The pan gesture recognizer was calling an undocumented method _gestureRecognizerShouldBegin: (note the leading underscore) that UIPageViewController implements and apparently relies upon to work properly (read: not-crash). I ended up implementing respondsToSelector: and forwardingTargetForSelector: in my class that uses the UIPageViewController to pass the undocumented delegate method on to the UIPageViewController without specifically naming it (and almost certainly earning me an app store review rejection).
-(BOOL)respondsToSelector:(SEL)aSelector {
if ([super respondsToSelector:aSelector])
return YES;
else if ([self.pageViewController respondsToSelector:aSelector])
return YES;
else
return NO;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([super respondsToSelector:aSelector]) {
return nil;
} else if ([self.pageViewController respondsToSelector:aSelector]) {
return self.pageViewController;
}
return nil;
}
My longer term solution will be to rework the use of UIPageViewController such that I don't need to displace the gesture recognizer delegates.
Ah,was wondering why no one has pointed out this bug,which i took almost 2 nights to find out the solution.
OLD CODE(iOS 5.1) : when returning nil on the first and last page you will experience the app crash.It works fine in iOS 5.1,but in iOS 6 it wont.
- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerBeforeViewController:
(UIViewController *)viewController
{
for (UIGestureRecognizer *recognizer in pageController.gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
recognizer.enabled = NO;
}
}
NSUInteger index = [self indexOfViewController:
(MainViewController *)viewController];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
for (UIGestureRecognizer *recognizer in pageController.gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
recognizer.enabled = NO;
}
}
NSUInteger index = [self indexOfViewController:
(MainViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
}
SOLUTION(iOS 6) : After adding the gesture effect to the superview,just call the delegate called -(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer. What i did is quiet simple, computing the speed of the user flipping the first page and last page (i mean using the gesture recognizer) , i denied the swiping.All you need to do is just paste the following code,and you are DONE!.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (pageNum==0) {
if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x > 0.0f) {
//NSLog(#"Swiping to left on 1st page is denied");
return NO;
}
if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x < self.view.frame.size.width/2) {
//NSLog(#"tapping to left on 1st page is denied");
return NO;
}
}
else if(pageNum ==totalNoOfFiles-1)
{
if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x < 0.0f) {
//NSLog(#"Swiping to right on 1st page is denied");
return NO;
}
if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x > self.view.frame.size.width/2) {
//NSLog(#"Tapping to right on 1st page is denied");
return NO;
}
}
return YES;
}
- (UIViewController *)pageViewController:(UIPageViewController*) pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
int index = [self indexOfViewController:(ChildViewController *)viewController];
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
int index = [self indexOfViewController:(ChildViewController *)viewController];
index++;
return [self viewControllerAtIndex:index];
}
This has been well discussed but I have one thing to add. Consider why you were setting the delegate of the gesture recognizers to self. In my case, it was because in some cases I wanted to prevent the gesture recognizers from recognizing, with the delegate's gestureRecognizerShouldBegin:.
But in iOS 6, where this issue arises, there is a whole new way of doing exactly that, by implementing gestureRecognizerShouldBegin: on a UIView. (This is a new UIView instance method in iOS 6.)
Thus I was able to accomplish exactly what I was accomplishing before, without altering the gesture recognizers' delegate.
I had the issue with UIPageViewController crashing with iOS6 with the same error ('The number of view controllers provided (0) doesn't match the number required (1) for the requested transition').
None of the above solutions worked for me but I eventually found that moving the following line from viewDidLoad to viewDidAppear fixed it.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
Totally same issue here.
What I did was a hotfix which just to returns clone of before/afterViewController instead of nil, i.e.
// viewController = before/afterViewController
NSUInteger index = [self indexOfViewController:viewController];
// NOTE: return nil crashes in iOS6
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
This means you can page-curl forever but I had no other choice...
Better solution is always welcome.

How to access the views of the MoreViewController and change their titles?

on language switch within my App, I need to access the views of the MoreViewController in a TabBar and change their titles.
Could anyone pls tell me how to do that?
Your help is much appreciated.
Cols
Here are some snippets that might work for you. Note that all of the below is subject to break on each and every new iOS release at it is not meant to be done.
Customizing the More view title as itself as well as the title while it is being edited by the user.
- (void)customizeTitleViewWithNavigationItem:(UINavigationItem *)navigationItem
{
VASSERT(navigationItem != nil, #"invalid navigationItem supplied", navigationItem);
UILabel *titleView = [[UILabel alloc] initWithFrame:CGRectZero];
titleView.backgroundColor = [UIColor clearColor];
titleView.font = [UIFont boldSystemFontOfSize:20.0f];
titleView.text = navigationItem.title;
[titleView sizeToFit];
navigationItem.titleView = titleView;
[titleView release];
}
This needs to be implemented within the UINavigationController's delegate;
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
if (navigationController == tabBarController_.moreNavigationController)
{
if ([viewController isKindOfClass:NSClassFromString(#"UIMoreListController")])
{
[self customizeTitleViewWithNavigationItem:viewController.navigationItem];
}
else
{
NSLog(#"viewController (%#) does not seem to be a UIMoreListController", viewController);
}
}
else
{
NSLog(#"navigationController (%#) does not seem to be the moreNavigationController", navigationController);
}
}
This needs to be implemented within the UITabBarController's delegate;
- (void)tabBarController:(UITabBarController *)controller willBeginCustomizingViewControllers:(NSArray *)viewControllers
{
//get the second view of the upcoming tabbar-controller
UIView *editView = [controller.view.subviews objectAtIndex:1];
//did we get what we expected, which is a UITabBarCustomizeView?
if (editView != nil && [editView isKindOfClass:NSClassFromString(#"UITabBarCustomizeView")])
{ //yes->get the navigation-view
UIView *navigationView = [editView.subviews objectAtIndex:0];
//is that a navigationBar?
if (navigationView != nil && [navigationView isKindOfClass:[UINavigationBar class]])
{ //yes->...
UINavigationBar *navigationBar = (UINavigationBar *)navigationView;
[self customizeTitleViewWithNavigationItem:navigationBar.topItem];
}
else
{
NSLog(#"the navigationView (%#) does not seem to be a navigationBar", navigationView);
}
}
else
{
NSLog(#"the editView (%#) does not seem to be a UITabBarCustomizeView", editView);
}
}
I was able to change the title of one of the tabBar items for my 6th view controller (the last one in the more list) with the following code:
NSArray *vcs = [(UITabBarController *)self.window.rootViewController viewControllers];
[[vcs.lastObject tabBarItem] setTitle:#"New Title"];
Is this what you want to do?
After Edit: To change these titles after they are set up the first time, you need to re-set the viewControllers property of the tabBar controller. In this code example, I connected a button in my 6th view controller to an action method that changes the titles of 3 of my controllers, 2 in the more list and one in the regular list.
-(IBAction)changeNames:(id)sender {
UITabBarController *tbc = (UITabBarController *)[[UIApplication sharedApplication] delegate].window.rootViewController;
NSArray *vcs = tbc.viewControllers;
[[vcs.lastObject tabBarItem] setTitle:#"New Title"];
[[[vcs objectAtIndex:4] tabBarItem] setTitle:#"New VC"];
[[[vcs objectAtIndex:3] tabBarItem] setTitle:#"New VC2"];
tbc.viewControllers = tbc.viewControllers;
}