UIViewController transition - objective-c - objective-c

I have UIViewControllers A and B, they are allocated in AppDelegate. I need to apply transition to them. How to transit them without reallocating and replacing UIViews?
This code calls from my UIBarButtonItem in UINavigationController:
[UIView transitionFromView:self.view //UIViewController A
toView:appDelegate.secondViewController.view //UIViewController B
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromLeft
This method replaces UIViews in my UIViewControllers, and I can transit them back, or just don't know how to do that. Can you tell me how to do this?

If you're in iOS 5 world and want to jump between various view controllers, you might want to pursue View Controller Containment. Or check out WWDC 2011 session 102.
View controller containment basically assumes that you have some parent view controller which is governing the navigation between multiple child controllers. In your case, the parent view would be one with the navigation bar with the button on it.
Update:
If you pursue containment, you could create a parent view controller that has a nav bar with the button on it. When you load that view, you can add the first child view. Thus viewDidLoad might look like:
- (void)viewDidLoad
{
[super viewDidLoad];
// this is my model, where I store data used by my view controllers
_model = [[MyModel alloc] init];
// let's create our first view controller
OneViewController *controller = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
// pass it our model (obviously, `model` is a property that I've set up in my child controllers)
controller.model = _model;
// let's put the new child in our container and add it to the view
[self addChildViewController:controller];
[self configureChild:controller];
[self.view addSubview:controller.view];
[controller didMoveToParentViewController:self];
// update our navigation bar title and the label of the button accordingly
[self updateTitles:controller];
}
The configureChild just does final configuration. As a matter of convenience, I frequently will have a UIView that I've set up in IB (in this case, called childView) which I use for setting up the frame, which gets me out of the world of manually creating frames, but you can do it any way you want:
- (void)configureChild:(UIViewController *)controller
{
// configure it to be the right size (I create a childView in IB that is convenient for setting the size of the views of our child view controllers)
controller.view.frame = self.childView.frame;
}
This is the action if you touch the button in the navigation bar. If you're in the first controller, set up the second controller. If you're in the second controller, set up the first one:
- (IBAction)barButtonTouchUpInside:(id)sender
{
UIViewController *currentChildController = [self.childViewControllers objectAtIndex:0];
if ([currentChildController isKindOfClass:[OneViewController class]])
{
TwoViewController *newChildController = [[TwoViewController alloc] initWithNibName:#"TwoViewController" bundle:nil];
newChildController.model = _model;
[self transitionFrom:currentChildController To:newChildController];
}
else if ([currentChildController isKindOfClass:[TwoViewController class]])
{
OneViewController *newChildController = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
newChildController.model = _model;
[self transitionFrom:currentChildController To:newChildController];
}
else
NSAssert(FALSE, #"Unknown controller type");
}
This does the basic transition (including the various containment related calls):
- (void)transitionFrom:(UIViewController *)oldController To:(UIViewController *)newController
{
[self addChildViewController:newController];
[self configureChild:newController];
[self transitionFromViewController:oldController
toViewController:newController
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[self updateTitles:newController];
}
completion:^(BOOL finished){
[oldController willMoveToParentViewController:nil];
[oldController removeFromParentViewController];
[newController didMoveToParentViewController:self];
}];
}
This method just sets up the title in the nav bar in our parent view controller based upon which child is selected. It also sets up the button to reference the other controller.
- (void)updateTitles:(UIViewController *)controller
{
if ([controller isKindOfClass:[OneViewController class]])
{
self.navigationItemTitle.title = #"First View Controller"; // current title
self.barButton.title = #"Two"; // title of button to take me to next controller
}
else if ([controller isKindOfClass:[TwoViewController class]])
{
self.navigationItemTitle.title = #"Second View Controller"; // current title
self.barButton.title = #"One"; // title of button to take me to next controller
}
else
NSAssert(FALSE, #"Unknown controller type");
}
This all assumes you are going to create and destroy controllers as you jump between them. I generally do this, but use a model object to store my data so I keep whatever data I want.
You said you don't want to do this "without reallocating and replacing UIViews": If so, you can also change the above code to create both child view controllers up-front and change the transition to be simply jump between them:
- (void)viewDidLoad
{
[super viewDidLoad];
// this is my model, where I store data used by my view controllers
_model = [[MyModel alloc] init];
// let's create our first view controller
_controller0 = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
_controller0.model = _model;
[self addChildViewController:_controller0];
[self configureChild:_controller0];
[_controller0 didMoveToParentViewController:self];
// let's create our second view controller
_controller1 = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
_controller1.model = _model;
[self addChildViewController:_controller1];
[self configureChild:_controller1];
[_controller1 didMoveToParentViewController:self];
// let's add the first view and update our navigation bar title and the label of the button accordingly
_currentChildController = _controller0;
[self.view addSubview:_currentChildController.view];
[self updateTitles:_currentChildController];
}
- (void)transitionFrom:(UIViewController *)oldController To:(UIViewController *)newController
{
[self transitionFromViewController:oldController
toViewController:newController
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[self updateTitles:newController];
}
completion:^(BOOL finished){
_currentChildController = newController;
}];
}
- (IBAction)barButtonTouchUpInside:(id)sender
{
UIViewController *newChildController;
if ([_currentChildController isKindOfClass:[OneViewController class]])
{
newChildController = _controller1;
}
else if ([_currentChildController isKindOfClass:[TwoViewController class]])
{
newChildController = _controller0;
}
else
NSAssert(FALSE, #"Unknown controller type");
[self transitionFrom:_currentChildController To:newChildController];
}
I've seen it both ways, so you can do whatever works for you.

please see here. You basically want to implement UIViewController containment which is a new feature in iOS5. The link provided above provides some code and a link to a github project.
Good luck
t

I found solution for my problem. This code works on iOS 4.x
[UIView beginAnimations:#"transition" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown
forView:self.navigationController.view
cache:NO];
[self.navigationController
pushViewController:self.alternateView animated:NO];
[UIView commitAnimations];

try
UIViewController* controller1;
UIViewController* controller2;
[controller1 transitionFromViewController:controller1 toViewController:controller2 duration:0.5f options:0 animations:nil completion:nil];
or
if on top of navigationtroller - controller1 then
UINavigationController* nav;
[nav pushViewController:controller2 animated:YES];

Related

Why is my UIViewController being loaded twice? iOS7

I am presenting my custom UIViewController (called "temp") with a custom animation. The UIVC gets called with:
[temp setModalPresentationStyle:UIModalPresentationCustom];
temp.transitioningDelegate = self;
[temp.view setHidden:YES];
[self presentViewController:temp animated:YES completion:nil];
My custom animation is presenting a view modally from right to top-left position of the screen. It is being presented hidden so the user doesn't see the animation. After it reaches the SCREEN_HEIGHT (768) position it is being set to visible and animated (moved) from top to bottom being presented in the middle. The goal was to present a view from top to bottom and dismiss it from top to bottom (like a movie scene). This code is the NOT working one:
- (void)animateTransition:(id)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
NSLog(#" fromViewController %# ",fromViewController);
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
NSLog(#" toViewController %# ",toViewController);
UIView *containerView = [transitionContext containerView];
if (self.presenting)
{
// set starting rect for animation toViewController.view.frame = [self rectForDismissedState:transitionContext];
[containerView addSubview:toViewController.view];
[UIView animateWithDuration:0.5
animations:^{
toViewController.view.frame = CGRectMake(-self.customSize.width, self.yValue, self.customSize.width, self.customSize.height);
}
completion:^(BOOL finished)
{
//HERE IS THE PROBLEM!!!
[toViewController.view setHidden:NO];
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
CGRect variable = [self rectForPresentedState:transitionContext];
CGRect fitToCurrentScreenResolution = CGRectMake(0, 0, variable.size.width, variable.size.height);
toViewController.view.frame = fitToCurrentScreenResolution;
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:YES];
}];
}];
}
else
{
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
fromViewController.view.frame = [self rectForDismissedState:transitionContext];
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:YES];
[fromViewController.view removeFromSuperview];
}
];
}
}
And here is the solution:
- (void)animateTransition:(id)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
NSLog(#" fromViewController %# ",fromViewController);
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
NSLog(#" toViewController %# ",toViewController);
UIView *containerView = [transitionContext containerView];
if (self.presenting)
{
// set starting rect for animation toViewController.view.frame = [self rectForDismissedState:transitionContext];
[containerView addSubview:toViewController.view];
[UIView animateWithDuration:0.5
animations:^{
toViewController.view.frame = CGRectMake(-self.customSize.width, self.yValue, self.customSize.width, self.customSize.height);
}
];
[toViewController.view setHidden:NO];
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
CGRect variable = [self rectForPresentedState:transitionContext];
CGRect fitToCurrentScreenResolution = CGRectMake(0, 0, variable.size.width, variable.size.height);
toViewController.view.frame = fitToCurrentScreenResolution;
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:YES];
}];
}
else
{
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
fromViewController.view.frame = [self rectForDismissedState:transitionContext];
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:YES];
[fromViewController.view removeFromSuperview];
}
];
}
}
My question is simple. Why is my UIVC being presented twice?
I have tried making my custom UIVC a property which is lazy loaded but my app crashes saying that a UIVC = nil can not be presented modally.
I have tried this solution, but it didn't apply to my problem :viewWillAppear being called twice in iOS5
I also did this with no help: Calling presentModalViewController twice?
I could have used a hack but I wouldn't find out why it is happening. So far it seems that when the animation enters the completion BLOCK it calls the view again.
The apple docs say:
A block object to be executed when the animation sequence ends. This
block has no return value and takes a single Boolean argument that
indicates whether or not the animations actually finished before the
completion handler was called. If the duration of the animation is 0,
this block is performed at the beginning of the next run loop cycle.
This parameter may be NULL.
Is the view being drawn again since the next run loop cycle is being started?
NOTE: Even thought the view is being presented twice, the viewDidLoad method is being called only once.
I would like to know why this is happening. There are some stackoverflow questions with the same code but with different usage scenarios having the same problem without a working solution or explanation.
Thank you for any advice/comment.
iOS 8.0, Xcode 6.0.1, ARC enabled
Yeah you are definetely onto it with "chained animation" (see comment from O.P.).
I witnessed a similar problem trying to hide and show the UIStatusBar for various UIViewControllers in my application, e.g. I have a dummy after load screen UIViewController that shows the same image as the load screen, but it has some added animations.
I am using a custom transition, which features a UIViewController that handles the transition from the "from" UIViewController and the "to" UIViewController by adding or removing their views from itself and assigning the "to" UIViewController "control" to itself. So on and so forth.
In the app. delegate,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
I had to instantiate the "initial view controller" and then initialize the transition UIViewController with it. Since there are no "to" UIViewControllers the transition UIViewController must hold an initial UIView of the UIViewController it was initialized with until a transition is triggered.
This was done utilizing,
self.window.rootViewController = self.transitionViewController;
[self.window makeKeyAndVisible];
After the very first transition, there always two UIViews overlaid onto each other. And two UIViewControllers one existing as the current control for the transition UIViewController that was assigned during the transition and the previous UIViewController that remains until the transition completes.
This was the code I was trying to use to show/hide the UIStatusBar, one must have the "View controller-based status bar appearance" set to "YES" in the *-Info.plist file.
- (void)viewDidLoad
{
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
}
- (BOOL)prefersStatusBarHidden
{
return false;
}
Whenever the "return" value was changes from default "false" to "true" regardless of when
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
was triggered, delay, no delay, conditional, etc.; both UIViewControllers, the "to" and "from" were reloaded. At first this was not noticeable, however after implementing an NSURLSession in one of the UIViewControllers that was triggered in the - (void)ViewDidLoad; the problem was clear. The session was executed twice and the graphical content involved was also updated.
I successfully solved the issue in two ways, however I kept the 2nd.
I put everything in -(void)ViewDidLoad; in an if statement and forced it to only be executed once, using an instance variable boolean. The -(void)ViewDidLoad; still loaded twice, however, things that I did not want to execute twice did not.
I transitioned to the UIViewController at which the UIStatusBar hidden state needed to change without using my transitional UIViewController. After the UIStatusBar was shown or hidden, I would reset the "rootViewController" for the app. delegate, once again assigning the transitional UIViewController as always "shown".
I describe how to do this in the following post: https://stackoverflow.com/a/26403108/4018041
Thanks. Hope this helps someone. Please comment on how this could be handled in the future for either the OP or myself.

switch for a TabBarView without StoryBoard

I have a ConnectionViewController and when i click on the Test button, i would like to switch for a tabViewController.
In my ConnectionViewController :
- (IBAction)testButton:(id)sender {
TabBarViewController *tabBarViewController = [[TabBarViewController alloc] init];
[self presentViewController:tabBarViewController animated:YES completion:nil];
}
but when i click on my test button, the TabBarView is black, without anything.
What could i do to fix that ? I would like a modal segue, not a push.
Thx a lot
[EDIT] : The solution is to create a custom segue with a class like CustomSegue.m with this method :
-(void) perform {
ConnectionViewController *src = (ConnectionViewController*) self.sourceViewController;
TabBarViewController *dest = (TabBarViewController*) self.destinationViewController;
[UIView transitionWithView:src.navigationController.view duration:0.2 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
[src presentViewController:dest animated:YES completion:NULL];
}
completion:NULL];
}
The reason why the tab bar controller is black is that it does not have any viewControllers to present. You need to set tabBarController.viewControllers property before you present it. Lets say you want to present a tabBarViewController with viewController1 on the first tab and viewController2 on the second tab.
tabBarViewController.viewControllers = #[viewController1, viewController2];

Pop up UIViewController with an IBAction UIViewController

Is it possible to pop up an UIViewController (xib file) like UIPopOverControl in iPad ?
I have a separate NIB file which is linked to an UIViewController. I want to popup that NIB file along with the button pressed with a customised size (200,200).
Is this possible?
I am trying to get something like this on the iPhone - http://www.freeimagehosting.net/c219p
You can also use one of these custom made clases to show a popup:
https://github.com/sonsongithub/PopupView
https://github.com/werner77/WEPopover
https://github.com/50pixels/FPPopover
Example with FPPopover:
//the view controller you want to present as popover
YourViewController *controller = [[YourViewController alloc] init];
//our popover
FPPopoverController *popover = [[FPPopoverController alloc] initWithViewController:controller];
//the popover will be presented from the okButton view
[popover presentPopoverFromView:okButton];
//release if you arent using ARC
[controller release];
yes it is. load Your pOpOver controller lazily at the point when it is needed. add its view as a subview (you could animate the addition). make its frame size what You need and add the image You have shown as a background subview of the pOpOver controller along with other controls You want in the pop up.
good luck
UPDATE:
alright, ii will show You how ii do this in my app Lucid Reality Check (deployment target iOS4.3).
one can use a UIPopoverController to present another controllers view. what ii do first is to make sure ii always know the current orientation of the device, so ii can reposition the popup on rotation (maybe this works by itself on iOS6?). so in my base controller (from where ii want to show a popup) ii have an instance variable like this:
UIInterfaceOrientation toOrientation;
and also:
UIPopoverController *popover;
UIButton *popover_from_button;
BOOL representPopover;
popover will be reused for all popups, and popover_from_button will hold the button from which the popup is initiated.
then the next code comes into the base controller:
- (void)popoverWillRotate {
if ([popover isPopoverVisible]) {
[self dismissPopover];
representPopover = YES;
}
}
- (void)popoverDidRotate {
if (popover && representPopover) {
representPopover = NO;
[self representPopover];
}
}
these two methods have to be called every time the device is rotated, like this:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
//DLOG(#"willRotateTo %i", toInterfaceOrientation);
toOrientation = toInterfaceOrientation;
if ([Kriya isPad ]) {
[self popoverWillRotate];
}
}
as one can see, first the orientation is captured then popoverWillRotate is called. this would hide the popover during the orientation animation. and after rotating, the popover must be redisplayed like this:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
//DLOG(#"didRotateFrom %i", fromInterfaceOrientation);
//[self layout:toOrientation]; //do some layout if You need
if ([Kriya isPad]) {
[self popoverDidRotate];
}
}
- (void)layout:(UIInterfaceOrientation)toInterfaceOrientation {
//one can do view layout here, and call other controllers to do their layout too
}
now that the orientation changes are worked out, the code for presenting the popover arrives here:
#pragma mark Popovers
- (void)presentPopoverWith:(id)controller fromButton:(UIButton*)button {
if (popover)
[popover release];
if (popover_from_button)
[popover_from_button release];
popover_from_button = [button retain];
popover = [[UIPopoverController alloc] initWithContentViewController:controller];
[popover setDelegate:self];
[self representPopover];
}
- (void)representPopover{
if (popover) {
UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny;
UIViewController *vc = (UIViewController*)[popover contentViewController];
CGSize contentSize = [vc contentSizeForViewInPopover];
if (contentSize.width > 0 && contentSize.height > 0) {
[popover setPopoverContentSize:contentSize animated:NO];
}
//DLOG(#"representPopover rect:%#", [Kriya printRect:popover_from_button.frame]);
[popover presentPopoverFromRect:CGRectOffset(popover_from_button.frame, 0, popover_from_button.frame.size.height + 7.0) inView:self.view permittedArrowDirections:arrowDirection animated:YES];
}
}
- (void)dismissPopover {
if (popover) {
[popover dismissPopoverAnimated:YES];
}
}
finally, if one wants to be notified when the popover is dismissed, the base controller must implement a delegate method:
#pragma mark UIPopoverControllerDelegate
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
//do something important here like drink some water
}
and don't forget to make the base controller a UIPopoverControllerDelegate in its header.
a use case for this way of doing popups would then look like this:
- (void)takeImage {
UIImagePickerController *picker = [[[UIImagePickerController alloc] init] autorelease];
[picker setDelegate:self];
[picker setAllowsEditing:NO];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[picker setSourceType:UIImagePickerControllerSourceTypeCamera];
if ([Kriya isPad]) {
[self presentPopoverWith:picker fromButton:backgroundImageButton];
} else {
//modals on iPhone/iPod
//DLOG(#"takeImage addSubview picker");
[self presentModalViewController:picker animated:YES];
}
} else {
//DLOG(#"no camera");
}
}
this would use an image picker as the content for the popup, but one can use any controller with a valid view. so just do this:
[self presentPopoverWith:popupsContentController fromButton:tappedButton];
one should not have any missing information, :), the method [Kriya isPad] is just this:
+ (BOOL)isPad {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200
// iPad capable OS
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
//this is an iPad
return YES;
}else {
//this is an iPod/iPhone
return NO;
}
#else
//can not pissible be iPad
return NO;
#endif
}
ENJOY!

CATransition also animating the navigationBar

I want to make a custom animation to pop my navigation controller. I only want to animate the view, not the navigationBar. With this code I animate both, the view and the navigationBar. How can I only animate the view??
CATransition* transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
transition.subtype = kCATransitionFromTop;
[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController popViewControllerAnimated:NO];
this code is fired when a custom back button added inside the navigationcontroller bar is pressed.
Here is a code that does custom animation both for back button and when you call popRootViewController: method.
It's a class that extends UINavigationViewController which by itself contradicts Apple's docs also it assigns private variable using KVO, which might stop working as soon as engineers change UINavigationController class so use it a t your own risk.
#import "MyNavigationController.h"
#interface MyNavigationController () <UINavigationBarDelegate> {
// Flag that we will use to avoid collisions between navgiation bar
// when we call popViewControllerAnimated: method directly
BOOL _isPopping;
}
- (UIViewController *)myPopViewControllerAniamted:(BOOL)animated;
#end
#implementation MyNavigationController
- (id)init
{
self = [super init];
if (!self) return nil;
// We can't intercept delegation of the original navigation bar,
// we have to replace it with our own, by assigning new instance to
// the private _navigationBar vairable
UINavigationBar *navigationBar = [[UINavigationBar alloc] init];
navigationBar.delegate = self;
[self setValue:navigationBar forKey:#"_navigationBar"];
return self;
}
// This is the delegate method called when you're about to pop navigation item
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
// If we're in the process of popping items we don't want to reenter
if (!_isPopping) {
[self myPopViewControllerAniamted:YES];
}
return YES;
}
// Similarly we have to override popToRootViewControllerAnimated:
// The only difference would be that we use not previous view as a
// target for the transfition, but the very first view
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
return [self myPopViewControllerAniamted:animated];
}
// Our custom popping method
- (UIViewController *)myPopViewControllerAniamted:(BOOL)animated
{
_isPopping = YES;
// If we got here, we have at least two view controllers in the stack
UIViewController *currentViewController = self.topViewController;
if (animated && self.viewControllers.count > 1) {
UIView *currentView = currentViewController.view;
UIViewController *previousViewController = [self.viewControllers objectAtIndex:self.viewControllers.count - 2];
UIView *previousView = previousViewController.view;
previousView.alpha = 0.0;
[currentView.superview insertSubview:previousView belowSubview:currentView];
// I use UIView just for the sake of the simplicity of this example
// In case of core animation you will have to deal with delegates
// to trigger view controller popping when animation finishes
[UIView animateWithDuration:0.33 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
currentView.alpha = 0.0;
previousView.alpha = 1.0;
} completion:^(BOOL finished) {
[super popViewControllerAnimated:NO];
_isPopping = NO;
}];
} else {
[super popViewControllerAnimated:NO];
_isPopping = NO;
}
return currentViewController;
}
#end
Once again, it was done purely as exercise of what is possible, I would highly recommend reading UIViewController guide, probably Container View Controller can satisfy you needs as a designated way of customizing view controller behaviour.
Hope it helps!

Reading touch events in a QLPreviewController

I've got a QuickLook view that I view some of my app's documents in. It works fine, but I'm having my share of trouble closing the view again. How do I create a touch event / gesture recognizer for which I can detect when the user wants to close the view?
I tried the following, but no events seem to trigger when I test it.
/------------------------ [ TouchPreviewController.h ]---------------------------
#import <Quicklook/Quicklook.h>
#interface TouchPreviewController : QLPreviewController
#end
//------------------------ [ TouchPreviewController.m ]---------------------------
#import "TouchPreviewController.h"
#implementation TouchPreviewController
- (id)init:(CGRect)aRect {
if (self = [super init]) {
// We set it here directly for convenience
// As by default for a UIImageView it is set to NO
UITapGestureRecognizer *singleFingerDTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleDoubleTap:)];
singleFingerDTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:singleFingerDTap];
[self.view setUserInteractionEnabled:YES];
[self.view setMultipleTouchEnabled:YES];
//[singleFingerDTap release];
}
return self;
}
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *) sender {
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[UIView beginAnimations:nil context:NULL];
sender.view.center = tapPoint;
[UIView commitAnimations];
NSLog(#"TouchPreviewController tap!" ) ;
}
// I also tried adding this
- (BOOL)gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*) otherGestureRecognizer {
return YES;
}
#end
Edit: For clarification, this is how I instantiate the controller:
documents = [[NSArray alloc] initWithObjects: filename , nil ] ;
preview = [[TouchPreviewController alloc] init];
preview.dataSource = self;
preview.delegate = self;
//set the frame from the parent view
CGFloat w= backgroundViewHolder.frame.size.width;
CGFloat h= backgroundViewHolder.frame.size.height;
preview.view.frame = CGRectMake(0, 0,w, h);
//refresh the preview controller
[preview reloadData];
[[preview view] setNeedsLayout];
[[preview view] setNeedsDisplay];
[preview refreshCurrentPreviewItem];
//add it
[quickLookView addSubview:preview.view];
Also, I've defined the callback methods as this:
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller
{
return [documents count];
}
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index
{
return [NSURL fileURLWithPath:[documents objectAtIndex:index]];
}
Edit2: One thing i noticed. If I try making swiping gestures, I get the following message. This could shed some light on what is wrong/missing?
Ignoring call to [UIPanGestureRecognizer setTranslation:inView:] since
gesture recognizer is not active.
I think your example code is incomplete. It isn't clear how you are instantiating the TouchPreviewController (storyboard, nib file or loadView.)
I have never used the class so I could be way out in left field.
If you've already instantiated a UITapGestureRecognizer in the parent viewController, it is absorbing the tap events and they aren't passed on to your TouchPreviewController.
I would implement the view hierarchy differently by attaching the UITapGestureRecognizer to the parent viewController and handle presentation and unloading of the QLPreviewController there.
I think you might not have to subclass QLPreviewController by instantiating the viewController from a nib file.
When your parent viewController's UITapGestureRecognizer got an event you would either push the QLPreviewController on the navigation stack or pop it off the navigation stack when done.
Hope this is of some help.