Remove 1px line at top of UISearchController in large titles UINavigationBar

一曲冷凌霜 提交于 2019-12-04 19:09:39

问题


I'm transitioning from a view with a large style UINavigationItem into a view that has a large style UINavigationItem and a UISearchController. I've customized the background colour of the UINavigationBar.

For some reason, there's a 1px line or space between the UINavigationBar and the UISearchController. I can tell it's from the NavigationBar, because if I scroll so that the search bar sticks to the top, the line disappears. I'm just not sure what's generating this line. I'm not using shadows, or anything fancy, other than setting the barTintColor and the tintColor. If I don't use large style titles, I see the line, but only when transitioning between views. It's like when transitioning the UISearchController just doesn't stick right to the UINavigationBar.

Any help in figuring out where this line is coming from is appreciated.

Update: On some further investigation, it seems this only happens when navigationItem.hidesSearchBarWhenScrolling is set to false. If it initially is hidden, then the animation from the previous view is smooth, and showing the bar is also smooth.


回答1:


Testing the navigation item and search bar setup you've described, I could only reproduce the 1px/point line during the transition animations.

Workaround: This line is actually a background layout view (forLastBaselineLayout) used by UINavigationBar ending up visible due to the buggy framing of UISearchBar. Set its background to match your navigation bar color to hide it in hind site

// MARK: UINavigationControllerDelegate

public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    viewController.navigationController?.navigationBar
        .forLastBaselineLayout.backgroundColor = .red // TODO:
}

Make sure to set the object implementing UINavigationControllerDelegate is set as navigationController.delegate to receive the above delegate call.

In addition (not part of the question): UISearchBar seems to behave strangely when transitioning (push and pop) between controllers especially when one has/show a searchBar but not the other. Workaround, I found it visually more pleasing to temporarily hide the search bar from presenting controllers. Here is the code I use:

public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    if let sender = navigationStack.popLast() {
        prepareSearchBarForTransition(from: sender)
    }
    navigationStack = navigationController.children
}

/// Set only from `navigationController: willShow`
private var navigationStack: [UIViewController] = []

func prepareSearchBarForTransition(from sender: UIViewController) {
    if #available(iOS 11.0, *) {
        let searchController = sender.navigationItem.searchController
        sender.navigationItem.searchController = nil
        weak var weakSender = sender
        let navigationTransitionDuration: TimeInterval = 0.33
        DispatchQueue.main.asyncAfter(deadline: .now() + navigationTransitionDuration) {
            /// Reset the search bar.
            weakSender?.navigationItem.searchController = searchController
        }
    } else {
        // TODO: Check if the above workaround is neccessary for < iOS 11
    }
}



回答2:


To remove one pixel line from whole application.

UINavigationBar.appearance().shadowImage = UIImage()

Above code remove bottom line from whole application. if want remove only for some specific controllers only then require to manage hide/show bottom bar image. For that use below UINavigationBar extension to manage hide show bottom bar one pixel image.

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
    }
}

extension UINavigationBar {

    func hideBottomHairline() {
        self.hairlineImageView?.isHidden = true
    }

    func showBottomHairline() {
        self.hairlineImageView?.isHidden = false
    }
}

To manage hide/show

self.navigationController?.navigationBar.hideBottomHairline()

self.navigationController?.navigationBar.showBottomHairline()

Above UIView extension for hairlineImageView can also be used for other UIControls like UIToolBar

extension UIToolbar {

    func hideBottomHairline() {
        self.hairlineImageView?.isHidden = true
    }

    func showBottomHairline() {
        self.hairlineImageView?.isHidden = false
    }
}



回答3:


Use the following code:

navigationController?.navigationBar.shadowImage = UIImage()



回答4:


If you want to remove 1px line on UINavigationBar, in whole application you can use below code in AppDelegate (didFinishLaunchingWithOptions) (tested code)

UINavigationBar.appearance().shadowImage = UIImage()


来源:https://stackoverflow.com/questions/52346812/remove-1px-line-at-top-of-uisearchcontroller-in-large-titles-uinavigationbar

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!