UInavigationController Custom transition is not getting called - objective-c

I'm trying to implement MAOFlipViewController in one of my application. Everything is working properly, but as per my requirement I want to move back to 0 viewcontroller whenever user come to that particular view.
For moving back to 0 view I added following code
NSUInteger targetIndex = self.flipNavigationController.viewControllers.count;
for (int i=(int)targetIndex; i>=0; i--) {
[self.flipNavigationController popViewControllerAnimated:YES];
}
It's working fine. But it is not showing the animation.
How can I use the animation that is defined with UIPanGestureRecognizer to my defauly pop navigation.
Any help will be appreciated. Thanks in advance.

I'm updating my answer, so it can be useful to someone else too.
I used another custom library for flipping the view Flip View. So to root view controller will work like :
-(void)popToRoot{
NSArray *viewControllers=[[self flipNavigationController] viewControllers];
NSUInteger cnt = viewControllers.count;
if(cnt >= 2){
UIViewController *lastVC = [viewControllers objectAtIndex:cnt-1];
UIViewController *secondVC = [viewControllers objectAtIndex:cnt-2];
[lastVC.view flipToView:secondVC.view duration:0.2 removeView:YES direction:JDFlipImageViewFlipDirectionDown completion:^(BOOL finished) {
[self.flipNavigationController popViewControllerAnimated:NO];
[self popToFirst];
}];
}
}

Related

Stacking multiple UIViewControllers and presenting the last one in UINavigationController stack

Possibly simple request here but I can't find the solution and it is bugging me for days.
I'm building simple options page where users could jump to desired page and I'm using UINavigationController instance to manage hierarchy. My storyboard looks like this:
Viewcontrollers are connected with push segues fired on next button, while I use [self.navigationController popViewControllerAnimated:YES] for previous button. If I connect, for instance, button labeled 2 on 5VC with 2VC through push segue, I get to the second page, but if I want to use previous button I will land to options page or 5VC which is something I don't want. Instead, I would like to be able to use previous button to go to first page, while on second page.
The way I see it, if I am on third page (3VC) and I call options page (5VC) and select button 3, system should stack 1VC-2VC and present 3VC, so I would be able to go to 2VC through [self.navigationController popViewControllerAnimated:YES] request.
I think the solution is somehow connected with setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated, but I don't know the syntax how to make things work.
You have 3 cases
Back to one of ancestors in the middle with push
case 5VC=>2VC, 5VC=>3VC:
NSArray *vcs = self.navigationController.viewControllers;
for(NSInteger i = vcs.count - 2; i > 0; i--) {
// find the target and its parent view controller
// i.e. class of 2VC is ViewController2
if([vcs[i] isKindOfClass:[ViewController2 class]]) {
UIViewController *target = vcs[i];
UIViewController *parent = vcs[i - 1];
// pop to its parent view controller with NO animation
[self.navigationController popToViewController:parent animated:NO];
// push the target from its parent
[self.navigationController pushViewController:target animated:YES];
return;
}
}
Back to the root view controller with push
case 5VC=>1VC:
UIViewController *root = self.navigationController.viewControllers.firstObject;
// reset view controllers stack with self as root.
[self.navigationController setViewControllers:#[self] animated:NO];
// push target from self
[self.navigationController pushViewController:root animated:YES];
// reset navigation stack with target as root.
[self.navigationController setViewControllers:#[root] animated:NO];
Push new VC from one of ancestors
case 5VC=>4VC
NSArray *vcs = self.navigationController.viewControllers;
for(NSInteger i = vcs.count - 1; i >= 0; i--) {
// find the parent view controller
if([vcs[i] isKindOfClass:[ViewController3 class]]) {
UIViewController *parent = vcs[i];
// pop to the parent with NO animation
[self.navigationController popToViewController:parent animated:NO];
// perform segue from the parent
[parent performSegueWithIdentifier:#"push4VC" sender:self];
return;
}
}
On the particular your case(5VC=>4VC), you know 3VC is the self's parent, you can get the parent directly:
NSArray *vcs = self.navigationController.viewControllers;
UIViewController *parent = vcs[vcs.count - 2]; // [vcs.count-1] is self.
[self.navigationController popToViewController:parent animated:NO];
[parent performSegueWithIdentifier:#"push4VC" sender:self];

How to keep a class alive like in Tabbed apps

I currently have an app like this that has the drawer interface but when I go into another view everything resets in the current view and never stays the same ?
This is how my code looks like when I switch views.
[self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homeChannel"];
self.slidingViewController.topViewController.view.frame = frame;
[self.slidingViewController resetTopView];
[tableView deselectRowAtIndexPath:indexPath animated:YES]; }];
So my question is "How would I keep the view alive while going into another view such as the tabbed apps "
Such as my tableView, when I go to another view and back to the original one it resets.
Create a NSMutableArray that will keep a pointer to your views. Save the view when you leave it, fetch it when you need it back. Beware! This will consume a lot of memory, but that's what you asked for.
- (void)initArray
{
myViews = [[NSMutableArray alloc] init];
for (int i=0; i<NUMBER_OF_VIEWS; i++)
{
[myViews addObject:[NSNull null]];
}
}
- (void)saveView:(UIView*)view atIndex:(int)index
{
myViews[index] = view;
}
- (UIView*)fetchViewAtIndex:(int)index
{
UIView *view = myViews[index];
if (view == [NSNull null])
return nil;
return view;
}

Xcode Page Based Application Interface Rotation Issue

Start a new page based application project in Xcode
Run the project and turn some pages
Rotate the simulator or device
=> The page view conroller switches back to the first page (january)
How can I prevent step 4. ?
EDIT:
This happens only the first time you rotate after the app started in simulator/device.
I use most recent Xcode 4.5 with iOS 6.0 Simulator and iOS 6 on my testing device.
The same thing happens when I download some other sample code from blogs / etc. Maybe an iOS 6 bug?
EDIT2:
I found out that the first page view that is passed to the UIPageViewController is not dealloced until first rotation. This really looks like a bug to me.
(UPDATE FROM 2014: This seems to have been fixed in iOS7, if you start again from a new Page View application template.)
I've experienced this bug as well. It seems to kick in any time after the main view reappears. My app has several full-screen modals in it, and after those go away the same behaviour occurs.
This happens in XCode 4.5.1 and iOS6 - I 'fixed' this by re-downloading XCode 4.4 and reverting my app back to iOS5.1. Obviously not a great long-term solution. I filed this in Radar and got a note back that it was already logged.
FWIW I noticed that iBooks had this same bug in it right after iOS6 came out, but they seem to have fixed it in a recent update.
Here's how I managed to fix this problem in my app. I'm afraid it's kind of a hacky solution, but it's a quirky bug.
Context: My app is a diary (it's called Remembary) and each page is a different day's diary entry. I have a singleton class called "AppContext" that keeps track of various app-level values, such as the currently showing diary entry object, the current date, and the like. Each day's dataViewController also keeps track of its own diary entry.
The trickiest part was finding a context where I could catch that the app was showing the wrong page. It turns out that this is in [RootViewController viewDidLayoutSubviews], so I added the following to that method:
// get the currently displaying page
DataViewController *currentPage = self.pageViewController.viewControllers[0];
// check if we're showing the wrong page
if ([currentPage myEntry] != [AppContext getCurrentEntry]) {
// jump to the proper page (the delay is needed to ensure that the rotation has fully completed)
[self performSelector:#selector(forceJumpToDate:)
withObject:[AppContext getCurrentEntryDate]
afterDelay:0.5];
}
Here's the forceJumpToDate function, which basically gets a new page based on the current date and tells the pageViewController to jump to it without animating:
- (void) forceJumpToDate:(NSDate *)targetDate {
DataViewController *targetPage = [self.modelController viewControllerForDate:targetDate
storyboard:self.storyboard];
NSArray *viewControllers = [NSArray arrayWithObject:targetPage];
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
}
The user might notice a brief hiccup on the screen as the new page is forced into place, but this only happens if they would otherwise be getting the wrong page, so it's still an improvement.
This was seriously interfering with my ability to upgrade my app to iOS6, so I'm glad I finally figured it out.
Here is my solution:
// RootViewController.m
#import "RootViewController.h"
#import "ModelController.h"
#import "DataViewController.h"
#interface RootViewController ()
#property (readonly, strong, nonatomic) ModelController *modelController;
//added
#property (strong, nonatomic) DataViewController *currentViewController;
#end
#implementation RootViewController
#synthesize modelController = _modelController;
//added
#synthesize currentViewController = _currentViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Configure the page view controller and add it as a child view controller.
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageViewController.delegate = self;
DataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
self.pageViewController.dataSource = self.modelController;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
CGRect pageViewRect = self.view.bounds;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
pageViewRect = CGRectInset(pageViewRect, 40.0, 40.0);
}
self.pageViewController.view.frame = pageViewRect;
[self.pageViewController didMoveToParentViewController:self];
// Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
//added
self.currentViewController = self.pageViewController.viewControllers[0];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (ModelController *)modelController
{
// Return the model controller object, creating it if necessary.
// In more complex implementations, the model controller may be passed to the view controller.
if (!_modelController) {
_modelController = [[ModelController alloc] init];
}
return _modelController;
}
#pragma mark - UIPageViewController delegate methods
/*
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
}
*/
//added
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
self.currentViewController = self.pageViewController.viewControllers[0];
}
- (DataViewController *)currentViewController
{
if (!_currentViewController) _currentViewController = [[DataViewController alloc] init];
return _currentViewController;
}
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
if (UIInterfaceOrientationIsPortrait(orientation) || ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)) {
// In portrait orientation or on iPhone: Set the spine position to "min" and the page view controller's view controllers array to contain just one view controller. Setting the spine position to 'UIPageViewControllerSpineLocationMid' in landscape orientation sets the doubleSided property to YES, so set it to NO here.
//deleted: UIViewController *currentViewController = self.pageViewController.viewControllers[0];
//changed to self.currentViewController
NSArray *viewControllers = #[self.currentViewController];
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:NULL];
self.pageViewController.doubleSided = NO;
return UIPageViewControllerSpineLocationMin;
}
// In landscape orientation: Set set the spine location to "mid" and the page view controller's view controllers array to contain two view controllers. If the current page is even, set it to contain the current and next view controllers; if it is odd, set the array to contain the previous and current view controllers.
// deleted: DataViewController *currentViewController = self.pageViewController.viewControllers[0];
//deleted: NSArray *viewControllers = nil;
//added
NSArray *viewControllers = #[self.currentViewController];
//changed currentViewController to self.currentViewController
NSUInteger indexOfCurrentViewController = [self.modelController indexOfViewController:self.currentViewController];
if (indexOfCurrentViewController == 0 || indexOfCurrentViewController % 2 == 0) {
UIViewController *nextViewController = [self.modelController pageViewController:self.pageViewController viewControllerAfterViewController:self.currentViewController];
viewControllers = #[self.currentViewController, nextViewController];
} else {
UIViewController *previousViewController = [self.modelController pageViewController:self.pageViewController viewControllerBeforeViewController:self.currentViewController];
viewControllers = #[previousViewController, self.currentViewController];
}
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
return UIPageViewControllerSpineLocationMid;
}
#end
What is it you want to prevent? Do you want to prevent rotation? If that is what you want, modify the shouldAutorotateToInterfaceOrientation return value in the RootViewController.m implementation file.
When I did this, the App was able to keep the same page (month) even after rotating the device. I used the simulator and tried on both iPhone and iPad. On the iPad, in landscape mode, it showed two months at a time, but then when rotated back to portrait, still kept the first of the two months that was displayed. This was when I incremented to June. I used the default project without changing a line of code.
Today I found out that in my app I could just use the following to remove the bug (but I have no clue why).
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
...
self.pageViewController.view.hidden = YES;
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
self.pageViewController.view.hidden = NO;
}

How do I get acces to the parent view?

I'm trying to acces the storyboard from code to be able to use this line:
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"];
I use that in my mapview and listview, but want to use it somewhere else to.
The problem is that this view, is a subview of another view.
It's set up as followed:
thisBigView is a view I added in the storyboard and it's is ThisBigViewController
In storyboard I added another view to that view, let's call it thisSmallView. The class is set to ThisSmallView.
ThisSmallView is a custom view where I generate buttons dynamically in on the view. These buttons call the following action:
-(void) radarEventClick:(UIButton *)sender{
SingletonManager *sharedManager = [SingletonManager sharedManager];
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"]; // PROBLEM 1
Event *a;
for(int i = 0; i < [sharedManager.eventsManager count]; i++){
if(sender.tag == ((Event*)([sharedManager.eventsManager objectAtIndex:i])).id_nr){
a = [sharedManager.eventsManager objectAtIndex:i];
break;
}
}
[detail setEvent:a];
[self.navigationController pushViewController:detail animated:YES]; // PROBLEM 2
}
This is code I'm using in my mapviewcontroller to respond to annotationdisclosure clicks, and want to use it here to, but I have 2 problems!
PROBLEM 1: Because thisSmallView is a subview of anotherview, it doesn't have direct access to the storyboard and don't know how to get that access.
PROBLEM 2: thisBigView is embed in a navigationcontroller, but again, I don't know who to access thisBigView, so I can't access the navigationcontroller.
(I think if I could solve problem 2, I would automatically be solving problem 1 to?)
-- EDIT: what I tried --
DetailViewController *detail = [self.superview.storyboard instantiateViewControllerWithIdentifier:#"detail"];
But then I just get 'property storyboard not found on object of type UIView*'
superview
[smallView superview]
http://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html#//apple_ref/occ/instp/UIView/superview
Add the subview as a property of the superview in interface builder (control-drag to the header file). Then add a UIViewController property to the subview. In the superview's code then do
nameOfSubView.superViewPropertyName = self;

transitionFromView and indexes

I'm working with a controller (MAIN) that manage how presents 2 other controllers (A and B) views in its main view.
In MAIN controller View i have a BUTTON created into the MAIN view xib.
BUTTON must be over A and B view.
So this's the view hierarchy structure i need :
MAIN.VIEW
|------A or B view (index 0)
|------BUTTON (index 1)
Here how i create A and B into MAIN:
//MAIN CONTROLLER viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
self.A = [[A alloc] init];
self.B = [[B alloc] init];
//Start from A view visible in MAIN
[self.view insertSubview:self.A.view atIndex:0];
}
After a specific action call i want to insert controller B view and remove A.
I read about transitionFromView and i tried to use it this way:
[UIView transitionFromView:self.A.view
toView:self.B.view
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:^(BOOL finished) {
if (finished) {
//nothing for now...
}
}];
The problem here is that after flip animation B view is over BUTTON and doesn't take exactly the place of A in my hierarchy (A was at index 0)
I can add this code into completion Block :
[self.view sendSubviewToBack:self.B.view];
But i think it's a little tricky solution :P (and it's not a good solution too, because it seems appear effectively at the end of the animation with a bad graphic effect...)
Which is the best way to be sure that A and B are placed at index 0 ?
May be a bug in the SDK. There is a Bug report at OpenRadar with exactly this behaviour.