Hide large title when scrolling up - ios

I have a normal view controller that is embedded in a navigation controller. In this view controller, I have a table view that is using the constraints of the safe area. (I don't use a table view controller)
The navigation controller is set to prefer large titles and the mode is set to .always. In beta 2 this worked perfectly, So when I came in the title was large and when I scrolled down it became small (Like the normal one). But since beta 3 this doesn't work anymore.
Anyone know how to turn this back on, or how to make it so when I scroll the table view it will become smaller. Like the behaviour of all the new iOS 11 apps?
Or is this a bug in the current version of swift 4/iOS 11 but the apps like messenger and settings still work this way.
Thanks in advance.

For me, it was that if you set the boolean "Prefers Large Titles" in the storyboard to true it will stay large, if you turn this on by code it works as expected!

I found a workaround on this site
basically, if the tableView (or element that has scroll)is not the first view in your view hierarchy, the large title fails to hide automatically.
https://markusbodner.com/2017/10/08/fix-large-navigation-bar-title-not-hiding-on-scroll-in-ios-11/
I added on the view willAppear:
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = true
} else {
// Fallback on earlier versions
}

(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y > 0) { //20
[self.navigationController.navigationBar setPrefersLargeTitles:NO];
} else {
[self.navigationController.navigationBar setPrefersLargeTitles:YES];
}
}

Check "Prefers Large Titles" for your navigation bar in IB, or use:
navigationController?.navigationBar.prefersLargeTitles = true

I'm using a programmatic layout and ran into a similar issue with large titles. I found the solution here: https://stackoverflow.com/a/46692583/131378. In viewDidLoad() I had to toggle the largeTitleDisplayMode off and on again. That was the correct combination that got the large titles working with scrolling:
self.navigationItem.largeTitleDisplayMode = .never
self.navigationItem.largeTitleDisplayMode = .always

Related

Responding to navigation bar height changes during push transition

In iOS 11 the search bar will now change the navigation bar height to 56dp when adding a search bar to the navigationItem.titleView
I like the height change and don't intend on forcing the height to stay at 44dp or lower.
unfortunately when transitioning from one view controller to another the pushed view will be drawn with the larger navigation bar in mind and then the bar height is changed after the transition is finished.
That looks a little like this:
I need a way of getting the navigation controller to recognise the height change during the transition so that it can animate to the smaller size and draw the view correctly.
I have one current fix which I don't like because it's a little jumpy and it's more work the app has to do and also it has to re-evaulate it's views regardless of which view controller it's being pushed from.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if #available(iOS 11, *) {
navigationController?.view.layoutSubviews()
}
}
So far I haven't found any similar questions on stack overflow, any comments from WWDC and nothing in the official apple documentation.
I've seen many apps deal with this however. The apple contacts app will create what looks like two navigation bars and will move between them without animating the height changes and the fb messenger app will perfectly transition between the heights and even allow for the interactive pop transition.
Using this one, it will keep your searchbar's height fixed
if #available(iOS 11.0, *) {
searchBar.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
}
I tried a lot of different functions.
This thing is the only one that really helps
OBJ C:
if (#available(iOS 11, *)) {
self.navigationController.view.layoutSubviews;
}
SWIFT:
if #available(iOS 11, *) {
self.navigationController.view.layoutSubviews()
}

iOS 11 prefersLargeTitles Weird Transition

So I'm having a weird issue with the new large titles in iOS 11. Instead of me trying to badly and confusingly explain the issue here is a 10-second screen recording of what is happening:
Screen recording of issue on YouTube
As you can see there is a weird black bar that appears when transitioning between a view controller that has
navigationItem.largeTitleDisplayMode = .never
And one that is set to .always
Thanks in advance!
Before the transition set this:
self.navigationController?.view.backgroundColor = .white
As Pranav said, the issue here is the background colour of the navigation controller's view, however, changing that from a child view controller is not the perfect way to do it.
Instead, a better way is to subclass UINavigationController and in the viewDidLoad() set the
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = .white
}
Then, just use your custom subclass rather than the standard UINavigationController. This way, you only ever need this code in one place.

UISearchController with UIBarPositionTopAttached throws UISearchBar offscreen; impossible to have standard UISearchController with UITableView?

I'm trying to create a similar experience to the Contacts apps in iOS 8. The primary components of this are:
Keep the search bar fixed below the navigation bar
Have the search bar attach to the top of the view (standard functionality) when presenting.
This, however, is easier said than done. After a while of struggling with tableHeaderView (which didn't allow for interaction in front of the table view, and was complex with the viewDidLayoutSubviews positioning), I decided to embed a UITableView within a UIViewController, so I could add the UISearchBar as a subview. This worked pretty well, and allowed interaction with the search bar at all scroll positions, and the insets weren't hard to calculate.
But, the search bar gets cut off below the status bar, when activated. Seems like a straightforward issue—even if I didn't experience it with the exact same implementation in a UITableViewController. So, I tried making sure all my properties were set up for alignment, in every possible view controller.
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = YES;
self.automaticallyAdjustsScrollViewInsets = NO;
I've tried every possible combination of these, in viewWillAppear as well as viewDidLoad, as well as translucent navigation bars and different starting frames for the table view and search bar. No luck. So, I tried to adjust the frames or constraints, perhaps using the topLayoutGuide or just 0. Unfortunately, adjusting the frame in any of the UISearchControllerDelegate methods didn't actually adjust its presented position, and adding constraints crashed immediately when the active animation begins (due to super.top not existing in the view hierarchy at the time; removing the constraints in willPresent did absolutely nothing).
After struggling for a bit longer, I tried implementing positionForBar as the UISearchBar's delegate:
- (UIBarPosition)positionForBar:(id <UIBarPositioning>)bar {
return UIBarPositionTopAttached;
}
This seemed to adjust the height, but the search bar flies off the top of the view. When activated, the search bar seems to appear directly above the visible area. This is even worse than it getting clipped behind or below the UIStatusBar. I also tried to just hide the status bar when the search controller becomes active, but conditionally implementing prefersStatusBarHidden didn't work at all (returning YES works great without UISearchController active, but when active it shows the status bar again or it gets shown beneath it). I assume this is because UISearchController refuses to obey any standards or rules, as is now painfully clear.
I've been trying to figure this out for a few days now, and I can't think of a solution besides reimplementing the UISearchController class/animation entirely. Please help!
For the behavior you described, this works great for me 😊
var resultSearchController = UISearchController()
And the following in your viewDidLoad function:
//Search Bar
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.searchBar.sizeToFit()
controller.dimsBackgroundDuringPresentation = false
self.tableView.tableHeaderView = controller.searchBar
self.definesPresentationContext = true
return controller
})()
What worked for me was to subclass UISearchController and override the prefersStatusBarHidden in the subclass.
Also make sure View Controller Based Status Bar Appearance is set to YES
And in your viewcontroller containing the tableview / search bar:
self.definesPresentationContext = YES;
I have the same problem. I add a UISearchController to UIViewController,not using tableView.header. And the UISearchBar.superview.frame.origin.y is -44. So you cannot see the searchBar.
Method 1: You can change the frame of UISearchBarWrapperView in the didPresentSearchController,the delegate of UISearchControllerDelegate。But you will see the animation of this process.
Method 2: like JBlake say. Set self.definesPresentationContext = YES; after the UISearchController is init. It works!
What i understood is if u need searchbar under navigationbar as u mentioned in #1 its very easy just set a boolean (Solution for #1) searchController.hidesNavigationBarDuringPresentation = true
Unfortunately i couldnt fix the searchbar-offscreen-issue while navigationbar is hidden.

iOS 7 Status Bar Collides With NavigationBar

I have a view controller in my app that has a navigation bar dragged on it in the storyboard. It was working fine in the iOS 6 but in iOS 7 it look like this:
The status bar and the navigation bar should no collide with each other. I have seen a lot of such questions on the stack overflow but they didn't of much help to me.
Some questions say that i should use this "self.edgesForExtendedLayout = UIRectEdgeNone;" but it didn't work. Some say i should remove the navigation bar and embed it inside the navigation controller that i cannot do due to the way my program is implemented. Some solutions suggests to use the view bounds and all but it didn't work for me as well.
What is the one thing that can help me resolve this issue. Thanks in advance!
UPDATE: I have embedded the view controller inside a uinavigation controller. Removed the navigation bar that was earlier manually added in it. Now it looks ok in the storyboard but when i run it, it shows the following:
It is showing text from another view controller that is currently behind it that is its parent view controller. Means its transparent now. Can anyone point out what i am doing wrong?
The latest version of the iOS has brought many visual changes and from a developer's point of view, the navigation and status bar are two noticeable changes.
The status bar is now transparent and navigation bar behind it shows through. The navigation bar image can even be extended behind the status bar.
First of all, if you are a beginner and have just started iOS development and are confused the way status bar and navigation bar is working, you can simply go through a blog post HERE that i found very useful. It has all the information related to navigation and status bar in iOS 7.
Now coming to the answer of your question. First of all i can see two different problems. One is that your status bar and navigation bar are both kind of colliding with each other as shown by you in the question with an image.
PROBLEM: Well the problem is that your have earlier dragged a navigation bar in your view controller which was working in iOS 6 correctly but with the arrival of iOS 7 SDK, this approach is resulting in status bar and navigation bar overlapping with each other.
SOLUTION to First Problem: You can either use UIBarPositionTopAttached or you can use view bounds and frames, i can also suggest and link you to Apple's documentation and bla bla bla but that would take some time for you to solve the issue.
The best and the most easiest way to solve this issue is to just embed your view controller inside a navigation controller and thats it. You can do it by just selecting the view controller and going to Editor > Embed In > Navigation Controller. (If there is any content on your old navigation bar, you can first drag it down, embed the view controller in navigation controller and then move the bar buttons on the new navigation bar and then delete the old navigation bar)
SOLUTION to Second Problem: This solution is for your specific question that you have mentioned in the update and is not for the general public reading this. As you can see that navigation and status bar is not visible and a transparent area is showing the parent view controller. I am not really use why you are facing this issue but most probably because of some third party library like ECSlidingView or any other is involved. You can select this view controller in your storyboard and set the background color of the view to be the same as your navigation bar. This will stop showing the parent view controller behind and your navigation bar and status bar will start showing. Now you can cover the rest of your view controller with text view or what ever your are using in it.
Hope this helps!
The navigation bar is too close to the status bar because starting in iOS 7, the status bar is more of an overlay over the whole view controller's view. Since your navigation bar is at (0, 0), the status bar will show on top of the navigation bar. To solve this, simply move the navigation bar down (or, as others have said), create a constraint between the navigation bar and the topLayoutGuide.
When you do that, you will see that there is now a 20 point gap between the navigation bar and the top of the screen. That's because you just moved the navigation bar down 20 points. "But UINavigationController can do it right!" Absolutely, and it does so by implementing UIBarPositioningDelegate on your view controller. This is a one-method protocol that should be implemented like this:
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
return UIBarPositionTopAttached;
}
After adding your view controller as the delegate for the navigation bar, you'll notice the navigation bar is still shifted down 20 points, but its background will extend up underneath the status bar, just like in UINavigationController.
Another thing you're seeing is that the navigation bar is translucent, meaning anything underneath the navigation bar will be visible to some extent. The translucent property on UINavigationBar is set to YES by default on iOS 7. Before iOS 7, the default was NO.
you can simply do this:
1) add a constrain between the Navigation Bar and Top Layout Guide (select navigationBar, hold ctrl key and go to Bottom Layout Guide, unhold ctrl key)
2) select vertical spacing:
3) set constant to 0:
Result:
UPDATE
In your AppDelegate file you can add this:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
// Prevent Navigationbar to cover the view
UINavigationBar.appearance().translucent = false
}
I suggest you in your viewDidLoad method you try:
self.navigationController.navigationBar.translucent = NO;
(by default it is yes now)
https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationBar_Class/Reference/UINavigationBar.html#//apple_ref/occ/instp/UINavigationBar/translucent
This works for me i hope you also have same luck :).
Add below code in your view.
-(void) viewDidLayoutSubviews
{
CGRect tmpFram = self.navigationController.navigationBar.frame;
tmpFram.origin.y += 20;
self.navigationController.navigationBar.frame = tmpFram;
}
It basically change location of navigation bar.
This is new feature with IOS7. Instead of staring at 20 px navigation bar in IOS7 staring at 0 px. As a solution shift the whole view downwards to 20 px or you can use image for navigation bar with height 64px.
In case it still helps someone, this is what worked for me for moving the Navigation Bar little bit down in ios 7 and above:
-(void)viewWillLayoutSubviews
{
float iosVersion = 7.0;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= iosVersion) {
// iOS 7+
CGRect viewFrame = self.view.frame;
viewFrame.origin.y += 10;
self.view.frame = viewFrame;
}
}
On a device with ios 6.1 and below the Navigation Bar will be unchanged, as it was before.
And this is what I used to make the contents of the Status Bar lighter:
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
If your UIViewController is NOT in a UINavigationController and you're using UIStoryBoard, you can set the "iOS 6/7 Deltas" to 20 for the delta Y, for every subview that needs to be offset from the UIStatusBar.
Using Swift:
As #Scott Berrevoets said in his answer you need to implement the method positionForBar in the protocol UIBarPositioningDelegate, but as the UINavigationBarDelegate protocol implements this protocol :
public protocol UINavigationBarDelegate : UIBarPositioningDelegate {
...
}
You only need to set the delegate of the UINavigationBar you set using Storyboard and implement the method and it's done, like in this way:
class ViewController: UIViewController, UINavigationBarDelegate {
#IBOutlet weak var navigationBar: UINavigationBar!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationBar.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func positionForBar(bar: UIBarPositioning) -> UIBarPosition {
return UIBarPosition.TopAttached
}
}
NOTE:
It's worth to mention if you set the position of the y-axis of the navigation bar, let's say to 40 from the top, then it will extend underneath to the top from this position, to simulate the behaviour of the UINavigationController you need to set to 20 from the top.
I hope it will help you.
First, set UIViewControllerBasedStatusBarAppearance to NO in Info.plist.
Then, in AppDelegate's application:didFinishLaunchingWithOptions: method add:
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
[application setStatusBarStyle:UIStatusBarStyleLightContent];
self.window.clipsToBounds = YES;
self.window.frame = CGRectMake(0, 20, self.window.frame.size.width, self.window.frame.size.height-20);
self.window.bounds = CGRectMake(0, 20, self.window.frame.size.width, self.window.frame.size.height);
}
return YES;
In iOS 7 app occupies 100 % of screen size.This not a problem .
http://www.doubleencore.com/2013/09/developers-guide-to-the-ios-7-status-bar/
in earlier iOS window start after statusbar and in iOS 7 window starts from 0px in earlier version view height is 460 (iPhone 4s and earlier) and 548 (iPhone 5) but in iOS 7 view height is 480 (iPhone 4s and earlier) and 568 (iPhone 5 and later) so you have to start view arrangement after 2o px or you have to start view from 20px.
you can write below code in rootviewcontroller or in all viewcontroller for set view from 20px
#define IOS7_HEIGHT 64
- (void)viewDidLayoutSubviews {
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:#"7.0" options:NSNumericSearch] != NSOrderedAscending)
{
CGRect frame=[self.view frame];
if (frame.origin.y!=IOS7_HEIGHT) {
frame.origin.y = IOS7_HEIGHT;
frame.size.height -= IOS7_HEIGHT;
[self.view setFrame:frame];
[self.view layoutSubviews];
}
}
}
here height is 64 because 20 for statusbar and 44 for navigationbar.
try below code it will help you. and your problem will be solved.
For the ones who are having problems implementing #Masterfego 's solution (which is also the official, but I have had problems with Xcode 6.3 and automatic constraints), this is what I did:
I have a UIViewController with an added Navigation Bar. I selected the NAvigation bar and added a height constraint of 64px. We later see a warning that the navbar will be higher (but this is what we do). Finally, you can see that the Status bar looks nice and has the same color as the navbar. :)
PS: I can't post images yet.
You can probably create constraints that are attached to the top layout guide to specify the navigation bar's position relative to the status bar. See the iOS 7 UI Transition Guide: Appearance and Behavior section for more information about using the layout guides.
it's the best answer.
But I wanted know how to use a Storyboard and dragged UINavigationBar on it.
When I implemented the delegate method, and set the return result to UIBarPositionTopAttached, it did not work.
- (void)viewDidLoad{
self.navigationbar.delegate = self;
}
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar{
NSLog(#"Got it");
//
// CGRect frame = self.navigaitonBar.frame;
//
// frame = CGRectMake(0, 20, CGRectGetWidth(frame), CGRectGetHeight(frame));
// self.navigaitonBar.frame = frame;
//
// NSLog(#"frame %f",frame.origin.y);
return UIBarPositionTopAttached;
}
If you use Xcode 6 and Swift, you can make it:
Open to info.plist file of your app.
Add a ViewControllerBasedStatusBarAppearance Boolean key if it is not existing and assign value “NO”.
Add “Status bar style” key if it is not existing and select “Opaque black style” value to it.
I was facing issue when full screen ModalViewController was opening from my MainViewController, NavigationBar position was getting changed when user was coming back to MainViewController from ModalViewController.
Issue which I noticed is status bar height was not getting included when user came back to MainViewController. Please debug and check origin of your NavigationBar before and after coming back to your ViewController.
// This method will adjust navigation bar and view content.
private func adjustNavigationControllerIfNeeded() {
var frame = self.view.frame
let navigationBarHeight = self.navigationController!.navigationBar.frame.size.height
if(frame.origin.y == navigationBarHeight && !UIApplication.shared.isStatusBarHidden) {
// If status bar height is not included but it is showing then we have to adjust
our Navigation controller properly
print("Adjusting navigation controller")
let statusBarHeight = UIApplication.shared.statusBarFrame.height
frame.origin.y += statusBarHeight // Start view below navigation bar
frame.size.height -= statusBarHeight
self.view.frame = frame
self.navigationController!.navigationBar.frame.origin.y = statusBarHeight // Move navigation bar
}
}
And call it from viewWillAppear method -
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.adjustNavigationControllerIfNeeded()
}

How to prevent UINavigationBar from covering top of view in iOS 7?

After updating to Xcode 5, the navigation bars in all of my app's views have shifted down. Here are some screenshots, the first showing everything in the view as it's pulled down, and the second showing all of it untouched. The search bar should begin where the navigation bar.
Anyone know how I can fix this?
edit: i have tried this previously recommendation:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;
But it yields very odd results.
This may be because I have a "slide menu" under this view controller that is appearing due to the transparency of the navigation bar.
Set the navigation bar's translucent property to NO:
self.navigationController.navigationBar.translucent = NO;
This will fix the view from being framed underneath the navigation bar and status bar.
If you have to show and hide the navigation bar, then use
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone; // iOS 7 specific
in your viewDidLoad method.
In iOS 7 by defaults all Controller translucent property value is YES, so you set translucent property NO for this issue.
self.navController.navigationBar.translucent = NO;
You can disable
the "Extend edges" in Attribute inspector of View Controller of this screen (as shown in below image)
:
This works for swift as well on iOS 8.1
navigationController?.navigationBar.translucent = false
If you want to keep the translucency on your navigationBar, at the end of your viewDidLoad or in your viewWillAppear add this line of code:
[self.view sendSubviewToBack:self.tableView]
Somehow if your scrollView subclass (UITableView, UICollectionView, etc.) is at index 0 in your current view subviews, it will automatically adjust the insets according to your navigationBar. And it shouldn't affect your UI in versions prior to iOS7 either.
EDIT
If you initialize your UITableView programmatically, then it is best to add it to the view using this [self.view insertSubview:self.tableView atIndex:0];
Swift 4:
Set following line of code in viewDidLoad method:
self.navigationController?.navigationBar.isTranslucent = false
You can add this method into your view controller as shown on this URL:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.searchBar.frame =
CGRectMake(0, self.topOfViewOffset, self.searchBar.frame.size.width, self.searchBar.frame.size.height);
}
Another approach is to set self.automaticallyAdjustsScrollViewInsets = YES; on your view controller.
This is enabled by default. But in your case:
I see you are using EGORefreshHeaderView. It plays with contentInset of UITableView. So when you release it, header will reset top inset instead of restore previous value.
If you want to have complete control on views and avoid faulty adjustments of iOS, subclass UITableView and adjust the insets (both scroll and indicators) in -(void)willMoveToWindow:(UIWindow *)newWindow. Works for me.
Another option is to open your Info.plist file in source code mode and enter the following info:
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarHidden</key>
<true/>
Hope this helps.

Resources