I need advice to create user interface like this UI.
Is it better to use UITableView or UICollectionView because its display is row based? But, when I swipe left and right, it has different section like the collection view horizontal scroll. It makes me confused because I have never created such a UI. I prefer to use UITableViewController, but when I see the segment, I can't use segmented controller because I can't swipe to change the filter.
1) Create storyboard view controller (UIViewController). Controller's view has two subviews: title view and container view (two IBOutlet views).
2) Title view - it's a top view (in your case - collection view with items).
Container view - it's a view for pageViewController.
You will do something like this:
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:nil];
self.pageViewController.dataSource = self;
self.pageViewController.delegate = self;
[self.pageViewController setViewControllers:#[[self viewControllerForIndex:index]]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
[self addChildViewController:self.pageViewController];
[self.containerView addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
- (UIViewController *)viewControllerForIndex:(NSUInteger)index {
UITableViewController *vc = [[UITableViewController alloc] init];
return vc;
}
Related
I have successfully created a "tutorial" pages that will show up at the first time user open the app. And i am facing the problem on how to move to another "branch" of view controllers in my storyboard.
So basically I have two main flows in my storyboard:
1. Page View Controllers
2. Main app view controllers
As you can see from screenshot below
So, the question is, how to move to my main app flow after the user clicked on the skip button from my UIPageViewController?
In my tutorial page view controller, i have this code to set the page view controllers
- (void)viewDidLoad {
[super viewDidLoad];
self.arrPageTitles = #[#"A",#"B",#"C"];
self.arrPageImages =#[#"1.jpg",#"2.jpg",#"3.jpg"];
// Create page view controller
self.PageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"pageViewController"];
self.PageViewController.dataSource = self;
TutorialPageContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.PageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
// Change the size of page view controller
self.PageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 30);
[self addChildViewController:self.PageViewController];
[self.view addSubview:self.PageViewController.view];
[self.PageViewController didMoveToParentViewController:self];
}
Or is there any better approach to achieve this? Is my design wrong?
I have searching for any clue but can't find any. Thanks!
First of all I'd recommend you to use different storyboards for Tutorial flow and for main app screens. It's very hard to store everything in single storyboard: you will get some mess when project will have at least 20-30 view controllers.
And you can replace root view controller at any time using this code:
let storyboard = UIStoryboard(name: "<storyboard_name>", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "<vc_name>")
UIApplication.shared.keyWindow?.rootViewController = viewController
I've got an app with a UIPageViewController that is set up like this:
UINavigationController *navController = (UINavigationController *)[self.storyboard instantiateViewControllerWithIdentifier:#"VCNavController"];
self.navViewControllers = [NSArray arrayWithObjects:navController, nil];
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainPageViewController"];
self.pageViewController.dataSource = self;
self.pageViewController.delegate = self;
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
if ([self.navViewControllers count] != 0)
{
[self.pageViewController setViewControllers:#[self.navViewControllers[0]]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO completion:nil];
}
I've implemented all the necessary delegate functions and my MainViewController that is hosting the UIPageViewController is conforming to UIPageViewControllerDataSource and UIPageViewControllerDelegate.
Hierarchy
MainViewController
UIPageViewController
UINavigationController
UIViewController
The last UIViewController in my hierarchy is displayed correctly, however, I cannot swipe the UIPageViewController. Is there an obvious reason I haven't accounted for? I'm thinking perhaps the UINavigationController doesn't work all that well with the delegate functions supplied by the delegate/datasource, or that the UINavigationController interferes with the touches. Nevertheless, I cannot get it to work.
I don't understand why you would need the UINavigationController, is there a specific reason for this? Try removing the UINavigationController and adding the UIViewControllers directly to the UIPageViewController. Then handle UIGestures in the UIPageViewController. This should achieve the desired effect if I understood you correctly.
I am making an app where I want to show the child view controller only within the parent view controller i.e. only in 3/4 part of the parent view controller. I have implemented the following code but the child view controller is filling up the whole parent view controller.
My code is:
- (CardsChildViewController *)viewControllerAtIndex:(NSUInteger)index {
CardsChildViewController *childViewController = [[CardsChildViewController alloc] initWithNibName:#"CardsChildViewController" bundle:nil];
childViewController.index = index;
return childViewController;
}
and on viewDidLoad function I am writing:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
CardsChildViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
}
I made this by taking help from : http://www.appcoda.com/uipageviewcontroller-tutorial-intro/
Try to change pageController's frame to the following.
CGRect frame = self.view.frame;
CGRect insetFrame = CGRectInset(frame, frame.size.width * 1/8, frame.size.height * 1/8);
Your page view controller is the child, not the view controller you create with your viewControllerAtIndex method.
The easiest way to set up a child view controller is to put a container view in your storyboard, make the child view controller a separate scene, and control-drag an embed segue from the container view to the view controller that you want to be the child.
When you do that the compiler does all the work to set up the connections to manage the child correctly. Your parent view controller's prepareForSegue method will fire when the view is first loaded and the child is installed. At that point you can hook up any outlets, delegate connections, or whatever else you might need.
Failing that, you can adjust the frame of your page view controller's view before adding it as a subview, as #ErAdhish suggests. But you will have a bunch of setup to do in order to forward housekeeping messages from the parent to the child.
I am trying to use UIPageViewController to create walkthrough screens with three separate UIViewControllers. As a summary, I have four View Controllers and a Page View Controller in my storyboard. One view controller act as base view (XYZViewController.h/m) and other three act as sub views that loads inside base view controller. Below shows how the XYZViewController.h displays roughly.
#import <UIKit/UIKit.h>
#import “XYZPageContentViewController.h"
#import "XYZPageTwoContentViewController.h"
#import "XYZPageThreeContentViewController.h"
#interface XYZViewController : UIViewController <UIPageViewControllerDataSource>
#property (strong, nonatomic) UIPageViewController *pageViewController;
#end
Following code snippet shows the viewDidLoad method of XYZViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
XYZPageContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.pageViewController.view.frame = CGRectMake(0, 60, self.view.frame.size.width, self.view.frame.size.height - 60);
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
bottomView.layer.zPosition = 1;
startBtnOutlet.layer.zPosition = 1;
}
Actually, loading sub views and walkthrough is working perfectly. What is not working is, the button touch up inside event I have declared in base view.
As you can see with the image, there is "Start again" button at the bottom of the screen. At first that view even didn't display when I set page controller view till bottom of the screen and not using z-index option.
Which means having this line
self.pageViewController.view.frame = CGRectMake(0, 60, self.view.frame.size.width, self.view.frame.size.height - 60);
with commenting following lines.
// bottomView.layer.zPosition = 1;
// startBtnOutlet.layer.zPosition = 1;
What I need is limit the page view controller where the gray view begins and available the sub view till end of the screen. I can limit the page view controller frame as mentioned above, then it is apply to my all walkthrough screens as well. What can I do for that?
I have followed this tutorial. Is there any better way to do this?
Found the solution.
Keep comment below lines of codes.
// bottomView.layer.zPosition = 1;
// startBtnOutlet.layer.zPosition = 1;
Add this line below that code, inside viewDidLoad method
[self.view sendSubviewToBack:_pageViewController.view];
I have a view controller set up programmatically which has a child UIPageViewController. This displays various other view controllers that are also set up programmatically. These view controllers' views have translatesAutoresizingMaskIntoConstraints set to YES, but the page view controller's view uses constraints to position itself in the top view controller.
The problem is, when the user rotates the device the page view controller resizes its frame but its child view controllers don't. By didRotateFromInterfaceOrientation, its frame is still from the old orientation. I've verified that the rotation methods are called on the child view controllers, their frame just doesn't change.
I set up the page view controller like this:
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageViewController.view.backgroundColor = [UIColor clearColor];
self.pageViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
self.pageViewController.dataSource = self;
self.pageViewController.delegate = self;
[self.pageViewController willMoveToParentViewController:self];
[self.view addSubview:self.pageViewController.view];
[self addChildViewController:self.pageViewController];
[self.pageViewController didMoveToParentViewController:self];
self.currentPageIndex = 0;
self.currentPageController = [self pageForIndex:self.currentPageIndex];
self.currentPageController.delegate = self;
[self.pageViewController setViewControllers:#[self.currentPageController] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
I've tried calling setNeedsLayout but it's a clumsy rotation animation and the next page that I swipe to is also not resized properly.
Why is the page view controller not resizing its child view controllers, and how do I get it to do this?
Thanks!
First, set up everything with auto layout, programmatically or Storyboard. Then, catch the orientation change in the parent view controller and force the UIPageViewController's view to layout again.
This is my working code (iOS 8.0 and later). childController1 was set up on a Storyboard with all layout using constraints, and instantiated with [self.storyboard instantiateViewControllerWithIdentifier:#"ChildControllerId"].
- (void)createPageController
{
...
pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
pageController.dataSource = self;
[pageController setViewControllers:#[childController1] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
[self addChildViewController:pageController];
[self.view addSubview:pageController.view];
UIView *pageView = pageController.view;
pageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[pageView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(pageView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[pageView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(pageView)]];
[pageController didMoveToParentViewController:self];
...
}
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[pageController.view setNeedsLayout];
[super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
}
Hope it helps.
Adding a controller's view directly into another controller's view and expecting the rotation and life cycle methods to be called isn't the best policy. I kind of feel adding it to the controller hierarchy could work, but I can't comment much on it.
I tested with a tab bar navigator and my UIPageViewController got the events along with the controller that was being shown in the UIPageViewController. I'm guessing that if I push the controller into a UINavigationController I'll get the same result.
I'd recommend you to show your UIPageVIewController in a more standard way than adding it's view to another controller's view.