How to use scrollEdgeAppearance in iOS 15? - ios

Here's Apple's document of scrollEdgeAppearance for UINavigationBar:
When a navigation controller contains a navigation bar and a scroll
view, part of the scroll view’s content appears underneath the
navigation bar. If the edge of the scrolled content reaches that bar,
UIKit applies the appearance settings in this property.
If the value of this property is nil, UIKit uses the settings found in the
standardAppearance property, modified to use a transparent background.
If no navigation controller manages your navigation bar, UIKit ignores
this property and uses the standard appearance of the navigation bar.
When running on apps that use iOS 14 or earlier, this property applies
to navigation bars with large titles. In iOS 15, this property applies
to all navigation bars.
Here's my demo. I embed ViewController into a UINavigationController, then drag a UITableView and set it as the subview of ViewController's view. I set the following appearance properties to UINavigationBar:
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let redAppearance = UINavigationBarAppearance()
redAppearance.backgroundColor = .red
navigationController?.navigationBar.standardAppearance = redAppearance
let greenAppearance = UINavigationBarAppearance()
greenAppearance.backgroundColor = .green
navigationController?.navigationBar.scrollEdgeAppearance = greenAppearance
}
}
I thought the initial navigation bar should be red, and it should turn green when I scroll the table view. But the truth is it's the other way around.
I did some search and most people just set standardAppearance and scrollEdgeAppearance to the same instance, or set one of them to be nil, so I'm quite confused about these two properties in iOS 15.

You have understood perfectly but backward.
navigationController?.navigationBar.scrollEdgeAppearance = greenAppearance
means the nav bar will be green only when the scrollable view is scrolled all the way down. That is what you see in your first screen shot.
As soon as you scroll up a little bit, the nav bar starts adopting its standard appearance, which is red. That is what you see in your second screen shot.

scrollEdgeAppearance is applied when the edge of the scrollable content reaches the nav bar so what you're seeing is correct. Many people set both scrollEdgeAppearance and standardAppearance to the same since they want the behavior to be no different when the user scrolls but your implementation is certainly a valid use case.

Related

Visible UISearchBar changes UINavigationBar background color

A tableview controller is embedded into a navigation controller.
I programmatically added a search bar to the tableview controller's navigation bar. I only changed the navigation bar Background color into something different than Default (purple) - all the rest I left default.
class TableViewController: UITableViewController {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesSearchBarWhenScrolling = true
navigationItem.searchController = searchController
}
}
Code above is reduced to bare minimum for demonstration purpose.
All done with Xcode 11 (11A420a).
I ran the project in iOS 12.0 and 13.0 simulators and devices.
iOS 13.0
The search bar shows upon start.
Navigation bar background color is correctly presented.
While scrolling, navigation bar background color remains correct.
With iOS 13.0, all works as expected!
iOS 12.0
The search bar doesn't show upon start.
Navigation bar background color is correctly presented.
While scrolling, navigation bar background color goes white as soon search bar is visible.
I tried to change all kind of color setting in storyboard as well as properties programmatically. I didn't succeed in changing the navigation bar background color when search bar is visible.
It seems (?!) that the navigation bar foreground looses transparency when search bar becomes visible.
If I use a Bar Tint color of the navigation bar (!= Default), all works as expected (and as with iOS 13.0), but I loose the gradient effect, which I would like to keep.
What did I miss?
How can I avoid this?
Bug?
I had some luck with the navigationItem.scrollEdgeAppearance property when facing a similar problem. For example:
vc.navigationItem.scrollEdgeAppearance?.backgroundColor = .red
This is only available on iOS 13 though.
Here's what I ended up doing to get correct colors in the navigation bar while allowing the search controller's scroll bar to hide during scroll:
if #available(iOS 13.0, *) {
let blurEffect = UIBlurEffect(style: .systemUltraThinMaterial)
navbar.standardAppearance.backgroundEffect = blurEffect
navbar.standardAppearance.backgroundColor = appMainColor.withAlphaComponent(0.75)
navbar.standardAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
navbar.compactAppearance = nil
navbar.scrollEdgeAppearance = navbar.standardAppearance.copy()
navitem.standardAppearance = nil
navitem.compactAppearance = nil
navitem.scrollEdgeAppearance = nil
}
I don't know if it is the look you're going for but I found if you disable hideSearchBarWhenScrolling the background stops changing color. However, the search bar is always there.
Add this to viewDidLoad():
navigationItem.hidesSearchBarWhenScrolling = false

iOS 11 - Weird UINavigationBar and UICollectionView behavior

I have a UINavigationController the root view controller of which is a UIViewController with a UICollectionView. The collection has constraint to the left, right, bottom and top. I've checked the prefersLargeTitles attribute in the UINavigationController, so I have big titles. I expected that, during the scroll, the UINavigationBar would automatically collapse, but I had to disable the large titles in my root view controller and add this line of code in my viewWillAppear :
navigationItem.largeTitleDisplayMode = .automatic
Then, I added also the search bar to the UINavigationBar, in this way :
let searchController = UISearchController.init(searchResultsController: nil)
searchController.searchResultsUpdater = self
navigationItem.searchController = searchController
Everything is perfect, visually.
Now, when I scroll down, the UICollectionView suddenly stick to the UINavigationBar which collapses. When I tap on the status bar to go to the top of the collection, a white space appears between the large title and the status bar. Is there a specific procedure that, for some reason, I'm not following? Do you have any solution?
EDIT:
Here you can find the video of the problem
When you see the collection that sticks to the UINavigationBar I confirm you that it's happening in a unnatural way. The last "bug" happens when I tap on the status bar.

Why is my navigation item/bar very dark in colour?

I am building a help screen view controller of my app. It basically contains a UIWebView because I want to use HTML to display the help text. There is also another view controller which is kind of the "main" VC. There is a "Present as Popover" segue connecting the two.
From the IB, I see this:
I have an unwind segue that unwinds to the main VC. It will be triggered if the user taps the "Done" button.
And here is my code:
import UIKit
class HelpViewController: UIViewController {
#IBOutlet var webView: UIWebView!
override func viewDidLoad() {
automaticallyAdjustsScrollViewInsets = false
let htmlString = "You don't need to know about this."
webView.loadHTMLString(htmlString, baseURL: nil)
}
#IBAction func doneClicked(sender: UIBarButtonItem) {
performSegueWithIdentifier("exitToMain", sender: self)
}
}
And the unwind segues are working perfectly but the navigation bar is somehow very dark in colour!
I don't know what's happening here. I checked out this question
iOS 7 SDK - UI Navigation Bar is sometimes darker and sometimes not. But why?
The asker said that's because of a piece of code that he wrote but I swear I didn't use the same code as he did (from my limited obj-c to swift translation knowledge)!
Anyway, here is the relevant storyboard hierarchy thingy:
What causes this problem and how to fix it?
I solved the problem by setting the background colour of the UIWebView to white. And the navigation bar turns white!
I found this by observing how the colour changes when I scroll the web view up and down. I saw that initially it is kinda grey. But when I scrolled down to the white part of the web view, it changes to white. I deduced that the grey colour is actually the web view's background property and the navigation bar is kind of translucent.
And that's why setting the background colour to white fixes this issue.
Stretch the web view through the bottom of the navigation bar and set automaticallyAdjustScrollviewInsets to true. That would adjust the scroll view inset to show the content at the right content.
did you try this
self.navigationController!.navigationBar.translucent = true

Autolayout with Navigation Bar - Remove Specific Constraint

I'm building an application for iOS, which is using a navigation controller. I want to put a view in the titleView, and have it fill the whole width of the navigation bar.
I'm calling setupNavBar in viewDidLoad of the view controller that is embedded in the navigation controller. Here is how I do:
func setupNavBar() {
let navBar = navigationController?.navigationBar
// navBar!.translatesAutoresizingMaskIntoConstraints = false
// navBar!.frame.size.height = CGFloat(100)
let searchBar = UIView(frame: navBar!.frame)
searchBar.bounds = CGRectMake(0, 0, navBar!.frame.width, navBar!.frame.height)
searchBar.backgroundColor = UIColor.brownColor()
navigationItem.titleView = searchBar
}
But the view (brown - "searchBar"), doesn't cover the full navigation bar:
So I figured out that the problem was related to Autoresizing and Constraints, because if I call navBar!.translatesAutoresizingMaskIntoConstraints = false, I can freely set the sizes of views frame, so there must be some constraints that change the view's bounds. But I would like to keep as much of the autolayout behaviour as possible.
Is there a way to only change the contraints on the titleView?
I'm doing everything programmatically, I don't use the storyboard or xib's!
EDIT:
But it doesn't seem like there is any constraints on either navigationItem or navBar:
for someObject in navigationItem.titleView!.constraints {
print(someObject)
}
It doesn't print any constraints. neither if I use navBar.constraints!
EDIT 2:
I have a screenshot from "View UI Hierarchy" from the debug navigator:
It seems that the view(brown) alligns with the Navigation Bar Back Indicator View, maybe this is a clue to what causes the problem?
How come the view is resized?
Two different suggestions here:
1. You can try the
navigationItem.titleView.sizeToFit()
Otherwise you could set the background colour of the navbar to brown as it appears you wish the brown bar to cover the entire width of the navbar. If you want to add other views on top of that you then can.
You could also try to make an outlet to the title view and add an NSLayoutConstraint using
navigationItem.titleView.addConstraint(NSLayoutConstraint: NSLayoutConstraint)
I am not entirely sure whether that will work, however.

Search bar bug - iOS

In my app I have this kind of bug:
When I scroll down, the content of table is seen above the search bar. What can be the reason?
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
controller.searchBar.barTintColor = UIColor(red: 52.0/255.0, green: 73.0/255.0, blue: 94.0/255.0, alpha: 1.0)
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.tableView.reloadData()
}
The iOS Human Interface Guidelines list 3 methods to prevent content from showing through the status bar.
Use a navigation controller to display content. A navigation controller automatically displays a status bar background and it
ensures that its content views don’t appear behind the status bar. (To
learn more about navigation controllers, see Navigation Controllers.)
Create a nondistracting custom image—such as a gradient—and display it behind the status bar. To ensure that the image stays
behind the status bar, you could use a view controller to keep the
image above a scrolling view or you could use a scrolling view to keep
it pinned to the top.
Position content to avoid the status bar area (that is, the area defined by the app’s statusBarFrame property). If you do this, you
should use the window’s background color to provide a solid color
behind the status bar.
You should make the status bar not translucent.
The status bar's color is a property of UIApplication. You need to set it to a valid UIStatusBarStyle.
The new UISearchController has been designed to work with a UITableView and a UINavigationBar. Its functionality depends on your navigation bar being present. when the search bar has content in its text field, it will be latched on top of the page under the navigation bar. well, you don't have any navigation bar, it will latch to the top view guide and it always takes into account the status bar (if present) I guess the only way you can resolve your problem without an actual navigation bar is to add a view under status bar and make it opaque just like your search bar. it's a bit hacky but it's alright in my book.

Resources