CosmicMind / Material

A UI/UX framework for creating beautiful applications.
http://cosmicmind.com
MIT License
12k stars 1.26k forks source link

Issue mixing searchbarcontroller and navigationcontroller #717

Closed bilsou closed 7 years ago

bilsou commented 7 years ago

I am trying to have the searchbar embedded within the navigationbar and for that I just modified the SearchBarController sample adding this in appdelegate:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func applicationDidFinishLaunching(_ application: UIApplication) {
        window = UIWindow(frame: Screen.bounds)
        window!.rootViewController = AppSearchBarController(rootViewController: AppNavigationController(rootViewController: RootViewController()))
        window!.makeKeyAndVisible()
    }
}

class AppNavigationController: NavigationController {
    open override func prepare() {
        super.prepare()
        guard let v = navigationBar as? NavigationBar else {
            return
        }

        v.depthPreset = .none
        v.dividerColor = .red//Color.grey.lighten3
        v.backgroundColor = .green
    }
}

Unfortunately this bit of code gives this:

simulator screen shot 15 mar 2017 18 55 47

Is there any way of making the searchbar fit the navigation bar ?

Thanks

daniel-jonathan commented 7 years ago

Hey :)

So that won't work. You can remove the SearchBarController and use the NavigationController directly. From there, you can then add the SearchBar to your navigationItem.centerViews property in the view controller you want to have the SearchBar displayed.

Let me know if you need any further help. Here is some sample code:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func applicationDidFinishLaunching(_ application: UIApplication) {
        window = UIWindow(frame: Screen.bounds)
        window!.rootViewController = AppNavigationController(rootViewController: FeedViewController())
        window!.makeKeyAndVisible()
    }
}
import UIKit
import Material

class AppNavigationController: NavigationController {
    open override func prepare() {
        super.prepare()
        isMotionEnabled = true
        prepareNavigationBar()
    }
}

extension AppNavigationController {
    fileprivate func prepareNavigationBar() {
        navigationBar.depthPreset = .none
        navigationBar.dividerColor = Color.grey.lighten3
    }
}
import UIKit
import Material

class FeedViewController: CollectionViewController {
    fileprivate let searchBar = SearchBar()

    open override func viewDidLoad() {
        super.viewDidLoad()
        isMotionEnabled = true
        view.backgroundColor = .black

        prepareSearchBar()
        prepareTextField()
        prepareNavigationItem()
    }
}

extension FeedViewController {
    fileprivate func prepareSearchBar() {
        searchBar.depthPreset = .none
        searchBar.backgroundColor = nil
        searchBar.delegate = self
    }

    fileprivate func prepareTextField() {
        let leftView = UIImageView()
        leftView.image = Icon.cm.search
        searchBar.textField.leftView = leftView
        searchBar.textField.leftViewMode = .always

        searchBar.motionIdentifier = "searchBar"
        searchBar.textField.cornerRadiusPreset = .cornerRadius2
        searchBar.textField.backgroundColor = Color.grey.lighten3.withAlphaComponent(0.5)
        searchBar.textField.delegate = self
    }

    fileprivate func prepareNavigationItem() {
        navigationItem.centerViews = [searchBar]
    }
}

extension FeedViewController: SearchBarDelegate {
    @objc
    func searchBar(searchBar: SearchBar, didClear textField: UITextField, with text: String?) {

    }

    @objc
    func searchBar(searchBar: SearchBar, didChange textField: UITextField, with text: String?) {

    }
}

extension FeedViewController: UITextFieldDelegate {
    @objc
    open func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
}
bilsou commented 7 years ago

You're really quick to answer, much appreciated.

I just tried your way, it does work if the view controller navigated to contains the search bar. Unfortunately in my case I have a tab bar and within it a SearchViewController for which I want to have the searchbar within the navigationbar.

More explicitly:

import UIKit
import Material

class MainTabBarController: NYKTabBarController {

    fileprivate let searchBar = SearchBar()

    internal override func viewDidLoad() {
        super.viewDidLoad()
    }

    internal override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        setupSearchBar()
    }

    internal override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        setupTabBarController()
    }

    private func setupSearchBar() {
        let leftView = UIImageView()
        leftView.image = Icon.cm.search
        searchBar.textField.leftView = leftView
        searchBar.textField.leftViewMode = .always

        searchBar.depthPreset = .none
        searchBar.backgroundColor = nil
        searchBar.motionIdentifier = "searchBar"
        searchBar.textField.cornerRadiusPreset = .cornerRadius2
        searchBar.textField.backgroundColor = Color.grey.lighten3.withAlphaComponent(0.5)
        searchBar.placeholder = "Ask and you shall receive"
        searchBar.textField.keyboardType = .default

        navigationItem.centerViews = [searchBar]
    }

    private func setupTabBarController() {
        let v1 = SearchViewController(searchBar)
        let v2 = ProfileViewController()
        let v3 = ProfileViewController()
        let v4 = ProfileViewController()
        let v5 = ProfileViewController()

        let n1 = AppNavigationController(rootViewController: v1)
        let n2 = AppNavigationController(rootViewController: v2)
        let n3 = AppNavigationController(rootViewController: v3)
        let n4 = AppNavigationController(rootViewController: v4)
        let n5 = AppNavigationController(rootViewController: v5)

        n1.tabBarItem = NYKTabBarItem(content:
            NYKTabBarItemContent(animator: NYKTabBarItemAnimator()))
        n2.tabBarItem = NYKTabBarItem(content:
            NYKTabBarItemContent(animator: NYKTabBarItemAnimator()))
        n3.tabBarItem = NYKTabBarItem(content:
            NYKTabBarItemContent(animator: NYKTabBarItemAnimator()))
        n4.tabBarItem = NYKTabBarItem(content:
            NYKTabBarItemContent(animator: NYKTabBarItemAnimator()))
        n5.tabBarItem = NYKTabBarItem(content:
            NYKTabBarItemContent(animator: NYKTabBarItemAnimator()))

        n1.tabBarItem.image = Icon.search
        n2.tabBarItem.image = Icon.phone
        n3.tabBarItem.image = Icon.favorite
        n4.tabBarItem.image = Icon.bell
        n5.tabBarItem.image = Icon.menu

        let controllers = [n1, n2, n3, n4, n5]
        self.viewControllers = controllers
    }
}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func applicationDidFinishLaunching(_ application: UIApplication) {
        window = UIWindow(frame: Screen.bounds)
        window!.rootViewController = AppNavigationController(rootViewController: MainTabBarController())
        window!.makeKeyAndVisible()
    }
}

As you can see I'm passing the searchbar as a parameter to SearchViewController which is defined here

class SearchViewController: UIViewController {
        fileprivate var searchBar: SearchBar!

 public init(_ searchBar: SearchBar) {
        super.init(nibName: nil, bundle: nil)

        self.searchBar = searchBar
    }
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        isMotionEnabled = true

        self.automaticallyAdjustsScrollViewInsets = false

        setupViews()
        setupBindings()

        view.backgroundColor = UIColor("#f8f8ff")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        searchBar.textField.becomeFirstResponder()
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

       searchBar.textField.resignFirstResponder()
    }
}

and here the result :

simulator screen shot 15 mar 2017 19 44 37

One thing to keep in mind, I have a table view in SearchViewController, it might be related

Thanks again.

daniel-jonathan commented 7 years ago

I am not sure exactly what the issue is. You can have the SearchBar in the NavigationBar from any view controller. Just add it to the correct navigationItem, or update that item when in that tab. So when in the SearchViewController, you can update the navigationItem of the TabBar, then remove it when necessary. The other option, is to use the SnackBarController itself and embed the TabBarController. Why exactly do you need the NavigationController in this case if you are using a TabBarController? This will effect how you set this all up.

bilsou commented 7 years ago

I basically wanted to have the tab bar containing the navigation, with each tab having its own navigation controller instead of the other way around. I saw after posting my mistake

window!.rootViewController = AppNavigationController(rootViewController: MainTabBarController())

changed with

window!.rootViewController = MainTabBarController()

Thanks again for being so available, cheers

daniel-jonathan commented 7 years ago

No worries :) All the best!