How to add shadow and remove bottom line in NavigationBar globally? - ios

So I'm looking for how can I add shadow to NavigationBar and remove the bottom black line from NavigationBar? Also if I have searchBar in NavigationBar then shadow should go below to search bar. I want to make a change globally without making a change in every viewController.
https://imgur.com/a/8ogGRaf.jpg
So I already archive these changes except add a shadow below to search controller.
For Shadow:
self.navigationController?.navigationBar.isTranslucent = false
self.navigationController?.navigationBar.layer.shadowColor = UIColor.black.cgColor
self.navigationController?.navigationBar.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
self.navigationController?.navigationBar.layer.shadowRadius = 7.0
self.navigationController?.navigationBar.layer.shadowOpacity = 0.2
For Remove a Bottom Bar:
UINavigationBar.appearance().backIndicatorImage = UIImage(named:"normal")
UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named:"normal")
This is What issue I'm getting with search Bar
https://imgur.com/a/QQ9yrbE

2.hide bottom line
public extension UINavigationBar {
/// Hide line under navigation bar
public func hideBottomHairline() {
let navigationBarImageView = hairlineImageViewInNavigationBar(self)
navigationBarImageView!.isHidden = true
}
/// Show line under navigation bar
public func showBottomHairline() {
let navigationBarImageView = hairlineImageViewInNavigationBar(self)
navigationBarImageView!.isHidden = false
}
fileprivate func hairlineImageViewInNavigationBar(_ view: UIView) -> UIImageView? {
if view.isKind(of: UIImageView.self) && view.bounds.height <= 1.0 {
return (view as! UIImageView)
}
let subviews = (view.subviews as [UIView])
for subview: UIView in subviews {
if let imageView: UIImageView = hairlineImageViewInNavigationBar(subview) {
return imageView
}
}
return nil
}
}

Related

Programmatically set navigation bar color and height in iOS15

I am very new to swift and am building a navigation view. Below is what i ran into.
currentView
The navigation bar background color is only partly yellow. How can I also make the section above it also yellow?
section should be yellow
Below is the code that I used in my viewController. I also tried in the navigation controller but it doesn't work.
self.navigationController?.navigationBar.backgroundColor = UIColor.yellow
How to set the height of the navigation bar? The search button (navigationItem.titleView) is right in the middle of the navigation bar and I want to give it more space by setting the navigation bar height. The code below is what I tried but doesn't work :(
self.tabBarController?.tabBar.frame.size.height = 50
Thanks.
you have to changed the status bar color as well. you can done this using below extension of UIApplication
extension UIApplication {
var statusBarView: UIView? {
if responds(to: Selector(("statusBar"))) {
return value(forKey: "statusBar") as? UIView
}
return nil
}
}
use above extension like this :
UIApplication.shared.statusBarView?.backgroundColor = .yellow
for iOS 13 and above
use UINavigationController extension to change status bar color :
extension UINavigationController {
func setStatusBar(backgroundColor: UIColor) {
let statusBarFrame: CGRect
if #available(iOS 13.0, *) {
statusBarFrame = view.window?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero
} else {
statusBarFrame = UIApplication.shared.statusBarFrame
}
let statusBarView = UIView(frame: statusBarFrame)
statusBarView.backgroundColor = backgroundColor
view.addSubview(statusBarView)
}
}
navigationController?.setStatusBar(backgroundColor: .yellow)

swift iOS13 searchBar textfield height change

any chances to change UISearchBar's UITextField height?
I tried almost all solutions which I've could found, but all are unsuccessful.
Updated:
I realized that ios13 searchBar doesn't contains UITextField, but contains UISearchBarTextField class
and here is hierarchy
as I understood we have to looking for the new class for iOS13 instead of UITextField
I've tried looping and search TextField
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
for subView in searchBar.subviews {
for subsubView in subView.subviews{
for subsubsubView in subsubView.subviews{
if #available(iOS 13.0, *) {
if let textField = subsubsubView as? UISearchTextField{
var bounds: CGRect
bounds = textField.frame
bounds.size.height = 40 //(set height whatever you want)
textField.bounds = bounds
textField.layoutIfNeeded()
}
} else {
if let textField = subsubView as? UITextField {
var bounds: CGRect
bounds = textField.frame
bounds.size.height = 40 //(set height whatever you want)
textField.bounds = bounds
}
}
}
}
}
}
and debugger goes into condition and found UITextField and changed height, but on device - there no height changes
What I finally did was to set an UIImageView as a container for the UISearchBar.
Didn't work with a simple UIView, I guess it had to do with intrinsic content size?
func showSearchBar() {
self.searchNavigationItem?.setRightBarButton(nil, animated: false)
guard let searchBar = self.searchBar else {
assert(false, "Need a search bar to display")
return
}
self.searchContainer = UIImageView()
self.searchContainer?.isUserInteractionEnabled = true
let image = // Some image with the corresponding size.
self.searchContainer?.image = image
self.searchContainer?.alpha = 0
self.searchContainer?.frame = // Same size as the image
// Here you add the searchContainer into the titleView.
self.searchNavigationItem?.titleView = self.searchContainer
searchBar.translatesAutoresizingMaskIntoConstraints = false
// Create top/bottom/leading/trailing constraints
self.searchContainer?.addSubview(searchBar)
searchBar.sizeToFit()
self.searchContainer?.addConstraints([topConstraint, bottomConstraint, leadingConstraint, trailingConstraint])
self?.searchContainer?.alpha = 1
}

Navigation bar + Search controller + Large title: Hairline during scrolling

Context:
UINavigationController with a UITableViewController
UISearchController in the navigation bar
Navigation bar translucent with black style, it uses large title, tintColor, barTintColor (so no background image).
Issue:
I have a strange animation glitch that shows a hairline above the search bar. The hairline appears only during the scroll.
I have already tried many solutions concerning similar problems, but they have not helped.
Tested with an iPhone 7 with iOS 12.1.3
You can fix hairline issue using this
searchController.searchBar.layer.borderColor = UIColor(red: 242/255.0, green: 82/255.0, blue: 46/255.0, alpha: 1).CGColor
searchController.searchBar.layer.borderWidth = 1
if the above didn’t work. you can remove the hairline completely
extension UINavigationBar {
func hideBottomHairline() {
self.hairlineImageView?.isHidden = true
}
func showBottomHairline() {
self.hairlineImageView?.isHidden = false
}
}
extension UIView {
fileprivate var hairlineImageView: UIImageView? {
return hairlineImageView(in: self)
}
fileprivate func hairlineImageView(in view: UIView) -> UIImageView? {
if let imageView = view as? UIImageView, imageView.bounds.height <= 1.0 {
return imageView
}
for subview in view.subviews {
if let imageView = self.hairlineImageView(in: subview) { return imageView }
}
return nil
}
}

How make UISearchbar rectangular shape and merge with navigation bar?

Hello I'm trying to change the UISearchbar to rectangular shape rather then rounded rectangular and merge it with the navigation bar with same color.
The background color of the navigation bar should be the same as the background of the search bar but there's difference and also there's a clear edge shown both should show the background color #be1b25 and not the #bc303a. As well the textfield still showing rounded rectangle and not fully rectangle.
The Main View Controller code
#IBOutlet weak var searchBar: UISearchBar!
self.navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.backgroundColor = UIColor().HexToColor(hexString: REDCOLOR)
searchBar.backgroundColor = UIColor().HexToColor(hexString: REDCOLOR)
searchBar.tintColor = UIColor().HexToColor(hexString: REDCOLOR)
searchBar.barTintColor = UIColor().HexToColor(hexString: REDCOLOR)
searchBar.barStyle = .default
searchBar.placeholder = NetworkManager.sharedInstance.language(key: "searchentirestore")
searchBar.layer.cornerRadius = 0;
searchBar.clipsToBounds = false;
searchBar.barTintColor = UIColor().HexToColor(hexString: REDCOLOR)
searchBar.layer.borderWidth = 0
//searchBar.layer.borderColor = UIColor.clear as! CGColor
The UISearchBar Extension
extension UISearchBar {
private var textField: UITextField? {
return subviews.first?.subviews.flatMap { $0 as? UITextField }.first
}
private var activityIndicator: UIActivityIndicatorView? {
return textField?.leftView?.subviews.flatMap{ $0 as? UIActivityIndicatorView }.first
}
var isLoading: Bool {
get {
return activityIndicator != nil
} set {
if newValue {
if activityIndicator == nil {
let newActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
newActivityIndicator.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
newActivityIndicator.startAnimating()
newActivityIndicator.color = UIColor().HexToColor(hexString: BUTTON_COLOR)
newActivityIndicator.backgroundColor = UIColor.white
textField?.leftView?.addSubview(newActivityIndicator)
let leftViewSize = textField?.leftView?.frame.size ?? CGSize.zero
newActivityIndicator.center = CGPoint(x: leftViewSize.width/2, y: leftViewSize.height/2)
}
} else {
activityIndicator?.removeFromSuperview()
}
}
}
Any help please, this is Swift 3. Thanks in advance
so there a few things you are dealing with
1 Rectangular UISearchbar
either you will traverse subviews of native UISearchbar and change its right sublayers corner radiuses, but this is a hack, Apple can change that hierarchy and you will be at the beginning again
you can create your own component that will look like UISearchbar, few UIViews, and UITextfield with some UIImageViews.
there are few libraries on GitHub you can use https://github.com/CosmicMind/Samples
there is also a similar question here iOS 11 UISearchBar in UINavigationBar
2 Merge with NavBar
one way is to use UISearchController with iOS 11
or you can with your custom UISearchbar you can either add it below the NavBar in UIView, not in navigation, but the NavBar cannot be translucent in this case, because you will never achieve the same color space
you can add your custom view as custom UIView into titleView in NavBar
Extend the UISearchBar and apply the customization.
Set the UISearchBar as titleView to the NavigationController
class SearchField: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
// Corner radius
self.layer.cornerRadius = 2.0
self..clipsToBounds = true
// Textfield inside color.
if let searchTextField = self.value(forKey: "_searchField") as? UITextField {
// Set TextField property
// Set the corner radius for TextField (not suggested)
if let clearButton = searchTextField.value(forKey: "_clearButton") as? UIButton {
// Set the clear button property
}
}
}
}
// Use it in the ViewController.
let searchBar = SearchField()
self.navigationItem.titleView = searchBar

Hiding the tabbar and removing the space

Is there a way to hide tabbar and remove that space left (around 50px) ?
I tried
self.tabBarController?.tabBar.hidden = true
self.extendedLayoutIncludesOpaqueBars = true
No luck. I see blank space.
If you're still seeing a black stripe under your hidden tab bar, have you tried to select Extend Edges Under Opaque Bars here?
Make also sure that Under Bottom Bars is still selected. Hope it helps!
Swift 3:
extension UITabBarController {
func setTabBarVisible(visible:Bool, duration: TimeInterval, animated:Bool) {
if (tabBarIsVisible() == visible) { return }
let frame = self.tabBar.frame
let height = frame.size.height
let offsetY = (visible ? -height : height)
// animation
UIViewPropertyAnimator(duration: duration, curve: .linear) {
self.tabBar.frame.offsetBy(dx:0, dy:offsetY)
self.view.frame = CGRect(x:0,y:0,width: self.view.frame.width, height: self.view.frame.height + offsetY)
self.view.setNeedsDisplay()
self.view.layoutIfNeeded()
}.startAnimation()
}
func tabBarIsVisible() ->Bool {
return self.tabBar.frame.origin.y < UIScreen.main.bounds.height
}
}
To use (if for example self is a UITabBarController):
self.setTabBarVisible(visible: false, duration: 0.3, animated: true)
Swift 2.x:
extension UITabBarController {
func setTabBarVisible(visible:Bool, duration: NSTimeInterval, animated:Bool) {
if (tabBarIsVisible() == visible) { return }
let frame = self.tabBar.frame
let height = frame.size.height
let offsetY = (visible ? -height : height)
// animation
UIView.animateWithDuration(animated ? duration : 0.0) {
self.tabBar.frame = CGRectOffset(frame, 0, offsetY)
self.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height + offsetY)
self.view.setNeedsDisplay()
self.view.layoutIfNeeded()
}
}
func tabBarIsVisible() ->Bool {
return self.tabBar.frame.origin.y < UIScreen.mainScreen().bounds.height
}
}
To use:
self.tabBarController?.setTabBarVisible(visible: false, duration: 0.3, animated: true)
After saw your screenshot in comment. I think you can try to set hidesBottomBarWhenPushed to true.
hidesBottomBarWhenPushed = true
Or storyboard.
It will hide bottom bar automatically when you pushed to another view controller, and appear it again when you go back.
Programmatically, add this to the next view controller for swift 4.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tabBarController?.tabBar.isHidden = true
edgesForExtendedLayout = UIRectEdge.bottom
extendedLayoutIncludesOpaqueBars = true
}
And add a background color
NOTE - This solution is to just to remove white space left after hiding tab bar.
For hiding tab bar best solution is - #Michael Campsall answer here
The simplest solution to this is to change your view's(in my case its tableView) bottom constraints, instead of giving bottom constraints with BottomLayoutGuide give it with superview. Screenshots attached for reference.
Constraints shown in below screenshots creates the problem, change it according to next screenshot.
Actual constraints to remove white space should be according to this(below) screenshot.
For those that like to do everything programmatically, add this line to the init method of a ViewController that shouldn't have the tabBar:
hidesBottomBarWhenPushed = true
I was facing the same issue and root cause was BOTTOM CONSTRAINT
Make sure you set the bottom constraint of your bottom most view in the main view hierarchy with SUPERVIEW, NOT "SAFE AREA"
Hope this helps someone..
The third answer on this question works for me in the following way:
The code on my view controller
#IBAction func buttonPressed(sender: AnyObject) {
setTabBarVisible(!tabBarIsVisible(), animated: true)
}
func setTabBarVisible(visible: Bool, animated: Bool) {
// hide tab bar
let frame = self.tabBarController?.tabBar.frame
let height = frame?.size.height
var offsetY = (visible ? -height! : height)
print ("offsetY = \(offsetY)")
// zero duration means no animation
let duration:NSTimeInterval = (animated ? 0.3 : 0.0)
// animate tabBar
if frame != nil {
UIView.animateWithDuration(duration) {
self.tabBarController?.tabBar.frame = CGRectOffset(frame!, 0, offsetY!)
self.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height + offsetY!)
self.view.setNeedsDisplay()
self.view.layoutIfNeeded()
return
}
}
}
func tabBarIsVisible() -> Bool {
return self.tabBarController?.tabBar.frame.origin.y < UIScreen.mainScreen().bounds.height
}
In storyboard:
The view controller main view background color is black color:
Then you could have another view inside (background color white), constrained trailing and leading space to superview and top and bottom space to the layout guide.
And the result is:
My preferred way to do that is using a wrapping controller. If I want to hide the tab bar, I just increase the height of the tab bar controller, thus effectively the tab bar is moved out of the screen.
With this solution you don't need to hack tab bar frame and you don't depend on navigation controller push animation:
import UIKit
class ViewController: UIViewController {
let tabController: UITabBarController = {
let tabController = UITabBarController()
// setup your tabbar controller here
return tabController;
}()
var tabbarHidden = false {
didSet {
var frame = self.view.bounds;
if (tabbarHidden) {
frame.size.height += self.tabController.tabBar.bounds.size.height;
}
self.tabController.view.frame = frame;
}
}
override func viewDidLoad() {
super.viewDidLoad()
// add the tab controller as child controller
addChildViewController(self.tabController)
self.tabController.view.frame = self.view.bounds
self.tabController.view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.view.addSubview(self.tabController.view)
self.tabController.didMoveToParentViewController(self)
// for debugging
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(switchTabbar))
self.tabController.view.addGestureRecognizer(tapRecognizer)
}
override func childViewControllerForStatusBarStyle() -> UIViewController? {
return self.tabController
}
override func childViewControllerForStatusBarHidden() -> UIViewController? {
return self.tabController
}
func switchTabbar() {
UIView.animateWithDuration(0.3) {
self.tabbarHidden = !self.tabbarHidden
}
}
}
try to set the tab bar translucent to before you hide the tab bar set to false again when you want to show again.
it works for me.
tabBarController?.tabBar.isTranslucent = true
Yes. You can hide your tab bar when you push to view controller. You can show tab bar in your home. You can hide your tab bar when you push to next View controller.
See the Hide Botton Bar on Push following image and set in all viewcontrollers where you dont want tab bar.
Hope it helps..
Sometimes that easiest way is just to add a view that uses the UIScreen bounds.
let whiteView = UIView()
whiteView.backgroundColor = .white
view.addSubview(whiteView)
whiteView.translatesAutoresizingMaskIntoConstraints = false
whiteView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
whiteView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
whiteView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
whiteView.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height).isActive = true
Cause sometimes the view edges extends beyond the nav bar giving you new problems if you extend the view layout.
Tested in Swift 5.4.
If you're adding any ViewController's view as subview programmatically and not using pushViewController, then you can simply try as follows:
// When you wanna hide TabBar
tabBarController?.tabBar.isHidden = true
tabBarController?.tabBar.isTranslucent = true // This is the key point!
// When you wanna show TabBar
tabBarController?.tabBar.isHidden = false
tabBarController?.tabBar.isTranslucent = false // This is the key point!
This code works on iOS 10, 11, and iPhone X (including simulators) to show/hide the tabBar. I created it several years (iOS 7 time frame?) and it has worked reliably since that time.
It works great on iPhone X as long as content content in your childViewControllers (in tabs) is pinned to topLayoutGuide, bottomLayoutGuide or SafeArea and not the main views walls. Then it all just works. Enjoy!
#interface UITabBarController (HideTabBar)
#property (nonatomic, getter=isTabBarHidden) BOOL tabBarHidden;
-(void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated;
#end
#implementation UITabBarController (HideTabBar)
-(BOOL)isTabBarHidden
{
CGRect viewFrame = self.view.frame;
CGRect tabBarFrame = self.tabBar.frame;
return tabBarFrame.origin.y >= viewFrame.size.height;
}
-(void)setTabBarHidden:(BOOL)hidden
{
[self setTabBarHidden:hidden animated:NO];
}
-(void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated
{
BOOL isHidden = self.tabBarHidden;
if(hidden == isHidden)return;
UIView *transitionView = [[[self.view.subviews reverseObjectEnumerator] allObjects] lastObject];
if(transitionView == nil) {
NSLog(#"UITabBarCategory can't get the container view");
return;
}
CGRect viewFrame = self.view.bounds;
CGRect tabBarFrame = self.tabBar.frame;
CGRect containerFrame = transitionView.frame;
CGRect selectedVCFrame = containerFrame;
tabBarFrame.origin.y = viewFrame.size.height - (hidden ? 0 : tabBarFrame.size.height);
containerFrame.size.height = viewFrame.size.height - (hidden ? 0 : tabBarFrame.size.height);
if([self.moreNavigationController.viewControllers containsObject:self.selectedViewController]) {
selectedVCFrame = self.selectedViewController.view.frame;
selectedVCFrame.size.height += hidden ? tabBarFrame.size.height : -tabBarFrame.size.height;
}
self.selectedViewController.view.frame = selectedVCFrame;
[UIView animateWithDuration:.5 animations:^{
self.tabBar.frame = tabBarFrame;
transitionView.frame = containerFrame;
[self.selectedViewController.view setNeedsLayout];
}];
}
#end
Usage - I call it in the viewController on rotation events like so:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
// Hide TabBar on iPhone, iPod Touch
if([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad) {
if(_startDateEditor.editing) return;
if(fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown || fromInterfaceOrientation == UIInterfaceOrientationPortrait)
[self.tabBarController setTabBarHidden:YES animated:YES];
else
[self.tabBarController setTabBarHidden:NO animated:YES];
}
}
For me in iOS 13 I had to display image in cell with full screen, I had collection view with trailing, leading, top, bottom constraint. I removed all constraint. set collection view frame to UIScreen.main.bounds. then return sizeForItemAt as collection frame size.

Resources