an image is worth a thousand word, please see the attached image. This is a mobile app that I am developing. There is visually that thin line between the navigation bar and search bar, but I have tried everything I can think of, as well as everything that ChatGPT suggested but I still can’t remove that line. Any out-of-the-box idea what I might be overlooking? How can I remove that thin line?
Edited: (Since some of you asked me of the codes, here they are, although as I said they are quite lengthy so I am not sure if that’s helpful…)
On AppDelegate (this method is called on each view controller under viewDidAppear):
extension UINavigationController{
func setupWhiteTintColor(){
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithDefaultBackground()
appearance.backgroundColor = UIColor(hex: "#4CAF50")
appearance.shadowColor = UIColor.clear
appearance.shadowImage = UIImage()
appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white,.font : UIFont.boldSystemFont(ofSize: 20)]
navigationBar.standardAppearance = appearance;
navigationBar.scrollEdgeAppearance = appearance;
navigationBar.compactAppearance = appearance;
navigationBar.tintColor = UIColor.white
navigationBar.setGradientBackground(colors: [UIColor(hex: "#388E3C"), UIColor(hex: "#4CAF50")], startPoint: .topLeft, endPoint: .bottomRight)
} else {
self.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white,.font : UIFont.boldSystemFont(ofSize: 20)]
self.navigationBar.tintColor = UIColor.white
self.navigationBar.barTintColor = UIColor(hex: "#4CAF50")
self.navigationBar.isTranslucent = false
self.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationBar.shadowImage = UIImage()
}
}
}
class UINavigationBarGradientView: UIView {
enum Point {
case topRight, topLeft
case bottomRight, bottomLeft
case custom(point: CGPoint)
var point: CGPoint {
switch self {
case .topRight: return CGPoint(x: 1, y: 0)
case .topLeft: return CGPoint(x: 0, y: 0)
case .bottomRight: return CGPoint(x: 1, y: 1)
case .bottomLeft: return CGPoint(x: 0, y: 1)
case .custom(let point): return point
}
}
}
private weak var gradientLayer: CAGradientLayer!
convenience init(colors: [UIColor], startPoint: Point = .topLeft,
endPoint: Point = .bottomLeft, locations: [NSNumber] = [0, 1]) {
self.init(frame: .zero)
let gradientLayer = CAGradientLayer()
gradientLayer.frame = frame
layer.addSublayer(gradientLayer)
self.gradientLayer = gradientLayer
set(colors: colors, startPoint: startPoint, endPoint: endPoint, locations: locations)
backgroundColor = .clear
}
func set(colors: [UIColor], startPoint: Point = .topLeft,
endPoint: Point = .bottomLeft, locations: [NSNumber] = [0, 1]) {
gradientLayer.colors = colors.map { $0.cgColor }
gradientLayer.startPoint = startPoint.point
gradientLayer.endPoint = endPoint.point
gradientLayer.locations = locations
}
func setupConstraints() {
guard let parentView = superview else { return }
translatesAutoresizingMaskIntoConstraints = false
topAnchor.constraint(equalTo: parentView.topAnchor).isActive = true
leftAnchor.constraint(equalTo: parentView.leftAnchor).isActive = true
parentView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
parentView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
}
override func layoutSubviews() {
super.layoutSubviews()
guard let gradientLayer = gradientLayer else { return }
gradientLayer.frame = frame
superview?.addSubview(self)
}
}
extension UINavigationBar {
func setGradientBackground(colors: [UIColor],
startPoint: UINavigationBarGradientView.Point = .topLeft,
endPoint: UINavigationBarGradientView.Point = .bottomLeft,
locations: [NSNumber] = [0, 1]) {
guard let backgroundView = value(forKey: "backgroundView") as? UIView else { return }
guard let gradientView = backgroundView.subviews.first(where: { $0 is UINavigationBarGradientView }) as? UINavigationBarGradientView else {
let gradientView = UINavigationBarGradientView(colors: colors, startPoint: startPoint,
endPoint: endPoint, locations: locations)
backgroundView.addSubview(gradientView)
gradientView.setupConstraints()
return
}
gradientView.set(colors: colors, startPoint: startPoint, endPoint: endPoint, locations: locations)
}
}
extension UITabBar {
func setGradientBackground(colors: [UIColor],
startPoint: UINavigationBarGradientView.Point = .topLeft,
endPoint: UINavigationBarGradientView.Point = .bottomLeft,
locations: [NSNumber] = [0, 1]) {
guard let backgroundView = value(forKey: "backgroundView") as? UIView else { return }
guard let gradientView = backgroundView.subviews.first(where: { $0 is UINavigationBarGradientView }) as? UINavigationBarGradientView else {
let gradientView = UINavigationBarGradientView(colors: colors, startPoint: startPoint,
endPoint: endPoint, locations: locations)
backgroundView.addSubview(gradientView)
gradientView.setupConstraints()
return
}
gradientView.set(colors: colors, startPoint: startPoint, endPoint: endPoint, locations: locations)
}
}
On each View Controller the following function is run:
func setupNavigationBarTitleView() {
let imageView = UIImageView(image: UIImage(named: "TransparentToyzone"))
imageView.contentMode = .scaleAspectFit
// Set the height constraint to 30 pixels
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.heightAnchor.constraint(equalToConstant: 30)
])
// Set the image view as the title view of the navigation bar
self.navigationItem.titleView = imageView
}
2
Answers
It turns out that the visual border comes from search bar instead, and for some reasons setting searchbar.layer.borderWidth = 0 does not help. however, by setting searchbar.layer.borderWidth = 1 and then also set the borderColor to be the same color as navigation bar and search bar, the border effectively disappear.
You will need to set the bar’s appearance using an instance of
UINavigationBarAppearance
. Specifically, you will want to set theshadowColor
attribute tonil
or.clear
.There are other appearance properties on
UINavigationBar
that may apply depending on location of the navigation bar on the screen.If you want to set this automatically for all navigation bars, do this early in your app launch