Current ChildView Controller - iphone

I am trying to implement a SwipeGesture on my viewcontroller.
The issue I am facing currently is that I cannot determine what the currently displayed childview controller is.
The swipeGesture is added to the container view controller. And then must determine what is the currently displayed VC is in the parent-child relationship, then move to the right or left.

If you are talking about custom container view controller, it's the job of the container controller to keep track of this. So, you might have your own #property that keeps track of which one you're on, and adjust it as you transitionFromController as a result of a swipe. So you might have a numeric property that you increment as you go to the right and that you decrement as you go to the left.
In general (if you're just trying to keep track of which "from" controller you're passing to transitionFromViewController), there are two approaches. One approach is implied by listing 14-3 in Adding a child controller of the View Controller Programming Guide.
In this scenario, in viewDidLoad you perform addChildViewController for the first view controller with. You do not, in this scenario, load the other child view controllers via addChildViewController at this point, though, but rather you let your method that does the transitioning take care of that (as in Listing 14-3).
If you do it this way, you can just grab [self.childViewControllers lastObject], and that will be your "current" child controller. So, that might look like:
#interface ViewController ()
#property (nonatomic) NSInteger currentIndex;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIViewController *initialController = ... // set your initial controller
[self addChildViewController:initialController];
initialController.view.frame = self.containerView.bounds;
[self.containerView addSubview:initialController.view];
[initialController didMoveToParentViewController:self];
UISwipeGestureRecognizer *gesture;
gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
gesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.containerView addGestureRecognizer:gesture];
gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
gesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.containerView addGestureRecognizer:gesture];
}
- (void) handleSwipe:(UISwipeGestureRecognizer *)gesture
{
if (gesture.direction == UISwipeGestureRecognizerDirectionLeft && self.currentIndex < (kMaxIndex - 1))
{
self.currentIndex++;
UIViewController *newController = ... // set your new controller
UIViewController *oldController = [self.childViewControllers lastObject];
[self cycleFromViewController:oldController
toViewController:newController
direction:gesture.direction];
}
else if (gesture.direction == UISwipeGestureRecognizerDirectionRight && self.currentIndex > 0)
{
self.currentIndex--;
UIViewController *newController = ... // set your new controller
UIViewController *oldController = [self.childViewControllers lastObject];
[self cycleFromViewController:oldController
toViewController:newController
direction:gesture.direction];
}
}
- (void) cycleFromViewController:(UIViewController*) oldController
toViewController:(UIViewController*) newController
direction:(UISwipeGestureRecognizerDirection)direction
{
[oldController willMoveToParentViewController:nil];
[self addChildViewController:newController];
newController.view.frame = oldController.view.frame;
UIViewAnimationOptions options;
if (direction == UISwipeGestureRecognizerDirectionRight)
options = UIViewAnimationOptionTransitionFlipFromLeft;
else if (direction == UISwipeGestureRecognizerDirectionLeft)
options = UIViewAnimationOptionTransitionFlipFromRight;
[self transitionFromViewController:oldController
toViewController:newController
duration:0.33
options:options
animations:^{
[oldController removeFromParentViewController];
}
completion:^(BOOL finished) {
[newController didMoveToParentViewController:self];
}];
}
The other model that I've seen some people do is to load all of the potential child controllers via addChildViewController in viewDidLoad. I don't personally like this approach, but I know people do it this way and it works fine.
But if you do it this way, you can no longer rely upon childViewControllers to know which controller is the current one. In that case, you have to define your own class property for the currentChildController. So it might look something like:
#interface ViewController ()
#property (nonatomic, strong) UIViewController *currentChildController;
#property (nonatomic) NSInteger currentIndex;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self addChildViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"ChildOne"]];
[self addChildViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"ChildTwo"]];
[self addChildViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"ChildThree"]];
self.currentChildController = self.childViewControllers[0];
self.currentChildController.view.frame = self.containerView.bounds;
[self.containerView addSubview:self.currentChildController.view];
for (UIViewController *controller in self.childViewControllers)
[controller didMoveToParentViewController:self];
UISwipeGestureRecognizer *gesture;
gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
gesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.containerView addGestureRecognizer:gesture];
gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
gesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.containerView addGestureRecognizer:gesture];
}
- (void) cycleFromViewController:(UIViewController*) oldController
toViewController:(UIViewController*) newController
direction:(UISwipeGestureRecognizerDirection) direction
{
self.currentChildController = newController;
newController.view.frame = oldController.view.frame;
UIViewAnimationOptions options;
if (direction == UISwipeGestureRecognizerDirectionRight)
options = UIViewAnimationOptionTransitionFlipFromLeft;
else if (direction == UISwipeGestureRecognizerDirectionLeft)
options = UIViewAnimationOptionTransitionFlipFromRight;
[self transitionFromViewController:oldController
toViewController:newController
duration:0.33
options:options
animations:^{
}
completion:nil];
}
- (void) handleSwipe:(UISwipeGestureRecognizer *)gesture
{
if (gesture.direction == UISwipeGestureRecognizerDirectionLeft && self.currentIndex < (kMaxIndex - 1))
{
self.currentIndex++;
UIViewController *oldController = self.currentChildController;
UIViewController *newController = self.childViewControllers[self.currentIndex];
[self cycleFromViewController:oldController
toViewController:newController
direction:gesture.direction];
}
else if (gesture.direction == UISwipeGestureRecognizerDirectionRight && self.currentIndex > 0)
{
self.currentIndex--;
UIViewController *oldController = self.currentChildController;
UIViewController *newController = self.childViewControllers[self.currentIndex];
[self cycleFromViewController:oldController
toViewController:newController
direction:gesture.direction];
}
}
Those are the two logical approaches.
By the way, the first approach doesn't preclude having your own class property to know which controller you're on, and sometimes that's useful (e.g. if the type of animation you employ is contingent upon whether you're going to the "right" or to the "left), but unless that's the case, you can get away with just looking at [self.childViewControllers lastObject].

In a project i'm working on, I have many View Controllers each with their own Navigation Controller added to a Parent View Controller. I know the initial entry child, but I do the following to find the currently displayed children of the child:
[myKnownViewController.childViewControllers objectAtIndex:0].navigationController.topViewController

Related

UIImagePickerControllerCameraDeviceFront works every other time

This question is very similar to an existing question asked here UIImagePickerControllerCameraDeviceFront only works every other time I tried the solution presented but it didn't work for me
I have a simplest of a project with two view controllers. In the blue one I am displaying a small UIView with a UIImagePickerController in it. NOTE: I am displaying front facing camera when app is launched.
I hit the next button and go to orange view controller and when I hit the back button and come back to blue view controller the UIImagePickerController flips from Front to rear. I guess the reason is that it thinks its busy and moves to the rear cam. If I keep moving back and forth between the view controllers the camera keeps flipping front, back, front, back, front, back...
Here is my code and screenshots, what am I doing wrong?
In my *.h
#import <UIKit/UIKit.h>
#interface v1ViewController : UIViewController <UIImagePickerControllerDelegate>
{
UIImagePickerController *picpicker;
UIView *controllerView;
}
#property (nonatomic, retain) UIImagePickerController *picpicker;
#property (nonatomic, retain) UIView *controllerView;
#end
In my *.m file (This code is only used when blue colored view controller is displayed)
#import "v1ViewController.h"
#import <MobileCoreServices/UTCoreTypes.h>
#implementation v1ViewController
#synthesize picpicker;
#synthesize controllerView;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
controllerView = picpicker.view;
[controllerView setFrame:CGRectMake(35, 31, 250, 250)];
controllerView.alpha = 0.0;
controllerView.transform = CGAffineTransformMakeScale(1.0, 1.0);
[self.view addSubview:controllerView];
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
controllerView.alpha = 1.0;
}
completion:nil
];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
You are dismissing the controller in both the viewDidDisappear and viewWillDisappear methods.
That could be the cause of your problem.
Although I do not have a device with a camera available right now to verify this, it seems that you're not dismissing the pickerview controller correctly. The documentation states that you should call dismissModalViewControllerAnimated: on the parent controller in order to dismiss the picker (though, calls to presented controllers will propagate to presenters - so this is not the problem), but in your case you're not displaying the controller modally in the first place so it will not work.
What I would try in this case is to release the picker instead (if not under ARC) and set it to nil (instead of calling [picpicker dismissModalViewControllerAnimated:YES];).
PS. In fact, it seems that there is a bigger problem with your design. Since each button is set to present the other party modally you are not dismissing any of the controllers ever. The controllers just keep stacking on each other. You should either consider to embed them in a navigation controller and have it handle the hierarchy or just set dismissModalViewControllerAnimated: (dismissViewControllerAnimated:completion: on iOS5+) as the action of the second controller's button instead of a modal segue.
This is a very simple issue. I don't know why this happens exactly, but it seems that UIImagePickerController was designed to recreated each time it's needed instead of keeping any reference to it, which seems logical if you think about it. Basically, you need to recreate and reconfigure your picker each time. Below I've pasted some code to give an image of what I mean.
Simple solution:
- (UIImagePickerController *)loadImagePicker {
UIImagePickerController *picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
return picpicker;
}
and in:
-(void)viewWillAppear:(BOOL)animated{
if(!self.picpicker){
self.picpicker = [self loadImagePicker];
[self.view addSubview: self.picpicker];
}
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.picpicker removeFromSuperview];
self.picpicker = nil;
}

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.

iOS6 autorotation - UIPageViewController

My UI structure:
UINavigationController
--UIViewController(first)
|
|Modal
|
V
UINavigationController (subclass)
--UIViewController (contains UIPageViewController)
For iOS6 new autorotation, I created a subclass of UINavigationController and added:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Only want keep the orientation is portrait.
And I initialize UIPageViewController like:
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
PageDataViewController *startingViewController = [self viewControllerAtIndex:start storyboard:self.storyboard];
NSArray *viewControllers = [NSArray arrayWithObject:startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
self.pageViewController.dataSource = self;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
CGRect pageViewRect = self.view.bounds;
self.pageViewController.view.frame = pageViewRect;
[self.pageViewController didMoveToParentViewController:self];
Then I perform the Modal segue,
if the first UIViewController is portrait, the pageViewController works,
but if the first UIViewController is landscape, I always got exception:
* Assertion failure in -[UIPageViewController willAnimateRotationToInterfaceOrientation:duration:],
/SourceCache/UIKit_Sim/UIKit-2372/UIPageViewController.m:945
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'No view controllers'
** First throw call stack: (0x2a5e012 0x19bae7e 0x2a5de78 0x1450f35 0xd75974 0x9f12b4 0x9f13ea 0x9e4c32 0x9f1372 0x9f13ea 0x9e4c32
0x9f1372 0x9f1751 0x93a1a6 0xbee5f9 0x9ec4f3 0x9ec777 0x9ec7b7
0xd57fe2 0xd49ad9 0xd49b54 0x9b1899 0x9b1b3d 0x13b8e83 0x2a1d376
0x2a1ce06 0x2a04a82 0x2a03f44 0x2a03e1b 0x2c967e3 0x2c96668 0x90265c
0xea7d 0x2685 0x1) libc++abi.dylib: terminate called throwing an
exception
Actually There is another solution for it.
The rotation just calling the willAnimateRotationToInterfaceOrientation method to the rootViewController, you have to implement the method of it.
#interface UIPageViewController (Rotation)
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
#end
//Implementation file
#import "UIPageViewController+Rotation.h"
#implementation UIPageViewController(Rotation)
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {}
#end
In the documentation about "supportedInterfaceOrientations" it is written:
When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window
Did you try setting the rootViewController for the main windows to your current NavigationController classes?
Like in the AppDelegate:
self.window.rootViewController = nvc;
And when your navigationController is changing, set the rootViewController again...
I have "fixed" the problem, by setting view controllers to UIPageViewController in viewWillAppear: again, because when this method is called, spine location for page view controller is correct and based on this information i set one, or two pages (view controllers)
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(self.pageViewController.spineLocation == UIPageViewControllerSpineLocationMid)
{
BookPageViewController *currentViewController = self.pageViewController.viewControllers[0];
NSArray *viewControllers = nil;
NSUInteger indexOfCurrentViewController = [self.modelController indexOfViewController:currentViewController];
if (indexOfCurrentViewController == 0 || indexOfCurrentViewController % 2 == 0) {
UIViewController *nextViewController = [self.modelController pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
viewControllers = #[currentViewController, nextViewController];
} else {
UIViewController *previousViewController = [self.modelController pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
viewControllers = #[previousViewController, currentViewController];
}
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward | UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}
if(self.pageViewController.spineLocation == UIPageViewControllerSpineLocationMin)
{
[self.pageViewController setViewControllers:[NSArray arrayWithObject:[self.pageViewController.viewControllers objectAtIndex:0]] direction:UIPageViewControllerNavigationDirectionForward | UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}
}
and set UIPageViewController Delegate method as below
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
BookPageViewController *currentViewController = self.pageViewController.viewControllers[0];
if(UIInterfaceOrientationIsPortrait(orientation))
{
[self.pageViewController setViewControllers:[NSArray arrayWithObject:currentViewController] direction:UIPageViewControllerNavigationDirectionForward | UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
pageViewController.doubleSided = NO;
return UIPageViewControllerSpineLocationMin;
}
if(UIInterfaceOrientationIsLandscape(orientation))
{
NSArray *viewControllers = nil;
NSUInteger indexOfCurrentViewController = [self.modelController indexOfViewController:currentViewController];
if (indexOfCurrentViewController == 0 || indexOfCurrentViewController % 2 == 0) {
UIViewController *nextViewController = [self.modelController pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
viewControllers = #[currentViewController, nextViewController];
} else {
UIViewController *previousViewController = [self.modelController pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
viewControllers = #[previousViewController, currentViewController];
}
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward | UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
return UIPageViewControllerSpineLocationMid;
}
return UIPageViewControllerSpineLocationMin;
}
I have "fixed" the problem, by setting view controllers to UIPageViewController in viewWillAppear: again, because when this method is called, spine location for page view controller is correct and based on this information i set one, or two pages (view controllers)

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;
}

Remove Unused ViewControllers

When I click in the TTTabBar, the tabs elected sends me to one the the correct ViewController, then I click another TTTabBar it will send me to the other one.....etc, but how can I remove the last ViewController, so they don't just keep stacking on top of each other.
- (void)tabBar:(TTTabBar*)tabBar tabSelected:(NSInteger)selectedIndex
{
if(selectedIndex == 0){
UIViewController* viewController = (UIViewController*)[[TTNavigator navigator] viewControllerForURL:#"tt://Forum"];
[self.view addSubview:viewController.view];
[self.view addSubview:_tabBar];
}else if(selectedIndex == 1) {
UIViewController* viewController = (UIViewController*)[[TTNavigator navigator] viewControllerForURL:#"tt://Profile"];
[self.view addSubview:viewController.view];
[self.view addSubview:_tabBar];
}else if(selectedIndex == 2) {
UIViewController* viewController = (UIViewController*)[[TTNavigator navigator] viewControllerForURL:#"tt://PMs"];
[self.view addSubview:viewController.view];
[self.view addSubview:_tabBar];
}else if(selectedIndex == 3) {
UIViewController* viewController = (UIViewController*)[[TTNavigator navigator] viewControllerForURL:#"tt://Friends"];
[self.view addSubview:viewController.view];
[self.view addSubview:_tabBar];
}
}
I got this to work
if ([viewController isKindOfClass:[UIViewController class]]) {
[viewController.view removeFromSuperview];
} else {
}
when i added it above
if(selectedIndex == 0){
Use:
- (void)removeFromSuperview
To do that in your scenario you'll have to keep a reference to the current view around or create a way of getting a reference to it using a tag.
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIView_Class/UIView/UIView.html
Is TTNavigator a class you wrote? It looks like a singleton manager class to me... Is viewControllerForUrl returning an autoreleased instance of UIViewController? You could implement a stack in the TTNavigator where you would hold the references to all your view controllers and then popping them as you see fit.