I would like to use UIPageControl to navigate between different view controllers. I have 2 view controllers and another main view controller contains the UIPageControl. The 2 view controllers to be navigated between have been added as children to the main view controller. When I currently start the application, I see a blank screen. I used pageControl.currentPage = 0 but I am unable to understand why I am unable to see the views.
Note that I do not want to use UIScrollView in my application.
Edit - I have followed this tutorial http://www.wannabegeek.com/?p=168
But it uses scroll view. I do not want to use scroll view since it is creating some issues with a UIWebView and a CorePlot which I use in the different view controllers.
I seek guidance as to how to modify this tutorial without using scroll view. Thanks!
Adding the other view controllers as children just associates the view controllers - it doesn't add the views they manage as subviews. So, when you add them as children you should also add the subviews and set the initial frames. You might also want to add swipe gestures to trigger view animations to animate the frames of the views (though there are several other options, like using a scroll view).
As part of the animation to move the views you manually update the page control (that won't happen automatically).
The loadScrollViewWithPage method is the key. Call this with parameter 0 and then 1 to load both of the pages and add the views as subviews. Just change self.scrollView to self.view.
I would add a (two really, but get 1 working first) swipe gesture to the page control. Then when it is swiped you can move the other views.
The swipe gesture callback:
- (void)swipeRightToLeftGesture:(UIGestureRecognizer *)gestureRecognizer
{
[UIView animateWithDuration:1
animations:^{
for (UIViewController *vc in self.childViewControllers) {
CGRect frame = vc.view.frame;
frame.origin.x -= self.view.frame.size.width;
vc.view.frame = frame;
}
}];
}
This iterations over the child view controllers and animates the frames of their views to move them to the left. The swipe left to right handler will be the same except it will += the width to move the to the right.
[Code Edited - syntax]
UIPageControl doesn't handle swiping or page switching for you. In order to use it, you NEED to have a scroll view (or some other kind of control) to handle the swipes and the actual page switching, and then implement the UIScrollViewDelegate protocol (or whatever protocol you need for the control you're using) to know when the page control should update it's current page position (the currently selected dot). UIPageControl itself doesn't really do a whole lot.
UIPageControl does not do anything much except display some dots. If you want a way of switching between view controllers it sounds like you want UIPageViewController, which is a completely different animal. A nice feature of UIPageViewController is that it can display a UIPageControl as a way of indicating / switching view controllers, which may be just the kind of thing you're after.
Related
I have created a UI which has two child view controllers which slide in as menus from each side of the screen when a button is pressed (think hamburger menu). I have a table view on one and a collection view on the other. Neither will scroll for me or accept touch events. The code below is used to add to the parent container.
sidePanel = sb.instantiateViewControllerWithIdentifier("sidePanel")
self.addChildViewController(sidePanel)
self.view.addSubview(sidePanel.view)
sidePanel.view.center.x += self.view.frame.size.width
sidePanel.view.frame.size.width = 250
sidePanel.view.updateConstraints()
sidePanel.view.layoutIfNeeded()
Any ideas why touch isn't working? I've checked all the obvious solutions (userInteractionEnabled etc.).
The issue is caused by moving the view. The sidePanel is not contained within the view so is not receiving touch events.
I'm trying to add a custom subview to a UITableViewController that doesn't move when the user scrolls.
Is it possible to add such a view?
I'm using a UITableViewController. I would switch to a UIViewController and add a UITableView, but the code relies on the UITableViewController's refreshControl. Adding a container view and having two controllers seems a bit much for such a simple task!
I've also tried adding the content to the UINavigationController view, but unfortunately it doesn't animate smoothly when the view controller appears and disappears.
Is there any way to add a fixed subview to a UITableView?
Edit:
Adding a container view and having two controllers (with an embed segue) is difficult for this project, since I'm updating an older code base and there is a lot of legacy code that depends on the main controller being a UITableViewController. Is there any way to achieve this without an embed segue / two view controllers?
Unfortunately creating a container view for a UITableViewController inside of a UIViewController isn't feasible in this case. That is a great solution if you're early enough in development to arrange things properly.
The best solution I've found, is to transform the floating view's y position on scrollViewDidScroll as per this answer:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
self.header.transform = CGAffineTransformMakeTranslation(0, self.tableView.contentOffset.y);
}
I have seen other answers which modify the floating view's frame, but I think transforming is probably more appropriate.
As an interesting aside, I was able to create a floating view that 'dismisses' when the user scrolls down, but 'floats' when the user pulls to refresh by checking the table view offset before applying the transform.
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!
In my Ipad app, I have a split view in which the detail view is a scroll view...
I have 3 subviews to the scrollview which are tableviews...
How do I use - (void)bringSubviewToFront:(UIView *)view to bring one of the subviews of the scrollview to the front when an action is performed? (since Views are "stacked" in the order they're added with the last one on top). Should I write the code in the subview or in the detailViewController and how do I call it?
You write the code in the controller.
The controller should have access to the table views through IBOutlet properties. Or, if you didn't set them up using a nib, the controller should have been the one to create them.
If a button tap, for example, is responsible for showing a particular table view, the button's action handler method is where you call the bringSubviewToFront: method.
HOWEVER: It sounds like you have three table views on top of each other and are using bringSubviewToFront: to show the current one, and they are all inside a UIScrollView.
Each UITableView contains a UIScrollView. Don't put a scrollview inside a scrollview, they will fight over tracking touches and things will get weird. Just put the three table views inside a plain UIView.
Instead of bringSubviewToFront:, you ought to consider hiding the inactive views (call setHidden:). That way, the hidden views won't be considered part of the responder chain (won't get sent events).