How come doing
[self.navigationController setToolbarHidden:NO];
works (shows the toolbar)
but setting on the view directly
self.navigationController.toolbar.hidden = NO;
doesn't?
This is really an academic question.
Apple has chosen to implement some UI features not as strict properties, even if they almost look like properties and often behave as such. This is what is confusing you.
In this particular case
[self.navigationController setToolbarHidden:NO];
is really short for
[self.navigationController setToolbarHidden:NO animated:NO];
i.e. there are some user interface considerations in addition to just changing a property of one item in the view hierarchy, so the method has to be called.
Related
I'm new to programming and have been developing an iOS app over the last couple months. To me the app looks like its functionally really close to done but I hit an issue today that I think might be a bigger underlying problem.
When I dismissViewController in a navigation controller and go back to the view later it seems to still have the same values. I thought when I do a dismiss that view is destroyed and a new one created later. I've been trying to read about it and I think maybe its a memory cycle thing, the view is kept in memory because there are objects in the view that still have pointers? Is there some general rules on how to handle this? Should I be setting object to nil any time I leave a view controller?How to I make sure I'm not keeping unnecessary things in memory?
If you create your view controller in the following manner, creating it and then pushing it(commented out line) or presenting it, then it is guaranteed that the ViewController will always have an initial state as defined by your initializtion code.
- (IBAction)showViewController: (UIButton *)sender {
MyViewController *vc = [[MyViewController alloc] init];
[self presentViewController: vc animated: YES completion: nil];
//[self.navigationController pushViewController:vc animated:YES]
}
check the viewController you dismiss,if the properties has strong reference(strong,retain) point to parentViewController.
In our app, there are many places that we need to quickly pop a view controller without animation and then push a new one with animation. We would do something like
[navController popViewControllerAnimated:NO];
[navController pushViewController:newVC animated:YES];
Pre-iOS8, this worked fine and the animation showed the new view controller sliding in over the current one, since the navigation controller was first popped without animation.
Now with iOS8, this seems to have changed and what happens now is the top view controller gets popped and the underlying view controller flashes for a split second and then the new view controller gets pushed on. I created a Xcode Project from scratch for iOS8 and tried to test this. Please see this GIF for a demonstration of what it looks like. Every time we tap one of the buttons in the master side of the split view, we perform the above two lines of code on the detail (right) side of the split. Note that the gray view (which is the root of the navigation controller) flashes for a brief second before the new one is pushed.
I have tried searching for any reason why this might have changed in iOS8 but I cant seem to find any documentation on it. Any one have any ideas on what might have caused this change? Any input would be greatly appreciated!
Also, I tried playing around with the code and discovered that doing the following code instead
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:navController.viewControllers];
[viewControllers removeLastObject];
[viewControllers addObject:newVC];
[navController setViewControllers:viewControllers animated:YES];
seems to fix the issue. However, I would prefer not to use this if possible since there are many places in our app that do this 2-line pop-push combo and I would prefer not to have to change it all over the place.
Thank you!
I came up with similar solutions to you, the first is situational however.
First, you could override the back buttons in the stack you're manipulating to pop to root (or whatever controller you want to be root).
Second, you could add a category to UINavigationController basically implementing the code you listed above. That would save you from having to change it everywhere in your app.
-(UIViewController *)popPushViewController:(UIViewController *)controller animated:(BOOL)animated {
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.viewControllers];
[viewControllers removeLastObject];
[viewControllers addObject:controller];
[self setViewControllers:viewControllers animated:animated];
}
I'm planning on implementing the latter if Apple doesn't fix it/respond.
I am working with the project of ios and doing well in it. But now i stuck at one place where i am having three views (Say 1stview, 2ndview, 3rdview). I am navigating to second view from first view and third view using the code line below.
[self.navigationController pushViewController:first view animated:YES];
How can i check on second view wheather i am navigating from first view or third view. So that i can use particular condition on it.
So please help me out regarding this issue. Your help will be much appreciable.
Take a variable in second View controller. When you are creating the object of it, set proper value into it. Later on when it will get pushed, you can use that value to take proper decisions.
In the file of FirstViewController you will write below lines:
SecondController *controller = [[SecondController alloc]init];
controller.flag = 1; //That means you came here from viecontroller 1
[self.navigationController pushViewController:controller animated:YES];
In the file of ThirdViewcontroller you will write below lines:
SecondController *controller = [[SecondController alloc]init];
controller.flag = 3; //That means you came here from viecontroller 3
[self.navigationController pushViewController:controller animated:YES];
Try to arrange thing so that the 2ndView doesn't know about 1stView or 3rdView, but instead just changes it's behavior according to how it was configured. So, let's say that when you're navigating to 2ndView from 1stView, 2nd should display with a green background and when you get there from 3rd it should use blue instead. Rather than telling 2nd which controller preceded it, have the preceding controller just tell 2nd what background color to use. Same goes for any other aspect of 2ndView's behavior.
The benefit of doing it this way is that you can change 1st or 3rd without having to change anything in 2nd, and you can later add a 4thView or 5thView that also use 2ndView without having to change 2ndView.
Implement the method – navigationController:willShowViewController:animated: from the UINavigationControllerDelegate Protocol Reference. Inside this method you can check the navigation stack to get the current view controller using several properties of UINavigationController. An example would be access the visibleViewController property.
As #Apurv pointed out you need some sort of identifier mechanism to be able to know which view controller the call came from. e.g.: viewController.view.tag
I don't know how to ask this more precisely. I have a master/detail and am creating the whole thing programmatically. I subclasses UISplitViewController and populated it with the two controllers, and everything looks as it should until I set splitViewController:shouldHideViewController:inOrientation such that it returns YES in portrait modes.
When I have the master hiding in portrait and portrait upside-down, as expected, it hides. However, I can't add a "Master" button to the nav bar at the top of the detail view in splitViewController:willHideViewController:withBarButtonItem:forPopoverController. This is probably because I have a fundamental misunderstanding of how I'm supposed to accomplish that task.
I followed the Apple examples and did:
barButtonItem.title = NSLocalizedString(#"Master", #"Master");
[detailController.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
I'm not getting any errors, but no button either. I speculate that perhaps it's because what I'm saving as detailController in my subclass is a UINavigationController and not a UIViewController.
Any guidance on this is much appreciated!
Having written this, I realized that there were several errors in wiring this whole thing up:
splitViewController:willHideViewController:withBarButtonItem:forPopoverController really wants you to not only to set the barButtonItem title, but also to add it to the nav bar of the detail controller.
If you ever want to programmatically dismiss the popover, you have to store the popover supplied in splitViewController:willHideViewController:withBarButtonItem:forPopoverController someplace in the master view.
So, the answer to the first part of the question was:
barButtonItem.title = NSLocalizedString(#"Master", #"Master");
[[detailController.topViewController navigationItem] setLeftBarButtonItem:barButtonItem animated:YES];
That got me to the UIViewController that can set a UIBarButtonItem on the navigation bar. I'm sure I could have done this directly on the UINavigationController but didn't immediately see how.
The second, unasked part of this question, deals with what to do with the popover once it's visible. Again, I needed the detail controller to know what the actual popover was so it can be dismissed, so in splitViewController:willHideViewController:withBarButtonItem:forPopoverController, I added code like:
[masterController.navigationItem topViewController].popoverController = pc;
where pc is the value of the argument passed into the delegate method. Then, in my master controller, I have a UITableView and on the didSelectRowAtIndexPath, I simply did this:
if(popoverController)
[popoverController dismissPopoverAnimated:YES];
And that's what I learned in iOS school today :)
There's code below, but this is also a conceptual issue. The question: Are the methods below the right way to move through view controllers? And that's broad, so let me add some specific details.
Say you're in View A. This view presents options to go to View B or View C. After the user makes the choice (i) I want to load the new view and (ii) the user won't be going back to View A. (Also, btw, View A was my initial rootViewController.)
So, say I wire up two buttons in View A, each to load its respective view. Is this an efficient/solid way to do it:
- (IBAction)loadViewB:(id)sender
{
ViewBController *viewBController = [[ViewBController alloc] initWithNibName:Nil bundle:Nil];
[self.view.superview insertSubview:viewBController.view atIndex:0];
[self.view removeFromSuperview];
}
- (IBAction)loadViewC:(id)sender
{
ViewCController *viewCController = [[ViewCController alloc] initWithNibName:Nil bundle:Nil];
[self.view.superview insertSubview:viewCController.view atIndex:0];
[self.view removeFromSuperview];
}
The "insertSubview:atIndex:" method seems to be right, based on Apple's View Controller Programming Guide. Also, the code works. But should I call the "removeFromSuperview"? Or should I just stack them up? (And if anyone has comments about memory management, I'm all ears.)
Also:
I don't want to move through structured data. So, it doesn't seem like using a UINavigationController would be right.
I don't want to temporarily interrupt flow. So, strike a modal view.
I know it's a simple question, but I just want to make sure I've understood the documentation right and have grasped the concept generally.
Thanks ahead of time.
I don't think there's anything wrong with your approach. Just beware that simply [self.view removeFromSuperview] might not cause any memory to be freed up, for example if your app delegate holds a reference to your root view controller. It might be a good idea to give this view-switching responsibility to the app delegate, so it can choose to release the reference to the root view controller if it desires.