I know that I can do custom transitions between view controllers but I'm not sure that applies here. I have a collection view with paging enabled. When swiping/paging I want to do a custom transition instead of just sliding across. Is this possible and how would I get started?
After some research this doesn't seem possible. The only solution would be displaying a collection view in a UIViewController and using a custom view controller animation on pan to create a new instance of that view controller with the collection view. Clearly not ideal but the only thing I could come up with.
Related
I am quite comfortable in using UICollectionView inside a UIViewController.
I have never used UICollectionViewController before.
I was wondering, is there any situation, where using UICollectionViewController is more appropriate?
In addition to the obvious -
setting your UICollectionView up for you
automatically marking conformation for necessary protocols like UICollectionViewDataSource & UICollectionViewDelegate
A UICollectionViewController gives you a few built in nice features that you otherwise have to do a lot of extra work to get right manually.
installsStandardGestureForInteractiveMovement
The default value of this property is true. When true, the collection view controller installs a standard gesture recognizer (based on a long-press gesture) to manage the reordering of views inside the collection view. The collection view’s data source must declare its support for reordering items by implementing the appropriate methods. Setting this property to false prevents the installation of this gesture recognizer.
useLayoutToLayoutNavigationTransitions
This property helps facilitate transitions between two or more collection view controllers using a navigation controller. When configuring your navigation controller, install a collection view controller as the root object on the navigation stack and set its value for this property to false. When the user selects an item that would require pushing a new collection view controller on the stack, set the value of this property for the new view controller to true. When you do that, the navigation controller performs an animated layout change between the contents of the two collection view controllers instead of the traditional push animation. Similarly, popping the topmost collection view controller off the stack animates back to the previous layout. The navigation controller drives the transition between the view controllers, including the ability to drive the transition interactively.
You must set the value of this property before pushing the collection view controller onto a navigation stack. Do not change the value of this property after the view controller is already on the navigation stack.
It's up to you if your case is a good candidate for using these features or not and based on that you can make the call.
UICollectionView inherits from UIScrollView (just another UIView)
UICollectionViewController inherits from UIViewController. and it implements some protocols. like UICollectionViewDelegate and UICollectionViewDataSource. it means everything is already done for you. and you just have to use it.. but as everything is already done for you. you may not be able to do some stuff. like resizing your collectionView.
if you want full control I recommend you to use your own UIViewController.. and add a UICollectionView as a subview.. so you can place it wherever you want and resize it.. don't forget to implement UICollectionView protocols for delegation and datasource.
You may change some properties with your View controllers. But when you use as part of its' controllers, you can't change it.
For example you Cant change UICollectionView Size of UICollectionViewController .
Is it possible to subclass UICollectionViewTransitionLayout to change the automatic (e.g. non-interactive) animation when pushing/popping a view controller onto/off a UINavigationController stack?
Apples documentation and some articles in the internet suggest it should be possible:
If you want to provide more than just a linear transition from the old to new layout over time, you need to subclass and provide the layout attributes for items yourself
…but I didn’t find a way yet.
I would like to use UICollectionViewController’s useLayoutToLayoutNavigationTransitions to push one collection view controller on top of another (using the same data source). The two view controllers use different subclasses of UICollectionViewLayout which I want to transition using custom UICollectionViewLayoutAttributes that need to be keyframed and modified by the transitionProgress.
I was hoping, just implementing collectionView:transitionLayoutForOldLayout:newLayout: would do the trick, but that method only is called when transitioning interactively.
The best way I could come up with for the pushing is to call startInteractiveTransitionToCollectionViewLayout:completion: on the UICollectionView and pushing the new view controller without animation in the completion handler. For popping the view controller I would hook into -viewWillDisappear: of the view controller and check for the current view controller in the stack; if it is not there, I could again perform startInteractiveTransitionToCollectionViewLayout:completion:
Somehow I guess there is a better way…
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.
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.
What is the best practice for implementing a horizontal scroll view with paging, with one view controller per page?
Is the PageControl example still the best way to implement this now that iOS5 has API for view controller containers/containment?
I know this question is a little old, but as of iOS 6, UIPageViewController has a new transition style property called UIPageViewControllerTransitionStyleScroll which lets you use the page controller for a use case like yours, with simple scrolling between pages instead of the iBooks-style page curl transitions.
You should also watch the 2012 WWDC video number 223 - Enhancing User Experience with Scroll Views where they basically transition an old app that uses the method you describe to the new UIPageViewController with scroll style transition.
My answer would be that it depends on your goal. If you want to make an app as efficiently as possible, I would just use the way it is done in the sample code. I have used it before, and I would do it again.
On the other hand, if your goal is to learn about view controller containment, how it works and how to use it, this might be a good case to try it out. If you go that way, don't forget to check out the WWDC video "Implementing UIViewController Containment" (https://developer.apple.com/videos/wwdc/2011/).
Appears best practice as of iOS5 is to remain using the same method as the PageControl example. That is one controller class (note not a view controller). With child view controllers for each page.
As of now there is no documented better way to implement a paging scroll view using view controller containment methods included in iOS5.
I would suggest a UIPageViewController instead.
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/UIPageViewControllerClassReference.html
I think UIPageViewController can go horizontally:
- (id)initWithTransitionStyle:(UIPageViewControllerTransitionStyle)style navigationOrientation:(UIPageViewControllerNavigationOrientation)navigationOrientation options:(NSDictionary *)options
And
enum {
UIPageViewControllerNavigationOrientationHorizontal = 0,
UIPageViewControllerNavigationOrientationVertical = 1
};
typedef NSInteger UIPageViewControllerNavigationOrientation;
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/UIPageViewControllerClassReference.html
If you add your View Controllers views (myViewControllerOne.view) to the UIScrollView - and then appropriately frame them to appear horizontally - when the user interacts with that view inside of the scroll view methods in the View Controller will be called.
By this I mean if you have MyViewControllerOne.view as a sub view inside of your scroll view, when you scroll to that 'page' of the scroll view and press the button the attached method (IBAction etc) in MyViewControllerOne will be called.
This should give you all the functionality you need such as adding UI Elements, tables, another scroll view, etc.. These elements will be controlled from their originating View Controller.
Hope this helps!