Best way of handling pan gestures - ios

I want to handle transitions between two instances of the same view controller class, using pan gestures.
I've read almost every transitions can be handled by segues, but is it the same when using pan gestures?
Segues don't seem to be a good fit for this kind of transitions.
For the moment, I have a master view controller which instantiates to sub view controllers. The gesture related code resides in the master view controller.
It works well, but I believe there has to be a solution where all the controller management stuff is done in the storyboard.
What is the best place in my code to handle this kind of transitions?

It sounds to me like what you want is a UIPageViewController set up to scroll instead of page curl (That's a settable property) A page view controller would do all the work for you.
There is a sample app in the Xcode docs called PhotoScroller that shows how to set this up. It does lots of other stuff too, (pinch to zoom and image tiling) but you can ignore that.
If you can't get a UIPageViewController to give you the transition you want then you might need to build your own custom parent view controller class. Embedding a single child using a container VC and an embed segue is trivial. I haven't tried to embed multiple children in the same container yet. using embed segues yet. I've done transitions between child VCs using the "manual" parent/child VC calls that were added in iOS 5

I've found a really great article on the way to implement custom transitions with iOS 7, whether they're interactive or not: http://www.captechconsulting.com/blog/tyler-tillage/ios-7-tutorial-series-custom-navigation-transitions-more
It comes with a very detailed demo.
Animations should be classes implementing the UIViewControllerAnimatedTransitioning or UIViewControllerInteractiveTransitioning protocols.
The code using the gesture recognizer can reside in those classes.
The animation can then be returned in the following methods of your navigation controller delegate:
navigationController:animationControllerForOperation:fromViewController:toViewController:
navigationController:interactionControllerForAnimationController:
This way I can plug the animation to whatever controller I want to.
I hope it could help someone.

Related

Custom Interactive Presentation Transition

I am attempting to create a custom view controller container, which will display a drawer at the bottom of the screen, like the Apple mail or music apps, and I want the user to either be able to tap on it to transition it to fullscreen, or slide it up interactively to reveal the content.
I have the drawer working, using a UIPanGestureRecognizer to slide it.
I can implement this by adding the content controller as a child controller, the content view to the hierarchy and call viewWillAppear: and viewDidAppear: when appropriate.
But I wish to allow the content view controller to animate alongside the swipe (e.g. any animations in viewWillAppear:, like with interactive pop), thus I am looking at custom modal presentation and UIPercentDrivenInteractiveTransition, but I am hitting a wall, and I can't see why this is happening. I have setup a transitioning delegate, returning a custom animation controllers and an interaction controller which is a UIPercentDrivenInteractiveTransition object.
My drawer is part of the container controller's view hierarchy, and naturally I want the content controller's view to be a subview of the drawer. But when calling presentViewController:animated:completion:, a new UITransitionViewsubview is added to the UIWindow, supposedly for where the transition animation should occur. But this kills my UIPanGestureRecognizer and the user cannot perform the swipe to open the drawer.
I tried creating a custom UIPresentationController and other ways to control where in the hierarchy this containerView should be, but I am unable to change the behavior.
Is what I am attempting to do the correct way? What have I missed?
If anyone is interested, here is my framework: LNPopupController
Update 2015.11.19
A demo project as well
Obj-C -> https://github.com/saiday/DraggableViewControllerDemo
Swift 2.x -> https://github.com/ostatnicky/DraggableViewController
Swift 4.x -> https://github.com/satishVekariya/DraggableViewController
Thanks #avdyushin mentioned my blog post.
And yes, my post Draggable view controller? Interactive view controller! is a tutorial just about your topic.
Quoted from my blog post:
How to do it
Design philosophy
There is no two UIViewController in together, instead there's only one UIViewController at a time. When user begin drag on certain subview, present another view controller with custom UIViewControllerAnimatedTransitioning by custom UIPercentDrivenInteractiveTransition protocol.
Technical things
These two protocols is the foundation of customize interactive UIViewController transitions. In case you don't know yet, take a glance before starting.
1. UIViewControllerAnimatedTransitioning protocol
2. UIPercentDrivenInteractiveTransition protocol
An updated answer for iOS 10: this is now trivial using UIViewPropertyAnimator.
UIViewPropertyAnimator is a continuation of the block-based animation concept, taken to its logical continuation - an object oriented wrapper around animations. This allows for much more control, such as controlling the completion of the animation using fractionComplete - precisely what was needed here.

Xcode Swift how to place UIViewController beneath parent UIView Controller

I would like to make similar effect that Facebook app has. So I would like to create navigation slide-out effect.
But when I place UIViewController with menu beneath parent UIViewController with content and then I move parent view aside my menu doesnt show up. Everything I can see is black screen. But menu frame is in the right position.
Any sugestion what should I do?
Content view will be separate UIViewControllers.
I was in the same situation than you and finally I used a custom class which is inspired on FaceBook and Wunderlist apps and is really simple to add into the project:
https://github.com/John-Lluch/SWRevealViewController
The child view controller is always above parent view controller's view.
I think the old and the new view controllers should be siblings and use a parent view controller to add them both.
In fact, the child controllers of native UINavigationController are siblings rather than child-parent relationship.
I would urge you not to do this on your own but to use one of the many libraries out there that have been heavily tested by many contributors. One of the good ones is MMDrawerController but there are many others. Take a look at cococontrols for many more examples.
I've previously implemented a similar mechanic to this using custom transitions, which were introduced in iOS7. For example: could present the menu modally, but using a custom transition have it appear from the left and only take up a proportion of the screen.
Using custom interactive transitions will also allow you to use a pan gesture recogniser to drag your main view controller back on top of your menu - like in the Facebook app.
Here's a good tutorial on Custom Transitions: http://www.thinkandbuild.it/ios7-custom-transitions/

The right way to present view controller with custom transition/animation in iOS<7

I want to present view controllers as simple as with native presentViewController:animated:completion:, dismissViewControllerAnimated:completion: methods, but use custom animations for this.
The common way to do this is to perform custom animation (using view screenshots instead of view itself) and call presentViewController:vc animated:NO completion:… after.
But in this case view controller life-cycle messages are sent not in time. viewWillApper: and viewDidAppear: sent together instead of normal way: the first one just before animation and the last — after. Also they sent with animated parameter set to NO.
The other bad thing is that screenshot of a view (for animation) captured before viewWillApper: called. So it can be some outdated, and this will cause flicking at the end of animation.
I've searched lot of related projects on github/cocoapods (SO answers too), but did not find any with the right life-cycle of presented view controller. There are sometimes even worse solutions like simple views changing without any calls to life-cycle methods.
Possible solutions I know:
iOS7 — I can't, I need it in iOS6 too
Use transition between contained view controllers — Not as simple and reusable as "modal presenting"
Use tricky animations like here — my animation can't be implemented in similar way
While writing this question I thought about custom segue with overridden perform: method. But looks like it's not easy too. At least I don't know how to use it without storyboard and how to do dismiss in easy way.
Do you know how to present view controller with custom animation in iOS6 and make it's life-cycle happy?
Update: finally I've decided to use container view controller. It has ability to customize transitions of it's children and preserve VC's life-cycle. The bad side: it's not as easy as custom transitions in iOS7. It needs container, making xib some harder. And needs additional code to support.

App structure: UIView vs. UIViewController in a paging interface

There are some great answers relating to when to use a UIViewContoller vs. a UIView. For example here and here.
The general gist is that a UIVIewController should be used to control a full screen of data, because
It is designed to handle rotation. Only one UIViewController should be on the screen at once because only the newest will be notified that the rotation occurred.
To stay true to the MVC paradigm, your business logic should live in a UIViewController, while only display and interaction logic should live in a UIView. Presumably business logic relates to what's on the entire screen.
My question is, given this, how do I structure an app with left-right paging between several top-level views?
I want to create an interface with several UITableViews holding a list of recipes. The user swipes left and right to navigate between lists. There is a common menu on the bottom that stays fixed no matter what.
My initial thought is to use one UIVIewController for the UIScrollView, then add subviews from there.
However I'd really like each list to have its own UIViewController, so it can handle its own rotation, and hold business logic like its own REST methods. It would seem to be a pain to have the top-level UIViewController handle the rotation of a child subview, and it would seem to be a violation of MVC to put the logic anywhere else.
Is there a way to structure an app so that multiple UIViewControllers live inside a UIScrollView, or would it appropriate to use a series of top-level UIViewControllers and UISwipeGestureRecognizer to simulate the paging effect of the UIScrollView?
Thanks.
A couple of thoughts:
If targeting iOS 5 and higher, I'd suggest using a UIPageViewController which is designed for precisely this UI (swiping back and forth between UIViewController instances). You would then have a separate UIViewController for each recipe. In iOS 5, you only have UIPageViewControllerTransitionStylePageCurl transition style, but in iOS 6, you also have UIPageViewControllerTransitionStyleScroll.
For more information, see the Page View Controller section of the View Controller Catalog for iOS.
This is much simpler than writing your own scroll view based solution. If you "roll your own" with a UIScrollView, you'll want to remove instances that have scrolled off screen (by registering as the scroll view's delegate and responding to scrollViewDidScroll) so you don't use up memory unnecessarily.
If you do add child view controllers to your scroll view, don't forget to call the appropriate custom container calls. Specifically, as you add view controllers to your scroll view, make sure you call the following (assuming controller is the child controller and self is the main view controller):
[self addChildViewController:controller];
[self.scrollView addSubview:controller.view];
[controller didMoveToParentViewController:self];
And as you respond to scrollViewDidScroll to remove view controllers that are no longer visible, do the appropriate removal calls, e.g.:
[controller willMoveToParentViewController:nil];
[controller.view removeFromSuperview];
[self removeChildViewController:controller];
For information about why it's important to call these custom container calls, see WWDC 2011 video Implementing UIViewController Containment.
I definitely wouldn't recommend using a UINavigationController, though, because it will keep all of the previous pages in memory.
I believe for the requirements you're talking about you could use a UINavigationController. It will give you the "left-right" paging that you want and you can use a UIViewController for each of your recipes.
Also, I think you want to use a UIScrollView because it lets you perform a "swipe" gesture. If that's the case you could also add a UISwipeGestureRecognizer to your view controllers and every time the gesture is recognized call pushViewController:animated: and popViewControllerAnimated: to perform navigation between your recipes.
This is just and idea.
Hope this helps!
As far as I see, there are 2 good options:
Using a root UINavigationController and push/pop child
ViewControllers depending on the direction of the swipe gesture
(recognized by UISwipeGestureRecognizer, just as you said).
Using a root UIViewController with a UIScrollView and adding the child
viewcontroller views as subviews of the scrollview. To handle
orientation changes, you could pass the orientation-change
UIViewController methods (willRotateToInterfaceOrientation,
didRotateFromInterfaceOrientation) to the child controllers, so they
can handle them.
I hope I helped
Firstly, you should be aware that since iOS 5 it's been possible to include child view controllers inside your main view controller.
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
So it seems to me that a good options would be to have a paged UIScrollView as your main controller, and then to put instances of your child controller onto each page.
Since that could all be a little memory intensive, you should really only have three instances at any one time (one being displayed, and one one either side so that they're ready if the user starts to scroll). The Apple demo project shows you how to configure a scroll view like that:
https://developer.apple.com/library/ios/samplecode/PageControl/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007795
Hope that helps.

UIViewControllers in UIScrollView?

I have a design question. I have a scroll view that I want to use as a means of navigation. I.e. the user can slide between screens. At present, I am creating view controllers and putting their views directly on the scroll view:
aViewController.view.frame=CGRectMake(0,0,320,200);
[self.myScrollView addSubview:[aViewController view]]
Etc, for all 3 view controllers.
I know this isn't best practice. But I can't think of any other way of doing it. Ideally I would like to get the same behaviour as a navigation controller except the fact that I'll be using a scroll view. Any help would be greatly appreciated.
You have to be aware that this breaks certain UIViewController behaviors because these child view controllers do not get informed about interface rotation events and their viewWill/Did(Dis)Appear: methods will not work.
The alternative (until iOS 4.x at least) is to not use UIViewController subclasses for these subviews. You could easily create your own custom controller class (derived from NSObject) to manage a child view.
It's debatable whether that approach makes it easier overall, though.
I'm guessing what you're trying to achieve is something similar to this app?
http://itunes.apple.com/us/app/fanhattan-for-ipad/id436928538?mt=8
This app works in the same manner, using a scroll view for navigation. You can analyze that app, you'd get some ideas from it, i'm sure.

Resources