This is a follow up on a previous unsolved post.
I have a navcontroller, a tableview controller and a searchbar in the tableview. All this is built in storyboard.
I then build another viewcontroller programmatically when a cell is selected.
I do not want the navigation bar to appear in this last view so, in viewWillDisappear I call
[self.navigationController setNavigationBarHidden:YES animated:animated];
This works just fine IF I select a cell in the main tableView.
However, if the cell is selected after narrowing the data from a search in searchbar, then, the navigationbar will appear in the subsequent view although the call to setNavigationBarHidden is made in viewWillDisappear.
I tried repeating this call in viewDidDisappear and, now, the navigation bar disappears from the subsequent view although it is briefly displayed.
I would like to know what happens between the calls to viewWilldisappear and viewDiddisappear that apparently resets the NavigationBarHidden property?
This is what I found: The NavigationBarHidden property is apparently reset because when the searchbar leaves the view, it will send the navigationbar again on the view.
I am not sure I understand exactly what is happening, but this line of code in my viewWillDisappear solves the problem:
self.searchDisplayController.active=NO;
followed by:
[self.navigationController setNavigationBarHidden:YES animated:animated];
Of course, I would love to hear from someone who really understands what happens here.
Related
I'm trying to load a basic search view overlaying my navigation controller (that is, not pushed on the navigation stack). To achieve this I'm using [self presentModalViewController:vc animated:NO].
Full modal view presentation code:
- (void)searchButtonPressed
{
TMSearchViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"Search"];
[self presentModalViewController:vc animated:NO];
vc.searchDisplayController.searchBar.delegate = self;
}
After the modal view is presented on the screen, the following code—inside the search view's controller—is run:
- (void)viewDidAppear:(BOOL)animated
{
[self.searchDisplayController.searchBar becomeFirstResponder];
[self.searchDisplayController setActive:YES animated:NO];
}
The problem is that when the search view renders, it does the normal thing where it blacks out the table view with a transparent black view, but it doesn't appear to cover the table view's separators, which looks like this:
I really have no idea how to fix it. My best guess is that it is something to do with the modal controller's process of loading a view that I'm not understanding properly. I've tried moving the becomeFirstResponder to the viewDidLoad method. I've also tried reordering the setActive method and the becomeFirstResponder method whilst changing the setActive method to animated:NO and YES.
So far, no avail, I'm clearly not understanding something here and I'm guessing I'm using something in a way that is unintended. Please help, Thanks :)
edit: interestingly enough, when I click the blacked out table view to resignFirstResponder the searchbar, and then click inside the searchbar again to activate becomeFirstResponder it loads fine, without the glitch, how it's meant to.
Did you try self.searchDisplayController.searchResultsTableView.separatorStyle=UITableViewCellSeparatorStyleNone; ??
Okay so after hours of messing around in the code, trying to understand what I'd done wrong, I've finally found the answer, or at least a fix. (I'm still mystified as to what was actually going wrong)
I simply deleted the searchbar from the interface builder in storyboard. And replaced it with another one—which involved reconnecting the searchbar to be the searchDisplayController's searchbar property, of course.
My best guess is that Xcode for whatever reason didn't like the order in which I dragged in and connected the UI elements to interface builder/storyboard.
Hope this helps someone.
So before I push a new viewController onto the stack in a certain view, I set the navigationBar to hidden I notice that it disappears before the next screen gets pushed and the slide animation happens (because I need a UIToolbar at the top).
So question #1: is there a way to push a new view controller and setting the navigationbar to hidden, and not getting the hide animation until after the new view controller is on screen. it looks funny that the navigation bar hides then pushes the new view controller.
Once the new view controller is present, when I pop it off, I set the navigation bar back
[self.navigationController.navigationBar setHidden:NO];
But when it is popped, the navigationbar is not back any more. Is it because this navigationBar is for the current navigationController and not the new one that is being presented after the pop? (question #2)
Question 3: Realizing it isn't showing my navigationBar, in the viewController that gets presented after the pop, in its viewDidAppear, I added
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.navigationController.navigationBar setHidden:NO];
}
which shows the navigationBar, but the view size is incorrect since it seems like once the navigation bar was hidden, the rest of the view took up the empty space, and then the navigationBar is on top of the content. Is there anything I can do about this? Or am I approaching it incorrectly with push and pop?(question #3).
Thanks!
I was running into the very same problem (only in reverse: I was starting from a NavigationBar being hidden and pushing a view where I wanted the NavigationBar visible), and there's actually an extremely easy fix.
Simply replace your calls:
[self.navigationController.navigationBar setHidden:NO];
with
[[self navigationController] setNavigationBarHidden:NO animated:YES];
In my code, I call these statements in the - (void)viewWillAppear:(BOOL)animated methods of each respective View Controller.
I just tried this solution in the order you are using (visible, then hidden), and it seems to work just as well.
Interesting issue. You could try changing the hidden property in viewWillAppear and viewWillDisappear, but it seems like that might not give the desired results either.
Can you present the view controller modally instead of making the navigation bar disappear? If it's the last view controller on the stack, that would be possible. It might also make more sense to the user to see a view controller presented differently. This might indicate to the user that navigating away from this view controller is no longer done with the Back Button. It could be more reasonable than having the navigation bar just disappear.
If you still wanted the view controller to slide in from the right, I don't think it can be done with a modal view controller. But, you could do that by animating a view that fills the screen. (You just add the view with a frame that has origin.x equal to the width of the screen. Then, in the animation, you change origin.x to 0.0. Let me know if you need more detail on this.)
However, I would recommend presenting the view controller in a different manner from the way a view is generally presented by a navigation controller. Because, essentially, you are no longer letting the user navigate away from this view as he/she generally would from within a navigation controller. (So, my response to question #3 would be 'yes'.)
I have a Navigation controller in a storyboard, currently with two screens. Screen1 contains an opening logo and some buttons, and I have hidden the navigation bar at the top using:
[[self navigationController] setNavigationBarHidden:YES];
in viewDidLoad and viewWillAppear: (in viewWillAppear I have it set with animated:YES, so it slides off when coming back from other screens).
When I go to Screen2, I have:
[[self navigationController] setNavigationBarHidden:NO animated:YES];
in the first view controller's viewWillDisappear, and the navigation bar slides in all nicely when that view comes on to the top of the navigation stack.
Problem is, when I tap back, the navigation bar animates off the right side of the screen, but Screen2 stays there, revealing another navigation bar underneath!
I can then tap back again and it will push Screen2 off and the main screen shall return, but this is not behaviour I want to pass on to any users, obviously!
Anyone had this issue before, or have any points on what might be the culprit?
Edit: I just found an error appearing when I run the iOS Simulator:
2011-11-02 19:29:13.548 TestHTML5[10261:f803] Unbalanced calls to begin/end appearance transitions for <LessonViewController: 0x6c5e960>.
This happens when I click the button to go the the second view (LessonViewController).
Hopefully that might be the thing to crack this, anyone know?
I found my IBActions, which contained:
SecondViewController *X = [self.storyboard instantiateViewControllerWithIdentifier:#"X"];
[self.navigationController pushViewController:X animated:NO];
on each were causing the view to double up or something like that. When I commented these two lines out in each IBAction, the problem disappeared.
Thanks heaps to #CodaFi for helping me through possibilities for this, to be honest, this solution doesn't make sense to me, even thought I can see it working here.
I'm writing my first iPhone app and I am trying to figure out how to have a MasterView and DetailsView like in the example. However, instead of using a TableView, I want to use a button on the MasterView to go to the SignUpView. I want the MasterView to NOT have a navigation bar but the SignUpView needs to have one.
I have tried putting a NavigationController into the MasterView using the interface builder. This doesn't seem to do anything at all ... I.e. I make the following call:
[self.navigationController pushViewController:signUpViewController animated:YES];
And nothing happens. The SignUpView is never shown.
So then I declared a NavigationController in the AppDelegate. The above call in the same function that it was in before (button handler, button is in MasterView) works now! It takes me to the SignUpViewController.
however, the issue is, when I press back on the navigation bar in the sign up view, the navigation bar shows up again in the MasterView. I tried to set
self.navigationController.navigationBarHidden = YES;
in viewDidLoad and viewDidAppear, but that causes a black bar to appear in the transition from SignUpView to MasterView.
I tried to not set it in one of the two, and that causes the animation to go smoothly, but the navigation bar shows up in the MasterView.
I feel like this should be pretty simple to do ... but I'm at my wits end trying to figure this out. Some help would be really appreciated!
Thanks.
Probably not the answer to your question, but just a small suggestion. In the many apps that I have come across, a sign-up/sign-in view is generally displayed as a modal view (on top of your master view) with a 'cross' in the top-right corner to dismiss it. Probably it results in a better user experience.
Also, did you try self.navigationController.navigationBarHidden = YES; in the MasterView's viewWillAppear ?
HTH,
Akshay
I had this problem too, until I discovered setNavigationBarHidden. You will probably want to use these in viewWillAppear/viewWillDisappear or viewDidAppear/viewDidDisappear. You don't want to call this in viewDidLoad because that is only called once when the view is initialized, not every time it appears.
To hide:
[self.navigationController setNavigationBarHidden:YES animated:YES];
To show:
[self.navigationController setNavigationBarHidden:NO animated:YES];
I'm looking for a way to slide the keyboard into view from the right, like what happens in the Contacts application when you edit a note.
My problem is that when I call [someTextView becomeFirstResponder] in viewWillAppear, the keyboard immediatly pops up with no animation. And when I call it in viewDidAppear, the view first slides in from the right (UINavigationController does the sliding), and then the keyboard slides in from the bottom.
Is it possible to have the keyboard slide in from the right, together with the view?
Solution
In iOS 7, calling becomeFirstResponder on _textView in viewDidLayoutSubviews works.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[_textView becomeFirstResponder];
}
Note: Doing it in viewWillLayoutSubviews also works.
Explanation
Read the discussion in the docs for becomeFirstResponder.
You may call this method to make a responder object such as a view the first responder. However, you should only call it on that view if it is part of a view hierarchy. If the view’s window property holds a UIWindow object, it has been installed in a view hierarchy; if it returns nil, the view is detached from any hierarchy.
When using a navigation controller to push your custom view controller onscreen, self.view.window is still nil by the time either viewDidLoad or viewWillAppear: is called. So, _textView.window is also nil in the same methods, since _textView is a subview of self.view, i.e., they're both in the same window. No matter how you present your custom view controller, self.view.window (and thus _textView.window) is also nil in initWithNibName:bundle:. self.view.window is set by the time viewDidAppear: is called, but that's too late because by that time, the navigation controller has already completed the animation of pushing the view onscreen.
self.view.window is also set by the time either viewWillLayoutSubviews or viewDidLayoutSubviews is called and these methods are called before the push animation of the navigation controller begins. So, that's why it works when you do it in either of those methods.
Unfortunately, viewWillLayoutSubviews and viewDidLayoutSubviews get called a lot more than just on the initial navigation controller push. But, navigationController:willShowViewController: and willMoveToParentViewController: get called too soon (after viewDidLoad but before self.view.window is set) and navigationController:didShowViewController: and didMoveToParentViewController: get called too late (after the push animation).
The only other way I can think of doing it is to somehow observe the window property of _textView so that you get notified when it changes, but I'm not sure how to do that since window is readonly.
All you need to do is tell the text view in question to become the first responder in the viewDidLoad method of the view controller you're pushing onto the navigation stack:
override func viewDidLoad() {
super.viewDidLoad()
someTextView.becomeFirstResponder()
}
This works in iOS 8. The keyboard slides in from the right along with the view.
In iOS 7 (or any version before) you can make a simple thing in loadView, viewDidLoad or viewWillAppear
[yourTextView performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
In this case you will get left-to-right appearance of the keyboard aligned with the motion of pushing view controller.
For iOS 7 I've found the following solution to work the best for me:
-Import UIResponder-KeyboardCache to your project.
-Add [UIResponder cacheKeyboard:YES]; to the viewDidLoad of the view before the keyboard view. It might be better to do this immediately when the application loads or during a time convenient when you can afford it (during an HTTP request, for example). In most cases, simply in the view before is sufficient.
-Add the following to the viewDidLoad of the keyboard view.
dispatch_async(dispatch_get_main_queue(), ^{
[_textField becomeFirstResponder];
});
To explain, this will preload the keyboard view, which will remove the delay from the first call of the keyboard view. Calling becomeFirstResponder on the text field in the main queue causes it to slide in with the view instead of animating upward before the view slides in.
You could try sending the becomeFirstResponder message to the new view controller before you push it onto the stack. For example:
-(void)functionWhereYouPushTheNewViewController {
yourNewViewController *newVC = [[yourNewViewController alloc] init];
[newVC.yourTextView becomeFirstResponder];
[self.navigationController pushViewController:newVC animated:YES];
}
I have found that changing animations on things like they keyboard is pretty tough though, and if you read the Human Interface Guidelines Apple makes it pretty clear that they want certain things to act in certain ways, all the time. There are ways to change the behaviors of certain animations but they often involve undocumented API calls and are grounds for rejection from the app store. It would be a violation of HIG to have pushed views slide up from the bottom, for example.
Hope this helps.