How to remove shadow below UINavigationBar with UISearchController - ios

I could successfully remove the shadow below the navigation bar with the following line of code.
self.navigationController?.navigationBar.shadowImage = UIImage()
When I added a search controller however, the shadow reappeared.
self.navigationItem.searchController = UISearchController(searchResultsController: nil)
I tried the following, but resulted an unexpected behavior.
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.barTintColor = .white
self.navigationController?.navigationBar.isTranslucent = false
How do I remove the shadow under a navigation bar when there is a search controller attached?

I have not found good solution too...
for now I will hide it this way:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let imageView = navigationItem.searchController?.searchBar.superview?.subviews.first?.subviews.first as? UIImageView {
imageView.isHidden = true
}
}

Here's the solution that I use
Create a separate class extending a UINavigationController instance, lets call it BaseNavigationController. Here's your class for you
If you are using storyboards, assign BaseNavigationController to your UINavigationController scene in the storyboard
If you are initializing a navigation controller via code, ex: UINavigationController.init(rootViewController: someViewControllerInstance) then simply use BaseNavigationController.init(rootViewController: someViewControllerInstance)
An example of the class is shown below:
open class BaseNavigationController:UINavigationController {
override open func viewDidLoad() {
super.viewDidLoad()
setNavigationBar()
setNavBarBorder(false)
}
func setNavigationBar(color:UIColor?=UIColor.white, tint:UIColor?=UIColor.darkGray){
let appearance = UIBarButtonItem.appearance()
appearance.setBackButtonTitlePositionAdjustment(UIOffset.init(horizontal: 0.0, vertical: 0), for: .default)
self.navigationBar.barTintColor = color!
self.navigationBar.isTranslucent = false
self.navigationBar.tintColor = tint!
self.navigationBar.titleTextAttributes = [ NSAttributedString.Key.foregroundColor: tint! ]
}
func setTitleColor(_ color:UIColor?=UIColor.darkGray){
}
func setNavBarBorder(_ enable:Bool) {
self.navigationBar.setBackgroundImage((enable ? nil : UIImage()), for: UIBarMetrics.default)
self.navigationBar.shadowImage = (enable ? nil : UIImage())
self.navigationBar.setValue(true, forKey: "hidesShadow")
}
}
Now, the fun part, if you are handling ambiguous layout you might need to do this in viewWillLayoutSubviews otherwise putting this bit of code in viewWillAppear
of the viewController instance.
(self.navigationController as? BaseNavigationController)?. setNavBarBorder(false)
the interesting bit of code is in self.navigationBar.setValue(true, forKey: "hidesShadow"), for some versions of iOS.

You can instead add searchBar to your viewController in storyBoard and set its Search Style property to Minimal and it'll be look like this:

My little contribution about this issue. Of course, it is not the right way to do it but it's working for opaque navigation bar. The idea is to add a view on top of this shadow.
let searchBar = searchController.searchBar
let maskView = UIView(frame: .zero)
maskView.backgroundColor = .white // or any color you want
searchBar.addSubview(maskView)
//We need to use constraint so the mask view follow the search bar animation
maskView.translatesAutoresizingMaskIntoConstraints = false
let views = ["maskView": maskView]
searchBar.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-(0)-[maskView]-(0)-|",
options: NSLayoutConstraint.FormatOptions.alignAllCenterY,
metrics: nil,
views: views))
searchBar.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[maskView(1)]-(-1)-|",
options: NSLayoutConstraint.FormatOptions.alignAllCenterX,
metrics: nil,
views: views))

Related

Hide Navigation bar separator line on iOS 13

I have a view controller with a navigation bar with a large title. When I push the controller, only on iOS 13 is a line visible under the Navigation bar. How can I solve it?
I have already tried several solutions on Stack but they have not worked like:
let navigationBar = navigationController?.navigationBar
let navigationBarAppearence = UINavigationBarAppearance()
navigationBarAppearence.shadowColor = .clear
navigationBar?.scrollEdgeAppearance = navigationBarAppearence
With this snippet, even if I change the "clear color" with red color it is visible only in the first controller, in the pushed controller it is always gray.
How can I solve it?
Edit
I've solved with:
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.shadowColor = nil
navigationController?.navigationBar.standardAppearance = appearance
}
import UIKit
public protocol HideableHairlineHelper {
func hideHairline()
func showHairline()
}
extension HideableHairlineHelper where Self: UIViewController {
public func hideHairline() {
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.toolbar.setShadowImage(UIImage(), forToolbarPosition: .any)
}
public func showHairline() {
self.navigationController?.navigationBar.shadowImage = nil
}
}
I've tried the above-suggested ones and failed to remove the navigation separator line. Eventually, I've figured it out that to use TransparentBackground
The trick is to initialize UINavigationBarAppearance with TransparentBackground. Then you could easily remove the horizontal line of the navigation bar.
let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
appearance.backgroundColor = UIColor.green // Required background color
Then add the appearance changes to the navigation item as the apple suggested.
self.navigationItem.standardAppearance = appearance
self.navigationItem.scrollEdgeAppearance = appearance
self.navigationItem.compactAppearance = appearance
This is my NavBar template I use on my controllers, there's no line.
navigationController?.setNavigationBarHidden(false, animated: false)
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.isTranslucent = true
let titleAttributes =
[
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18),
NSAttributedString.Key.foregroundColor: UIColor.red,
]
self.navigationController?.navigationBar.titleTextAttributes = titleAttributes
I obviously change the font size and colour around but that's the basic function I put for each controller to get it clear(translucent).

Status Bar becomes white in iOS when using UISearchController in iOS 13

have a weird situation here. When I use the UISearchController, I get this appearance first (as expected)
But when you select inside the TextField to start searching, the Status Bar becomes completely white (or black if you are in dark mode)
This never used to happen. Is there some setting in UISearchController that tells it to use a certain Status Bar style when using the Search Bar?
I'd prefer it stay the color it was before selecting the TextField
---EDIT---
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItem.Style.plain, target: nil, action: nil)
frc = getFRC()
frc.delegate = self
self.resultsSearchController.delegate = self
let searchBar = self.resultsSearchController.searchBar
self.resultsSearchController.searchResultsUpdater = self
self.resultsSearchController.obscuresBackgroundDuringPresentation = false
self.resultsSearchController.extendedLayoutIncludesOpaqueBars = true
searchBar.sizeToFit()
self.tableView.tableHeaderView = searchBar
searchBar.placeholder = "Catalog Search"
searchBar.barTintColor = UIColor.darkAqua
searchBar.searchTextField.backgroundColor = UIColor.white
self.definesPresentationContext = true
searchBarHeight = searchBar.frame.height
do {
try frc.performFetch()
} catch {
error.tryError(tryMessage: "Perform initial fetch", loc: self)
}
if tutorials.catalog {
createTutorialTab(segueNameOnOpen: "catalogTutorial")
}
}
You use this function to change status bar colors, it's a kind of hack to deal with status bar ;). If you are using one theme status bar in whole app , call this function from didFinishLaunching in AppDelegate.
func changeStatusBar(backgroundColor: UIColor, contentColor:UIColor) {
if let statusBar = UIApplication.shared.value(forKey: "statusBar") as? UIView {
statusBar.backgroundColor = backgroundColor
statusBar.setValue(contentColor, forKey: "foregroundColor")
}
}

Swift strange back button text in iphone plus

Please take a look at my screenshot, the blue "Back" text always show on iphone plus (6s plus, 7 plus for both simulator and real device) . It does not show on smaller screen iphone. I tried lot of way to hide/change it from present/previous controller but no luck.
So why does it work on smaller iphone but not the plus one ?
Can anyone help me:(. Thanks.
Here is the code:
#IBAction func filter(_ sender: Any) {
let view:FilterViewController = self.storyboard?.instantiateViewController(withIdentifier: "FilterViewController") as! FilterViewController
view.superVC = self
view.currentFilter = currentFilter
self.setLeftCloseNavigation()
self.navigationController?.pushViewController(view, animated: true)
}
func setLeftCloseNavigation(){
self.navigationController?.navigationBar.backgroundColor = UIColor.clear
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.layer.mask = nil
self.navigationController?.navigationBar.backIndicatorImage = UIImage(named: "icon_close")?.withRenderingMode(.alwaysOriginal)
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "icon_close")?.withRenderingMode(.alwaysOriginal)
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
And here is the viewDidLoad in pushed controller:
override func viewDidLoad() {
super.viewDidLoad()
statusBar = UIColor.black
setResetNavigation() }
func setResetNavigation(){
navigationItem.hidesBackButton = false
let skipButton = UIButton(frame: CGRect(x: 0, y: 0, width: 70, height: 30))
skipButton.setTitle("Reset all".localized(), for: .normal)
skipButton.setTitleColor(UIColor.black, for: .normal)
skipButton.titleLabel?.font = UIFont(name: "HJGothamMedium", size: 16)
skipButton.addTarget(self, action: #selector(resetAllClicked), for: .touchUpInside)
let skip = UIBarButtonItem(customView: skipButton)
navigationItem.rightBarButtonItem = skip
}
This is the view hierarchy
Add this function :
override func viewDidAppear(_ animated: Bool) {
setResetNavigation()
self.navigationController?.navigationBar.backItem?.title = ""
}
try this
self.navigationItem.hidesBackButton = true
Or Check your storyboard it will remain
Use the below line to remove the text
navigationController?.navigationBar.topItem?.title = ""
You can inspect your UI hierarchy and if found related view then remove that view :
You can also invoke the view debugger by choosing View UI Hierarchy from the process view options menu in the debug navigator, or by choosing Debug > View Debugging > Capture View Hierarchy.
To hide the back text you need to set navigation item title to space character on the view controller that pushes the presented view controller:
self.navigationItem.title = " "
Be aware you have to set it on the previous view controller and not on top most one. Also you have to set a space character and not an empty string !!!
Also you can do this directly on storyboard
From below code you can set backButton text colour to any colour you want.You can simply set backButton to clear textColor. So, It won't be visible when it presents.
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UINavigationBar.classForCoder() as! UIAppearanceContainer.Type]).setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .normal)
Update: If you want to go for a different approach.Check this post How to customize the navigation back symbol and navigation back text? and accepted answer.

Why won't my UISearchBar obey constraints?

In my iOS app I have a navigation bar composed of a UISearchBar and a UIButton in a UIStackView. Since I can't storyboard a UISearchController I have a blank UIView in the stack view and I'm adding the UISearchBar as a subview. Here's what my storyboard looks like:
Here's my code to add the search bar
override func viewDidLoad() {
super.viewDidLoad()
configureSearchController()
print(wrapperView.bounds)
print(searchController.searchBar.bounds)
}
func configureSearchController() {
searchController = UISearchController(searchResultsController: nil)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search here..."
searchController.searchBar.sizeToFit()
searchController.searchBar.isTranslucent = false
searchController.searchBar.delegate = self
searchController.delegate = self
searchController.searchBar.barTintColor = addButton.backgroundColor
wrapperView.addSubview(searchController.searchBar)
let color = addButton.backgroundColor
searchController.searchBar.layer.borderWidth = 1
searchController.searchBar.layer.borderColor = color?.cgColor
searchController.searchBar.trailingAnchor.constraint(equalTo: addButton.leadingAnchor)
}
But the search bar goes past the UIButton, as shown here:
How can I get the UISearchBar to end at the UIButton like this?
Try replacing:
searchController.searchBar.trailingAnchor.constraint(equalTo: addButton.leadingAnchor)
with:
searchController.translatesAutoresizingMaskIntoConstraints = false
wrapperView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[subview]-0-|", options: nil, metrics: nil, views: ["subview": searchController]))
wrapperView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[subview]-0-|", options: nil, metrics: nil, views: ["subview": searchController]))
EDIT:
Otherwise try with a UISearchController:
searchController.searchBar.setImage(<image_name>, forSearchBarIcon: .Bookmark, state: .Normal)
this will allow you to add a custom button near the search bar.
I experienced the same issue, and I was able to fix it by calling searchController.searchBar.sizeToFit() after adding it to my view (right after the addSubview() line). You might have to run wrapperView.layoutIfNeeded() before, so something like this:
wrapperView.addSubview(searchController.searchBar)
wrapperView.layoutIfNeeded()
searchController.searchBar.sizeToFit()
Hope it helps!

How to add a custom border to viewcontroller (ios)

I have a view (UIPopoverPresentation) which functionality works fine, but I need to add a custom border. I'm currently using a borderWidth and borderColor but I cannot seem to find a way to make a customized border, as seen in the photo below. How do I go about creating this customized border? Make a CGRect?
What I need:
What I have:
I've attempted to add an image to the background of the popover and it resulted in this:
EDIT: //PopOverView (presented using UIPopOverPresentation)
override func viewDidLoad() {
super.viewDidLoad()
self.view.layer.cornerRadius = 10.0
self.view.layer.borderWidth = 1.5
self.view.layer.borderColor = UIColor.whiteColor().CGColor
self.navigationController?.navigationBarHidden = true
self.popViewTableView.delegate = self
self.popViewTableView.dataSource = self
self.popViewTableView.alwaysBounceVertical = false
self.popViewTableView.backgroundColor = UIColor(red: 151.0/255.0, green: 87.0/255.0, blue: 172.0/255.0, alpha: 1.0)
}
//Base View Controller. When button is pressed, this function is called which presents the popover
func presentPopOver() {
let contentView = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("popViewController") as! DeckPopViewController
contentView.modalPresentationStyle = UIModalPresentationStyle.Popover
contentView.preferredContentSize = CGSizeMake(deckSelectionCGRect.width, 160)
let popoverMenuViewController = contentView.popoverPresentationController!
popoverMenuViewController.delegate = self
popoverMenuViewController.sourceView = view
popoverMenuViewController.permittedArrowDirections = UIPopoverArrowDirection(rawValue:0)
popoverMenuViewController.sourceRect = CGRectMake((self.view.bounds.width/2) - (deckSelectionCGRect.width/2), 120, deckSelectionCGRect.width, deckSelectionCGRect.height)
presentViewController(contentView, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
//ALSO: On the third image, I placed the border imageview over the entire view controller in the storyboard, set padding to zero on all sides (autolayout). It doesn't look good, though.

Resources