4
votes

I’m new to swift dev and stack overflow, I’ll really appreciate if someone could shed some light over a search bar sizing issue I’m having. I’m not being able to correctly size a search bar in the screen. I created a simple version of my project to simplify the reproduction of the odd behavior. It’s a single ViewController with a tableView object to display results of a searchBar search. I don’t want the search bar to roll up along with the table cells, so I created a UIView as placeholder for the searchBar just above the tableView in the Main.storyboard and added necessary constraints to them, so they could be resized correctly with different screen sizes and orientations — indeed, auto-layout seems to be working fine.

Please see the image links as I still can't post images here. Main.storyboard image

I also don’t want to use a UISearchBar object in the storyboard because I want to have control over it in my code later. So I added the SearchController searchBar programmatically as a UIview subview with (apparently all) the needed constraints to be resized along with the UIview. Also, as you can see, tableView and the UIView widths are a little smaller than the screen width, it’s one difference to other posts I found, they were all fully stretched to screen edges — I don’t know if this is important or not. I'm using swift 4 on xcode 9.0.1 (9A1004).

The issue:

As you can see the searchBar right edge passes over the UIView frame right edge. When it's tapped the cancel button passes over the screen limit as well.

searchBar image 1

It's even more weird, when orientation is changed to landscape, searchBar width initially doesn’t stretch with the UIView. But when searchBar is tapped its width returns to pass over the screen limit. And when cancel is tapped searchBar width continues greater than the screen width. This behavior occurs no matter screen size and orientation I choose.

What I tried so far:

This project contains all the recommended properties and constraints I could find, with the exception of the property setTranslatesAutoresizingMaskIntoConstraints which I didn't manage to use. On swift 4, I only found a similar one translatesAutoresizingMaskIntoConstraints, but I couldn’t make it work, it returns “Cannot call value of non-function type 'Bool’”.

// searchController.searchBar.setTranslatesAutoresizingMaskIntoConstraints(true)    
searchController.searchBar.translatesAutoresizingMaskIntoConstraints(true)   (!) Cannot call value of non-function type 'Bool'

The post Display UISearchController's searchbar programmatically is a very good one, it’s the most similar to mine (except that it extends to the left, and my case it does do the right), but it didn’t work as well. I also realized that @kcstricks sets the searchBar width to self.view.frame.size.width instead of the UIview frame width.

// self.searchController.searchBar.frame.size.width = self.view.frame.size.width
self.searchController.searchBar.frame.size.width = searchBarPlaceholderView.frame.size.width

By changing it, solved the initial searchBar behavior. Now it's initially shown correctly:

searchBar image 2

But once it’s tapped the odd behavior returns and never gets back to normal again.

searchBar image 3

searchBar image 4

I also tried moving the main code to viewDidAppear method, but no results. Where am I failing at?

Other researched stack overflow posts:

Fixed UISearchBar using UISearchController - Not using header view of UITableView
UISearchController's searchBar doesn't fill full width
UISearchBar width doesn't change when its embedded inside a UINavigationBar

Thank you very much in advance.

Complete code:

//  ViewController.swift
//  SearchBarTests
//  Copyright © 2017 equilibrio. All rights reserved.

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchResultsUpdating {

    @IBOutlet var myTableView: UITableView!
    @IBOutlet var searchBarPlaceholderView: UIView!

    var searchController: UISearchController!

    var selectedBeers = [Beer]()

    struct Beer {
        var type = String()
        var examples = String()
    }

    var beers = [Beer(type: "American Lager", examples: "Budweiser, Coors, Pabst Blue Ribbon"),
                 Beer(type: "German Helles", examples: "Victory Helles Lager, Stoudt's Gold Lager"),
                 Beer(type: "German Pilsner", examples: "Tröegs Sunshine Pils, Bavaria, Sierra Nevada's Nooner Pilsner"),
                 Beer(type: "Belgian Gueuze", examples: "")]

    // Delegates and Datasources
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.selectedBeers.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "cell")

        cell.textLabel?.text = self.selectedBeers[indexPath.row].type
        cell.detailTextLabel?.text = self.selectedBeers[indexPath.row].examples
        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Row \(indexPath.row) selected")
    }

    func updateSearchResults(for searchController: UISearchController) {
        if searchController.searchBar.text! == "" {
            selectedBeers = beers
        } else {
            selectedBeers = beers.filter { $0.type.lowercased().contains(searchController.searchBar.text!.lowercased()) }
        }

        self.myTableView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        selectedBeers = beers

        self.searchController = UISearchController(searchResultsController: nil)
        self.searchController.searchResultsUpdater = self
        self.searchController.dimsBackgroundDuringPresentation = false
        self.searchController.searchBar.delegate = self
        definesPresentationContext = true

        self.searchBarPlaceholderView.addSubview(self.searchController.searchBar)
        self.searchController.searchBar.searchBarStyle = UISearchBarStyle.minimal
        // self.searchController.searchBar.frame.size.width = self.view.frame.size.width
        self.searchController.searchBar.placeholder = "Type desired beer style..."
        self.searchController.searchBar.frame.size.width = searchBarPlaceholderView.frame.size.width
        self.searchController.searchBar.sizeToFit()

        // searchController.searchBar.setTranslatesAutoresizingMaskIntoConstraints(false)
        // searchController.searchBar.translatesAutoresizingMaskIntoConstraints(true)

        self.myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        self.myTableView.reloadData()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
1
Similar issues here. No matter what I tried, in one or the other way the behaviour of the search bar is broken.CouchDeveloper

1 Answers

0
votes

I had the same issue. I added frame adjusting code to all over the place. For example when i choose an item from tableview suggestion list i call a function. Inside that function i wrote code below, so searchbar came back to width i wanted after animating. I hope somebody can give more professional solution. It doesnt look nice in my code but works.

func(youCallbyTapping) {


DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {

            var searchBarFrame = self.fromSearchController.searchBar.frame
            searchBarFrame.size.width = self.myFrom.frame.size.width - 15
            self.fromSearchController.searchBar.frame = searchBarFrame
            self.toSearchController.searchBar.frame = searchBarFrame
            }
}