Objective c stop animations of UIImageView on UINavigationController segue - objective-c

I have some strange animations when transitioning between views with UINavigationController. The images start with different frame properties and animate to the ones that i have set each time i segue with UINavigationController. I can't find anything to make that animation go away. Does it have something to do with ViewWillAppear implementation?
- (void)viewDidLoad
{
LoadingView* loadingView;
// adding a loading view
loadingView = [self setViewLoading];
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithTitle:#"≡"
style:UIBarButtonSystemItemDone
target:self.revealViewController
action:#selector(rightRevealToggle:)];
// pan gesture for side menu (Facebook style)
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
self.navigationItem.title = #"Brands";
[self.navigationController setNavigationBarHidden:NO];
self.menusDataArray = self.menusData[TAG_MENUS];
// removing loading view.
[self setViewNotLoading:loadingView];
}
- (void)viewWillAppear:(BOOL)animated
{
// if I don't do this, the swipe gesture for the Facebook stile side menu won't be recognised after pressing back on navigation controller bar
[super viewWillAppear:animated];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}

Related

Pop controller after back bar button is pressed

I have a UINavigationController ans a chain of 3 simple controllers. Each one has a button. When press a button a next controller is Pushed. ViewController1 -> ViewController2 -> ViewController3. When I push a back button on the 3rd view i want to move to the first view. Using of backBarButtonItem is obligatory. Here is the code for second controller:
#import "ViewController2.h"
static BOOL isBackButtonPressed;
#implementation ViewController2
- (void)viewDidLoad {
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:nil action:nil];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
if (isBackButtonPressed) {
[self.navigationController popViewControllerAnimated:YES];
} else {
isBackButtonPressed = YES;
}
[super viewWillAppear:animated];
}
#end
But when I press back button on the third view I return to the second view instead of the first view. Could you help me to return to the first view pressing back button on the third view.
I tried suggestions from answers but they don't help.
Adding a selector to backBarButtonItem doesn't help because it is never called.
Adding a [self.navigationController popToRootViewControllerAnimated:YES] in viewWillDisappear methos also doesn't work. I don't know why. I think that the actual problem is how backBarButtonItem works.
Any other suggestions?
The behaviour I try to achieve exists in the calendar on iPhone. When you rotate iPhone to landscape you get to the weeek view. Then go to the event details, and rotate to the portrait. When you press back button you will get to a day view not to a week view, so a controller with weekview is skipped.
After countless number of tries my solution was simply not use backBarButtonItem! As whatever i do it always goes to previous viewController instead of calling its selector
Instead I use only leftBarButtonItem for navigation, as it guarantees calling my action.
Here an example
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 27, 22)];
[backButton setImage:[UIImage imageNamed:#"backbutton"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
This certainly calls backButtonPressed action.. This works both for IOS 6 and 7
No need to register a new selector for the back button, just do:
-(void)viewWillDisappear{
if ( [self.navigationController.viewControllers containsObject:self] )
//It means that the view controller was popped (back button pressed or whatever)
//so we'll just pop one more view controller
[self.navigationController popViewControllerAnimated:NO];
}
in your ViewController3 viewWillDisappear method
Try using this in your third view controller, this way you check if you have pressed the back button and directly pop to the root view controller which as you stated is your first view controller.
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
[super viewWillDisappear:animated];
}
I had the same problem as you beofre and fixed it like this:
You can capture the back button on the ViewController3 and before poping the view, remove ViewController2 from the navigation stack like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:self action:#selector(customBackPressed:)];
}
-(void)customBackPressed:(id)sender {
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray: navigationController.viewControllers];
for (UIViewController *vc in viewControllers)
{
// If vc is ViewController2 type, remove it
}
navigationController.viewControllers = allViewControllers;
[self.navigationController popViewControllerAnimated:YES];
}
Because ViewController2 is not in the stack anymore, it will jump to ViewController1.
Also, if ViewController1 is the root view controller, you can just do:
[self.navigationController popToRootViewControllerAnimated:YES];

How can a UIViewController inside a UIPageController get touch events?

It seems that the button simply doesn't get pressed.
I tried:
UIPageViewController Gesture recognizers
However, here is the catch
if page transition of UIPageViewController is UIPageViewControllerTransitionStyleScroll then
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
NSLog(#"%#",self.pageViewController.gestureRecognizers.description);
NSLog(#"%#",self.pageViewController.gestureRecognizers.description);
will simply be empty.
I tried adding my own gestureRecoqnizers instead of button into the UIViewControllers's subview. It still doesn't work.
So I have a UIPageViewController that display a UIViewController whose view has some button. How do I get that button pressed?
I also tried both solutions here:
http://emlyn.net/posts/stopping-gesture-recognizers-in-their-tracks
None works. First of all I cannot disable gesture recoqnizers of the UIPageViewController because there is no such thing. In scrollView mode, UIPageViewController doesn't have gesture recoqnizers.
I tried to add my own gesture recoqnizers to my own button but those are never called.
I want the UIPageViewController to still handle swipe though. But not tap.
I can't access the gesture recoqnizers of UIPageViewControllers because self.pageViewController.gestureRecognizers is empty. The UIPageViewController seems to just "absorb" all tap events. So my button doesn't get it.
I can add button in front of UIPageViewController but that button will absorb the swiping action which I want UIPageVIewController to still handle.
By the way if we uses the template from IOS (Create a page based app, and change the transition to scroll the pageviewcontroller.gesturerecoqnizers will be empty
- (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:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];//Turn this to scroll view
self.pageViewController.delegate = self;
SDDataViewController *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;
NSLog(#"%#",self.pageViewController.gestureRecognizers.description); //This is empty
while (false);
}
Maybe you can set up a custom gesture recognizer in your UIPageViewController. This way, you will be able to filter through UITouch Events, by implementing this in your gesture recognizer delegate (the one for your UIPageViewController) :
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch {
if (touch.tapCount > 1) { // It will ignore touch with less than two finger
return YES;
}
return NO;
}
This, you will tell the UIPageViewController gestureRecognizer to not care about touch with one finger. You'll have to try it, but i think it might do the trick. The problem here is you have to ask your user for a two-finger swipe...
Check here for an other answer

Presenting a modal view controller with cross-disolve isn't showing views added in code

When I present a modal view controller over my view storyboard originated root view controller it flashes the story board view, even if it has been altered. My root view controller has a bright green back ground with large white subview across the top that contains a label bound to an IBOutlet. In UIViewController viewDidLoad I am adding a light gray, slightly transparent UIView that covers the entire view as a subview of the viewcontroller's view. I am also setting the label text to be different than that of the Storyboard layout.
When I trigger the modal, either via segue or via presentViewController:completion: what I see is the light gray view over my label then the view added in code seems to become transparent and animate away and the bright green of the storyboard layout shows up and then the modal view cross-dissolves in. When I dismiss the first and present the second in the completion block i see a cross-dissolve to bright green, pop of light as the added view becomes visible again, pop to bright green and the cross-dissolve in of the second view controller's view.
When I dismiss the second view controller then I see cross-dissolve out to the bright green and then the added light gray, semi-transparent view pops back to being visible.
Anyone have any idea how to stop the cross-dissolve from showing the views underneath the view added in code?
This example is made more jarring if you remove the opacity from the overlay view added in viewDidLoad.
https://github.com/jonnolen/ios-cross-disolve-problem
Code snippet and story board layout:
#interface DTViewController (){
BOOL hasShownSegue;
}
#end
#implementation DTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView * view = [UIView new];
view.frame = self.view.bounds;
view.backgroundColor = [UIColor colorWithWhite:.95 alpha:.7];
NSLog(#"View Bounds: %#", NSStringFromCGRect(self.view.bounds));
[self.view addSubview:view];
self.label.text = #"Hello!";
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (!hasShownSegue){
[self performSegueWithIdentifier:#"modal.1.segue" sender:self];
hasShownSegue = YES;
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
DTCallbackViewController *vc = segue.destinationViewController;
vc.completionCallback = ^{
[self dismissViewControllerAnimated:YES completion:^{
DTCallbackViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:#"vc.2"];
vc.completionCallback = ^{
[self dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:vc animated:YES completion:nil];
}];
};
}
Figured it out, I don't really like it but it works:
I put all of the maskable content into a separate view:
And then hide that view when I need it to be "masked".
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"View Bounds: %#", NSStringFromCGRect(self.view.bounds));
self.label.text = #"Hello!";
self.content.hidden = YES;
}
Then animations behave as expected. However, this only works with an opaque mask (which is what I needed).

iOS UISplitViewController's Popover controller button disappear after pushing new view controller in portrait mode

In my UISplitViewController application, I have
RootViewController - view controller in the left pane.
DetailViewController - view controller in the right pane.
When one item (which is in a UITableView) in RootViewController is tapped, new view controller will be set as the following shows:
[detailViewController setViewControllers:[NSArray arrayWithObjects:newViewController, nil] animated:animated];
//detailPane is my DetailViewController
All works pretty well in landscape mode. However, I can't make the UISplitViewController work as what I want in portrait mode, that is, the RootViewController's popover button does not appear appropriately in my DetailViewController when I launch and use the application in portait mode.
When I launch the app in portrait mode, the popover button appears appropriately. But after tapping one item in the popover and a new view controller has been set on detailViewController, the button disappeared. I have to rotate the device to landscape and then back to portrait again to make the button appear again.
I set my UISplitViewController's delegate in my application's AppDelegate as follows:
self.splitViewController.delegate = self.detailViewController
And here is my UISplitViewControllerDelegate implementation
- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc {
NSLog(#"Will hide view controller");
barButtonItem.title = #"Menu";
[self.navigationItem setLeftBarButtonItem:barButtonItem];
self.popoverController = pc;
}
- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
NSLog(#"Will show view controller")
NSMutableArray *items = [self.navigationItem.leftBarButtonItems mutableCopy];
[items removeAllObjects];
[self.navigationItem setLeftBarButtonItems:items animated:YES];
[items release];
self.popoverController = nil;
}
Any hint or help is greatly appreciated.
Thanks.
Just came up with a new solution.
Subclass UINavigationController and implement UISplitViewControllerDelegate. Set an instance of this class as the right ViewController of the splitViewController. Everytime you want to change the detail view controller from the master
NewDetailViewController *newDetailVC = ....// Obtain the new detail VC
newDetailVC.navigationItem.leftBarButtonItem = [[[[self.splitViewController.viewControllers objectAtIndex:1]topViewController]navigationItem ]leftBarButtonItem]; //With this you tet a pointer to the button from the first detail VC but from the new detail VC
[[self.navigationController.splitViewController.viewControllers objectAtIndex:1]setViewControllers:[NSArray arrayWithObject:newDetailVC]]; //Now you set the new detail VC as the only VC in the array of VCs of the subclassed navigation controller which is the right VC of the split view Controller
This works for me and I can avoid defining a hole protocol and setting the master as the delegate, which is a big trade off. Hope it helps.
If you still need it:
http://developer.apple.com/library/ios/#samplecode/MultipleDetailViews/Introduction/Intro.html
What I did to my source (I had similar setup to you) to fix it:
I have the master viewcontroller (UITableViewController in my case) be the delegate of the UISplitViewController. In the two delegate methods for UISplitViewControllers (so this would be in your master viewcontroller implementation) you would save the popupviewcontroller and the barbuttonitem in your class. Now, if you change your details viewcontroller, you do:
self.viewControllers = [NSArray arrayWithObjects:[self.viewControllers objectAtIndex:0], newDetailsViewController, nil];
UIViewController <SubstitutableDetailViewController>*vc = (UIViewController <SubstitutableDetailViewController>*)newDetailsViewController;
[vc invalidateRootPopoverButtonItem:_tableViewController.rootPopoverButtonItem];
[_createReportViewController showRootPopoverButtonItem:_tableViewController.rootPopoverButtonItem];
where we have
#protocol SubstitutableDetailViewController
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
#end
the delegate that each of your detailsViewControllers should adhere to. You would implement like this:
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
self.navigationItem.leftBarButtonItem = barButtonItem;
}
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
self.navigationItem.leftBarButtonItem = nil;
}
Let me know if this helps you.
I liked Nekto's solution, but it misses one key problem.
It's not clear what action: selector will cause the UISplitViewController to show the MasterViewController in a popover. When I finally figured this out, by examining the BarButtonItem in the debugger, I realized why it was so tricky to figure this out: the action: selector isn't documented anywhere in Apple's iOS SDK. Oops.
Try this:
UIBarButtonItem *showListView = [[UIBarButtonItem alloc] initWithTitle:#"List" style:UIBarButtonItemStyleBordered target:[self splitViewController] action:#selector(toggleMasterVisible:)];
[[detailViewController navigationItem] setLeftBarButtonItem:showListView];
You may want to surround this code with a conditional that checks the window is in in portrait mode, such as if ([self interfaceOrientation] == UIInterfaceOrientationPortrait)
When you are setting new view controllers placed on navigation stack, probably, all navigation buttons are reset. You can manually add appropriate buttons after changing navigation stack.
For example, you can pick code from - (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc where default popover controller button is created:
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:#"Menu" style:UIBarButtonItemStyleBordered target:self action:#selector(appropriateSelector)];
[self.navigationItem setLeftBarButtonItem:barButtonItem];
self.popoverController = pc;

Objective C: Part of UITableView hidden by Tab bar controller

Hi I have a Uitableview added to a Uiviewcontroller as seen in the code below. The controller is part of a UITabbarcontroller.
The problem here is that the tab bar (at bottom of screen) overlaps with the table view. Is there any way to shorten the table height so that it is not partially hidden by the tab bar?
- (void)viewDidLoad
{
[super viewDidLoad];
self.view = [[[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];
self.feedTableView = [[UITableView alloc]init];
self.feedTableView.frame = self.view.bounds;
self.feedTableView.delegate = self;
self.feedTableView.dataSource = self;
[self.view addSubview: self.feedTableView];
[self getData];
[self.feedTableView reloadData];
}
You have two problems here:
You set self.view in viewDidLoad (it should already have been set in loadView).
You set the view frame to applicationframe, but your view does not take up the whole applicationframe (the tab bar controller's view probably does that).
When you have a tabbarcontroller and set viewcontrollers inside it, the tabbarcontroller sets up all the viewcontrollers (tells them when to load and what size to have and when to show). You should never need to set the view's frames in this case. So removing that first line after [super viewDidLoad]; should fix your problem.