iOS翻译-如何自定义SearchBar

jopen 9年前

翻译了一篇appcoda的文章,介绍如何自定义SearchBar的文章

原文链接: http://www.appcoda.com/custom-search-bar-tutorial/

app里面经常需要展示一些搜索的数据显示到tableview上。不幸的是,大部分开发者使用原生的控件。在iOS8里,苹果提供了一个特殊的控件 UISearchDisplayController 。这个控件结合了 UISeachBar ,很轻松的添加搜索的功能。不过,现在已经成为了历史。

UISearchController 在iOS8里面已经取代了 UISearchDisplayController . 尽管这虽然改变,但是在IB里面没有继承可视化控件,它必须用代码去初始化配置,不过非常简单。

除了上面所说的,有一个有趣的关于tableview datasource搜索的地方。iOS提供了一个searchbar预设的外观,这个bar适用于大多数情况。 然而,当UI需要高度自定义,这个searchbar看起来与我们的不是那么搭。在这种情况下,我们的searchbar必须适当的定制。

在这个教程里面,会展示 UISearchController 在iOS8里面是如何搜索和过滤数据的,使用默认的searchbar。

接着如何重写默认的searchbar的外观,实现自定义。所以我们设置了 UISearchController 和 UISearchBar 的子类,里面的代码通俗易懂,并且控件非常通用,可以复用。

Demo App Overview

我们将实现下面两个界面

第一张在 UISearchController 使用默认 searchbar 。第二张是自定义的searchbar.

首先下载工程的: Demo

加载和展示数据

首先加载 countries.txt 文件里面的数据,然后我们再通过列表的方式展示。

打开 ViewController.swift 文件,声明下面的属性

var dataArray = [String]() //countries.txt文件里面的内容,tableview展示的数据     var filteredArray = [String]()//搜索匹配的内容     var shouldShowSearchResults = false //控制是否需要展示数据
</div>

加载tableView所需要的数据数据

 func loadListOfCountries() {      // Specify the path to the countries list file.      let pathToFile = NSBundle.mainBundle().pathForResource("countries", ofType: "txt")         if let path = pathToFile {          // Load the file contents as a string.          let countriesString = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)!             // Append the countries from the string to the dataArray array by breaking them using the line change character.          dataArray = countriesString.componentsSeparatedByString("\n")             // Reload the tableview.          tblSearchResults.reloadData()      }  }
</div>

在 viewDidLoad() 里面调用它

override func viewDidLoad() {      ...         loadListOfCountries()  }
</div>

更新数据,返回dataArray

 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {      if shouldShowSearchResults {          return filteredArray.count      }      else {          return dataArray.count      }  }
</div>

确定cell需要展示的内容

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {      var cell = tableView.dequeueReusableCellWithIdentifier("idCell", forIndexPath: indexPath) as! UITableViewCell         if shouldShowSearchResults {          cell.textLabel?.text = filteredArray[indexPath.row]      }      else {          cell.textLabel?.text = dataArray[indexPath.row]      }         return cell  }
</div>

下面我们来配置search controller 显示默认的search bar

配置UISearchController

首先在 ViewController.swift 里面声明一个searchController

var searchController: UISearchController!
</div>

接下来我们来实现 configureSearchController() 方法。

func configureSearchController() {         // Initialize and perform a minimum configuration to the search controller.         searchController = UISearchController(searchResultsController: nil)         searchController.searchResultsUpdater = self         searchController.dimsBackgroundDuringPresentation = false         searchController.searchBar.placeholder = "Search here..."         searchController.searchBar.delegate = self         searchController.searchBar.sizeToFit()                  // Place the search bar view to the tableview headerview.         tblSearchResults.tableHeaderView = searchController.searchBar     }
</div>

这里有一个重要的知识点:在我们的demo中tableview显示搜索结果是在一个已经存在的controller中,然而还有一种情况是把搜索结果展示到另外一个ViewController中,所以需要知道把搜索结果展示到哪里. 当初始化的时候传 nil ,search controller 知道viewController已经存在,搜索数据回调到给这个viewController. 另外一种情况就是传指定的controller。

在搜索的时候有两种方法更新我们的tableview,第一种是通过searchbar的delegate。第二种是在delegate对象的Viewcontroller里面设置 searchResultsUpdater 属性(bool),稍后会在自定义控制器中看见这个是如何使用的。

在这里我们通过实现 UISearchResultsUpdating 协议。这个是iOS8才有的.

func updateSearchResultsForSearchController(searchController: UISearchController) {      let searchString = searchController.searchBar.text         // Filter the data array and get only those countries that match the search text.      filteredArray = dataArray.filter({ (country) -> Bool in          let countryText: NSString = country             return (countryText.rangeOfString(searchString, options: NSStringCompareOptions.CaseInsensitiveSearch).location) != NSNotFound      })         // Reload the tableview.      tblSearchResults.reloadData()  }
</div>

这里用了 searchString 保存关键字,然后用filter过滤

最后实现 UISearchbarDelegate

 func searchBarTextDidBeginEditing(searchBar: UISearchBar) {      shouldShowSearchResults = true      tblSearchResults.reloadData()  }      func searchBarCancelButtonClicked(searchBar: UISearchBar) {      shouldShowSearchResults = false      tblSearchResults.reloadData()  }    func searchBarSearchButtonClicked(searchBar: UISearchBar) {            if !shouldShowSearchResults {                    shouldShowSearchResults = true          tblSearchResults.reloadData()      }      searchController.searchBar.resignFirstResponder()  }
</div>

最后运行app

自定义Searchbar

创建一个UISearchBar的子类 CustomSearchBar .在这里,我们会创建一个自定义的初始化方法,传递 frame , font , text color 等参数,在此之前,先定义两个属性

 var preferredFont: UIFont!     var preferredTextColor: UIColor!
</div>

创建一个初始化方法,并且改变了searchBar的类型。设置了 translucent 属性,背景是不透明的。

init(frame: CGRect, font: UIFont, textColor: UIColor) {     super.init(frame: frame)       self.frame = frame     preferredFont = font     preferredTextColor = textColor          searchBarStyle = UISearchBarStyle.Prominent     translucent = false     }
</div>

提示: searchbar 并不是单一的控件,textfield只是其中的一部分。另外searchbar有一个UIView作为子类,这个view里面包含textfield和一个backgroundView。 可以打印一下就知道了

print(searchController.searchBar.subviews[0].subviews)
</div>

将会输出

我测试的时候输出的是这样的:layer那里有一个null?

[<UISearchBarBackground: 0x7fb771e8e7f0; frame = (0 0; 600 44); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fb771e83cf0>> - (null), <UISearchBarTextField: 0x7fb771e97b70; frame = (0 0; 0 0); text = ''; clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x7fb771e969f0>>]
</div>

为了找到 searField ,写一个找到子类的方法

 func indexOfSearchFieldInSubviews() -> Int! {      var index: Int!      let searchBarView = subviews[0] as! UIView         for var i=0; i<searchBarView.subviews.count; ++i {          if searchBarView.subviews[i].isKindOfClass(UITextField) {              index = i              break          }      }         return index  }
</div>

接下来实现 drawRect 方法。 修改了textfield的大小,在底部添加了一条线

override func drawRect(rect: CGRect) {                       if let index = indexOfSearchFieldInSubviews() {                          let searchField : UITextField = (subviews[0]).subviews[index] as! UITextField                          searchField.frame = CGRectMake(5.0, 5.0, frame.size.width - 10.0, frame.size.height - 10.0)             searchField.font = preferredFont             searchField.textColor = preferredTextColor                          searchField.backgroundColor = barTintColor         }                  let startPoint = CGPointMake(0.0, frame.size.height)         let endPoint = CGPointMake(frame.size.width, frame.size.height)                  let path = UIBezierPath()         path.moveToPoint(startPoint)         path.addLineToPoint(endPoint)                  let shapeLayer = CAShapeLayer()         shapeLayer.path = path.CGPath         shapeLayer.strokeColor = preferredTextColor.CGColor         shapeLayer.lineWidth = 2.5                  layer.addSublayer(shapeLayer);                  super.drawRect(rect)              }
</div>

自定义SearchController

创建一个子类 CustomSearchController ,并且在里面增加 CustomSearchBar 的引用

var customSearchBar: CustomSearchBar!
</div>

自定义初始化方法

init(searchResultsController: UIViewController!, searchBarFrame: CGRect, searchBarFont: UIFont, searchBarTextColor: UIColor, searchBarTintColor: UIColor) {      super.init(searchResultsController: searchResultsController)         configureSearchBar(searchBarFrame, font: searchBarFont, textColor: searchBarTextColor, bgColor: searchBarTintColor)  }
</div>

需要添加两个方法

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {      super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)  }     required init(coder aDecoder: NSCoder) {      super.init(coder: aDecoder)  }
</div>

configureSearchBar() 方法

 func configureSearchBar(frame: CGRect, font: UIFont, textColor: UIColor, bgColor: UIColor) {      customSearchBar = CustomSearchBar(frame: frame, font: font , textColor: textColor)         customSearchBar.barTintColor = bgColor      customSearchBar.tintColor = textColor      customSearchBar.showsBookmarkButton = false      customSearchBar.showsCancelButton = true  }
</div>

在 viewController 里面初始化

 func configureCustomSearchController() {      customSearchController = CustomSearchController(searchResultsController: self, searchBarFrame: CGRectMake(0.0, 0.0, tblSearchResults.frame.size.width, 50.0), searchBarFont: UIFont(name: "Futura", size: 16.0)!, searchBarTextColor: UIColor.orangeColor(), searchBarTintColor: UIColor.blackColor())         customSearchController.customSearchBar.placeholder = "Search in this awesome bar..."      tblSearchResults.tableHeaderView = customSearchController.customSearchBar  }
</div>

最后实现的效果

完整的demo下载

参考demo

</div>

来自: http://www.liuchendi.com/2016/01/06/iOS/30_SearchBar/