Nesting UIPageViewController in a UINavigationController shows only one page - uinavigationcontroller

I've spend hours on this and can't get it to work. Hope that someone can help me.
I have a UIPageViewController which works perfectly when I add it to my default view when the application starts. This is what I do:
//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.
ContentViewController *contentViewController = [[ContentViewController alloc] initWithNibName:#"ContentViewController" bundle:nil];
contentViewController.labelContents = [self.modelArray objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:contentViewController];
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
[self addChildViewController:self.pageViewController];
[self.view addSubview: self.pageViewController.view];
Now I want to use a navigation controller in my initial view which then pushes the UIPageViewController on the stack, when the user clicks on a button. This works too, but when I'm in landscape mode the UIPageViewController shows only one page (which is horizontally streched) and not two, as it should be. I need to rotate the device to portrait and then back to landscape mode to force the UIPageViewController to show both pages.
I'm adding the UIPageViewController to the navigation controller with:
[self.navController pushViewController:self.pageViewController animated:NO];
When i debug my code I see that without the navigation controller the delegate methods of the UIPageViewController are called on startup. When I push the UIPageViewController on the navigation controller they're not called until I rotate the device.
Does anyone know how to solve this? Thanks in advance for any help/tips on this.

Figured this out with help from this thread.
The key is setting the spineLocation on the pageViewController when you create it through the options dictionary:
options = [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger:UIPageViewControllerSpineLocationMid]
forKey: UIPageViewControllerOptionSpineLocationKey];
And this option should be put inside of a check to see what the current interface orientation is.
In the above mentioned thread, the interface orientation is checked by a ternary operator; I put it in an if-statement so that I can use the same opportunity to tack an extra UIViewController onto the viewControllers array.
If the interface orientation is portrait, 'options' will still be nil when the pageViewController is created, and so it'll go on to create it in portrait mode with only one viewController.
Here's my whole viewDidLoad code in BookViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
_modelController = [[ModelController alloc] init];
DataViewController *startingViewController = [self.modelController viewControllerAtIndex:0
storyboard:self.storyboard];
NSMutableArray *viewControllers = [NSMutableArray arrayWithObjects:
startingViewController,
nil];
NSDictionary *options;
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
DataViewController *secondViewController = [self.modelController viewControllerAtIndex:1
storyboard:self.storyboard];
[viewControllers addObject:secondViewController];
options = [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger:UIPageViewControllerSpineLocationMid]
forKey: UIPageViewControllerOptionSpineLocationKey];
}
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:options];
self.pageViewController.delegate = self;
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
self.pageViewController.dataSource = self.modelController;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// Set the page view controller's bounds
self.pageViewController.view.frame = self.view.bounds;
[self.pageViewController didMoveToParentViewController:self];
// Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
}

Related

Segue to present contents of UITableViewController and NavigationController in iPad popover

I have a segue that works for iPhone code to present the contents of a tableViewController embedded in a Navigation controller.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UINavigationController *nc = (UINavigationController *)segue.destinationViewController;
tvc = (MyTableViewController *)[nc.viewControllers lastObject];
tvc.managedObjectContext = managedObjectContext;
}
tvc is a storyboard item, UITableViewController subclass, embedded in a navigationController. I am trying to find the easiest way to use the same setup on iPad, but present the contents in a popover, with a navigation controller, as opposed to presenting full screen as it's doing now. Else do I need to create a new viewController without using this setup?
I created a custom UIStoryboardPopoverSegue, with the following perform code:
- (void)perform
{
UITableViewController *src = (UITableViewController *) self.sourceViewController;
MyController *dst = (MyController *) self.destinationViewController;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:dst];
UITableViewCell *cell = [src.tableView cellForRowAtIndexPath:[src.tableView indexPathForSelectedRow]];
UIPopoverController *pop = [[UIPopoverController alloc] initWithContentViewController:navigationController];
dst.popoverController = pop;
CGSize size = CGSizeMake(475, src.view.frame.size.height);
pop.popoverContentSize = size;
[pop presentPopoverFromRect:cell.frame
inView:src.tableView
permittedArrowDirections:UIPopoverArrowDirectionUp | UIPopoverArrowDirectionDown
animated:YES];
}
The downside of this is that it requires a seque that is custom to destination view controller. The upside is an easy to implement and support, well functioning popover segue.

how to create a camera overlay view that recognized swipe gestures?

I am trying to create a camera overlay that can recognize swipe gestures to push to other views.
I am wondering if I can still use the UIImagePicker or if I have to use the AVCaptureSessionManager.
Also i would prefer to create the overlay view in the story board is there a way to do that?
can I select a view inside the storyboard controller be the camera overlay and simply present the UIImagePicker on view did load?
I've never used the Storyboard to create a camera overlay, but I have created a xib which works fine. You can create the overlay viewController in the normal (xib) way, complete with gesture recognizers, then you can handle them directly in that VC or use a delegate (most likely the VC which presented the camera).
Some code -
-(void)setupCamera
{
self.picker = [[UIImagePickerController alloc] init];
_picker.sourceType = UIImagePickerControllerSourceTypeCamera;
_picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
self.overlay = [[OverlayViewController alloc] init];
_overlay.delegate = self;
_picker.cameraOverlayView = _overlay.view;
_picker.delegate = self;
[self presentViewController:self.picker animated:YES completion:nil];
}
The overlay -
-(id)init
{
self = [super initWithNibName:#"OverlayViewController" bundle:nil];
if (self)
{
// set up stuff
}
return self;
}
... & some code which handles the swipe -
-(IBAction)swipe:(UISwipeGestureRecognizer *)sender
{
// swipe stuff
[self.delegate doSwipeStuff]; // if you want the delegate to handle it
}
Hope this helps.

Odd UINavigationController and UIPopOverController behavior

I am presenting a popover with my viewcontroller:
UIBarButtonItem *barButton = (UIBarButtonItem*)sender;
SettingsViewController *settingsViewController = [[SettingsViewController alloc] initWithNibName:nil bundle:nil];
[self presentPopoverFromBarButtonItem:barButton andController:settingsViewController andSize:CGSizeMake(RECT_POPOVER_SETTINGS.size.width, RECT_POPOVER_SETTINGS.size.height)];
and inside this view controller I have a button that pushes another view controller.
AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
Now in this view controller I have an action, and when it's done I do:
[self.navigationController popViewControllerAnimated:YES];
Now the strange behavior is that when I pop the view controller, the
viewWillAppear
method gets called in SettingsViewController, then the breakpoints come back to pop method inside my anotherViewController.
This is a universal App so I am using this same setup on the iPhone, but without the initial UIPopOverController. This is functioning properly in the iPhone (with no popovers, pop method gets called first, then viewWillAppear), but on the iPad the viewWillAppear gets called first, then pop.
So my question is, can somebody explain why this is happening?
Thanks.

Using ImagePickerController to get photo from library ok but navigating away then back to same view the image is gone

I am trying to build a view where a user can select an image and it is retained within that view after navigating away from the view and coming back again. As a simple test app, I have created two view controllers. The first view controller has a forward button and uses a modal segue to go to the second view controller. The second view controller has a back button (modal segue back to first view controller) a Choose button and a Image button. So far I have configured the choose button to use the imagepickercontroller to select an image from the library and display onto the button which works no problem. The issue is that when i press the back button and the forward again back to the same screen the image isn't retained i have to reselect it. The intention is for the user to be able to take a picture or video of themselves performing an action and then select it within this app and have the picture displayed there for future reference.
My coding is as follows:
ViewController.h
#interface ViewController : UIViewController
<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
{
IBOutlet UIButton *selectFromPhotoLibraryButton;
IBOutlet UIButton *displayPictureButton;
}
#property (nonatomic, retain) UIButton *selectFromPhotoLibraryButton;
#property (nonatomic, retain) UIButton *displayPictureButton;
- (IBAction)selectPicture;
#end
ViewController.m
#implementation ViewController
#synthesize selectFromPhotoLibraryButton;
#synthesize displayPictureButton;
-(IBAction)selectPicture
{
if([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypePhotoLibrary])
{
UIImagePickerController *picker = [[UIImagePickerController alloc]init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentModalViewController:picker animated:YES];
[picker release];
}
}
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo: (NSDictionary *)info
{
UIImage *image = [[info objectForKey:UIImagePickerControllerOriginalImage] retain];
[displayPictureButton setImage:image forState:UIControlStateNormal];
[self dismissModalViewControllerAnimated:YES];
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *) picker
{
[picker dismissModalViewControllerAnimated:YES];
}
Please let me know if you require any of the other bits of code (its only the default app stuff, no further modification).
As looking to your problem
I think you have to store selected image in NSUserDefault as archived data or something like that.
When u come back to second view then check first in NSUserDefault for image is saved or not.
If u got it then assign it to UIImage object by unarchiving.
As I have not implemented it so can't be sure.
I've just set something like this up myself, I have the chosen image immediately placed in a UIImageView.
First, set up your view controller as a UIImagePickerDelegate:
<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
Then wherever you want to init the UIImagePicker, alloc, init and set its delegate to self:
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker setDelegate:self];
[self presentViewController:imagePicker animated:YES completion:nil];
And then I have the image picker storing the UIImage into the ImageView and also into the NSUserDefaults.
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo: (NSDictionary *)info {
UIImage *image = [[info objectForKey:UIImagePickerControllerOriginalImage] retain];
[userDefaults setObject:image forKey:#"image"];
[userDefaults synchronize];
[self dismissViewControllerAnimated:YES completion:^{
[self updatePreviewImage:image];
}];
}
- (void) imagePickerControllerDidCancel:(UIImagePickerController *) picker {
[self dismissViewControllerAnimated:YES completion:nil];
}
I hope this works for you, and anyone else that view this post.

uinavigationController's toolbar with custom items

I am trying to use the NavigationController's toolbar in my app. This toolbar's toolbarItems are suppose to change depending on which view controller is presented. this is very basic.
What I am trying to do is to add custom buttons to the toolbar using the UIBarButtonItem's "initWithCustomView:" method. However, the button won't show up on the toolbar. But if I create the UIBarButtonItem using the "initWithTitle:" or "initWithBarButtonSystemItem:" method, the button show up. For example, look at the code below:
UIBarButtonItem *item1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemBookmarks target:self action:nil];
UIBarButtonItem *item2 = [[UIBarButtonItem alloc] initWithTitle:#"edit" style:UIBarButtonItemStylePlain target:self action:nil];
NSArray *array = [[NSArray alloc] initWithObjects:item1, item2, nil];
[self setToolbarItems:array];
If this is done, buttons show up on the toolbar. But, if I were to do the following:
UIButton* replyBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[replyBtn setImage:[UIImage imageNamed:#"Reply_Message.png"] forState:UIControlStateNormal];
[replyBtn addTarget:self action:#selector(replyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
replyBtn.frame = CGRectMake(10, 0, 40, 40);
UIBarButtonItem *replyButton = [[UIBarButtonItem alloc] initWithCustomView:replyBtn];
UIBarButtonItem *item1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemBookmarks target:self action:nil];
UIBarButtonItem *item2 = [[UIBarButtonItem alloc] initWithTitle:#"edit" style:UIBarButtonItemStylePlain target:self action:nil];
NSArray *array = [[NSArray alloc] initWithObjects:item1, replyButton, item2, nil];
[self setToolbarItems:array];
In this code, only the item1 and item2 are displayed on the toolbar. replyButton is not shown on the toolbar. There is blank space at the place where the button is suppose to be at.
If this same code used on a regular toolbar that I create instead of NavigationController's toolbar, the button shows up. I am trying to just use one toolbar throughout the app to have the same feel that Apple's Mail application does. The reason that I need to use the "initWithCustomView:" method is because one of the icons is colored and this is the only way it shows up colored on a normal toolbar. Now, I have looked through apple documentation and there isn't any mention of why the "initWithCustomView:" method couldn't be called (or maybe I couldn't find it).
Could please somebody shine some light on this topic to help me point in the right direction. thanks in advance guys.
I can't see the difference from what you tried, but it eventually worked for me, with that code:
//////////////////////////////////////////////////////////////////////////////////////////////
/////>>>> Adding buttons for browsing in the toolbar of the popover's navigation controller///
//////////////////////////////////////////////////////////////////////////////////////////////
//>>>>Create a goBack button
goBack = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *goBackImage = [UIImage imageNamed:#"back1.png"];
[goBack setImage:goBackImage forState:UIControlStateNormal];
[goBack addTarget:self action:#selector(goBackClicked:) forControlEvents:UIControlEventTouchUpInside];
goBack.frame = CGRectMake(0,0,goBackImage.size.width,goBackImage.size.height);
//Create a Bar button to hold this button
UIBarButtonItem *goBackBarButton = [[[UIBarButtonItem alloc] initWithCustomView:goBack] autorelease];
UIBarButtonItem *flex1 = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease];
UIBarButtonItem *addButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:nil action:nil] autorelease];
NSArray *arrayOfButtons = [[[NSArray alloc] initWithObjects:goBackBarButton, flex1, addButton, nil] autorelease];
[self setToolbarItems:arrayOfButtons];
Note few differences from yours (maybe that's where the catch? I'm not sure):
1. my buttons are not allocated locally in the method, but in the class (you know, the property, synthesize, etc)
2. yours
[replyBtn setImage:[UIImage imageNamed:#"Reply_Message.png"] forState:UIControlStateNormal];
where mine looks a bit different
[goBack setImage:goBackImage forState:UIControlStateNormal];
Try these tiny changes, maybe it will work :)
I was having the same problem. It turns out that the toolbar for the UINavigationController resets it's items every time a new view gets pushed on the stack. I was trying to set the toolbar items in the applicationDidFinish function, and it was not working. It worked once I set the toolbar itms in the - (void) viewDidAppear function of the viewController that was being pushed onto the navigation stack.
So, it seems like if you want the navigation controller to keep the same toolbar items throughout the application, you have to set the toolbar items in each view that you push onto the navigation controller after the view appears.
I hope that helps!
I'd wager a guess that the item isn't showing up because its view doesn't have anything in it. Are you sure there's an image called Reply_Message.png in your project? [UIImage imageNamed:#"Reply_Message.png"] might be nil.
You can use one toolbar throughout your app without using the navigation controllers toolbar. Since your code works with a non-navigationController toolbar, this might be the easiest way to accomplish what you want. In your app delegate, try adding a toolbar to window, like this. This way it is persistent throughout the app. Make sure you consider a scheme that will let you access the toolbar to add/remove buttons, or hide/show it from different view controllers.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/////// frame sizes ////////
// compensate for the status bar
int statusBarHeight = 20;
// toolbar
int toolbarWidth = window.frame.size.width;
int toolbarHeight = 30; // I think standard size is 30
int toolbarxOffset = 0;
int toolbaryOffset = window.frame.size.height - tHeight;
CGRect toolbarFrame = CGRectMake(toolbarxOffset,
toolbaryOffset,
toolbarWidth,
toolbarHeight);
/////// toolbar //////////////////////
self.myPersistentToolbar = [[UIToolbar alloc] initWithFrame:toolbarFrame];
[window addSubview:self.myPersistentToolbar];
}

Resources