10

The documentation for UISearchController says that you can override - searchBar to provide a custom subclass of UISearchBar for the controller to use. The custom search bar does get used, and its own delegate methods are called correctly, but the UISearchResultsUpdating method no longer gets called when the search bar changes. Do I need to do a lot of wiring things up manually, or is there something I'm missing to get the controller to behave like it does with a natively supplied search bar?

Farski
  • 1,670
  • 3
  • 15
  • 30
  • Nothing in there is using a subclassed UISearchController that I can see – Farski Sep 18 '14 at 11:28
  • Have there been any updates on this issue? I'm struggling with the same. I used custom `UISearchBar` and `UISearchController` to avoid the Cancel button showing up. Now, `UISearchResultsUpdating` doesn't get called on search text changes, and therefore the results table fails to show up at all. – artooras Jul 06 '15 at 08:25
  • searchController.searchBar.showsCancelButton = NO – malhal Oct 26 '18 at 08:43

4 Answers4

4

Override the SearchBar getter in your custom UISearchController class, it have to return your custom SearchBar and it have to be already initialized, then you setup its properties only after the UISearchController init, this way all the UISearchController functionality are retained:

public class DSearchController: UISearchController {

    private var customSearchBar = DSearchBar()
    override public var searchBar: UISearchBar {
        get {
            return customSearchBar
        }
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }
    public init(searchResultsController: UIViewController?,
                searchResultsUpdater: UISearchResultsUpdating?,
                delegate: UISearchControllerDelegate?,
                dimsBackgroundDuringPresentation: Bool,
                hidesNavigationBarDuringPresentation: Bool,
                searchBarDelegate: UISearchBarDelegate?,
                searchBarFrame: CGRect?,
                searchBarStyle: UISearchBarStyle,
                searchBarPlaceHolder: String,
                searchBarFont: UIFont?,
                searchBarTextColor: UIColor?,
                searchBarBarTintColor: UIColor?, // Bar background
                searchBarTintColor: UIColor) { // Cursor and bottom line

        super.init(searchResultsController: searchResultsController)

        self.searchResultsUpdater = searchResultsUpdater
        self.delegate = delegate
        self.dimsBackgroundDuringPresentation = dimsBackgroundDuringPresentation
        self.hidesNavigationBarDuringPresentation = hidesNavigationBarDuringPresentation        

        customSearchBar.setUp(searchBarDelegate,
                              frame: searchBarFrame,
                              barStyle: searchBarStyle,
                              placeholder: searchBarPlaceHolder,
                              font: searchBarFont,
                              textColor: searchBarTextColor,
                              barTintColor: searchBarBarTintColor,
                              tintColor: searchBarTintColor)

    }
}

And this is my custom searchBar:

public class DSearchBar: UISearchBar {

    var preferredFont: UIFont?
    var preferredTextColor: UIColor?

    init(){
        super.init(frame: CGRect.zero)
    }

    func setUp(delegate: UISearchBarDelegate?,
               frame: CGRect?,
               barStyle: UISearchBarStyle,
               placeholder: String,
               font: UIFont?,
               textColor: UIColor?,
               barTintColor: UIColor?,
               tintColor: UIColor?) {

        self.delegate = delegate
        self.frame = frame ?? self.frame
        self.searchBarStyle = searchBarStyle
        self.placeholder = placeholder
        self.preferredFont = font
        self.preferredTextColor = textColor
        self.barTintColor = barTintColor ?? self.barTintColor
        self.tintColor = tintColor ?? self.tintColor
        self.bottomLineColor = tintColor ?? UIColor.clearColor()

        sizeToFit()

        //        translucent = false
        //        showsBookmarkButton = false
        //        showsCancelButton = true
        //        setShowsCancelButton(false, animated: false)
        //        customSearchBar.backgroundImage = UIImage()
    }

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


    let bottomLine = CAShapeLayer()
    var bottomLineColor = UIColor.clearColor()

    override public func layoutSubviews() {
        super.layoutSubviews()

        for view in subviews {
            if let searchField = view as? UITextField { setSearchFieldAppearance(searchField); break }
            else {
                for sView in view.subviews {
                    if let searchField = sView as? UITextField { setSearchFieldAppearance(searchField); break }
                }
            }
        }

        bottomLine.path = UIBezierPath(rect: CGRectMake(0.0, frame.size.height - 1, frame.size.width, 1.0)).CGPath
        bottomLine.fillColor = bottomLineColor.CGColor
        layer.addSublayer(bottomLine)
    }

    func setSearchFieldAppearance(searchField: UITextField) {
        searchField.frame = CGRectMake(5.0, 5.0, frame.size.width - 10.0, frame.size.height - 10.0)
        searchField.font = preferredFont ?? searchField.font
        searchField.textColor = preferredTextColor ?? searchField.textColor
        //searchField.backgroundColor = UIColor.clearColor()
        //backgroundImage = UIImage()
    }

}

Init example:

searchController = DSearchController(searchResultsController: ls,
                                     searchResultsUpdater: self,
                                     delegate: self,
                                     dimsBackgroundDuringPresentation: true,
                                     hidesNavigationBarDuringPresentation: true,
                                     searchBarDelegate: ls,
                                     searchBarFrame: CGRectMake(0.0, 0.0, SCREEN_WIDTH, 44.0),
                                     searchBarStyle: .Minimal,
                                     searchBarPlaceHolder: NSLocalizedString("Search a location...", comment: ""),
                                     searchBarFont: nil,
                                     searchBarTextColor: nil,
                                     searchBarBarTintColor: UIColor.whiteColor(),
                                     searchBarTintColor: iconsColor)
searchController.searchBar.keyboardAppearance = .Dark
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
Marco M
  • 1,215
  • 12
  • 12
3

This is a known bug. Unfortunately, there is no workaround that does not involve private API.

MyztikJenz
  • 1,650
  • 14
  • 19
  • Can you elaborate on this? I can't find any mention of this issue as a known bug. – pbuchheit Oct 10 '14 at 15:43
  • @Alex UISearchController is available only in iOS 8 and on. The compiler won't recognize the class in iOS 7 and below. Stick with UISearchDisplayController until you are only supporting iOS 8 and above. – Brian Sachetta Feb 05 '15 at 21:36
  • @MyztikJenz, do you know if there have been any changes regarding this bug? My problem is that the search results table fails to show up altogether because `UISearchResultsUpdating` doesn't get called on search text changes with a custom `UISearchBar`. – artooras Jul 06 '15 at 08:27
2

When you subclass UISearchController you can customise UISearchBar in getter (setter doesn't exist).

Example - in subclass implementation:

-(UISearchBar*)searchBar{
     UISearchBar *baseSearchBar = [super searchBar];
    if (baseSearchBar.showsScopeBar) {
        baseSearchBar.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 88);
    }else{
        baseSearchBar.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 44);
    }
    return baseSearchBar;
}

Hope this helps someone.

DarkoM
  • 335
  • 4
  • 5
-1

I think it's supposed to behave like that.

This is from UISearchController.h

// You are free to become the search bar's delegate to monitor for text changes and button presses.
@property (nonatomic, retain, readonly) UISearchBar *searchBar;

All that delegate method (updateSearchResultsForSearchController:) does is return your search controller so you can access its search bar.

You can just do that through your custom search bar delegate methods.

Matt Tang
  • 1,297
  • 11
  • 17