在 Market 项目中发现一个漂亮的搜索页面,记录一下。
SearchViewController.swift:
import UIKit
import NVActivityIndicatorView
import EmptyDataSet_Swift
class SearchViewController: UIViewController {
//MARK: - IBOutlets
@IBOutlet weak var searchOptionsView: UIView!
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var searchTextField: UITextField!
@IBOutlet weak var searchButtonOutlet: UIButton!
//MARK: - Vars
var searchResults: [Item] = []
var activityIndicator: NVActivityIndicatorView?
//MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
searchTextField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)
tableView.emptyDataSetDelegate = self
tableView.emptyDataSetSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
activityIndicator = NVActivityIndicatorView(frame: CGRect(x: self.view.frame.width / 2 - 30, y: self.view.frame.height / 2 - 30, width: 60, height: 60), type: .ballPulse, color: #colorLiteral(red: 0.9998469949, green: 0.4941213727, blue: 0.4734867811, alpha: 1), padding: nil)
}
//MARK: - IBActions
@IBAction func showSearchBarBattonPressed(_ sender: Any) {
dismissKeyboard()
showSearchField()
}
@IBAction func searchButtonPressed(_ sender: Any) {
if searchTextField.text != "" {
searchInFirebase(forName: searchTextField.text!)
emptyTextField()
animateSearchOptionsIn()
dismissKeyboard()
}
}
//MARK: - Search database
private func searchInFirebase(forName: String) {
showLoadingIndicator()
searchAlgolia(searchString: forName) { (itemIds) in
downloadItems(itemIds) { (allItems) in
self.searchResults = allItems
self.tableView.reloadData()
self.hideLoadingIndicator()
}
}
}
//MARK: - Helpers
private func emptyTextField() {
searchTextField.text = ""
}
private func dismissKeyboard() {
self.view.endEditing(false)
}
@objc func textFieldDidChange (_ textField: UITextField) {
searchButtonOutlet.isEnabled = textField.text != ""
if searchButtonOutlet.isEnabled {
searchButtonOutlet.backgroundColor = #colorLiteral(red: 0.9998469949, green: 0.4941213727, blue: 0.4734867811, alpha: 1)
} else {
disableSearchButton()
}
}
private func disableSearchButton() {
searchButtonOutlet.isEnabled = false
searchButtonOutlet.backgroundColor = #colorLiteral(red: 0.6666666865, green: 0.6666666865, blue: 0.6666666865, alpha: 1)
}
private func showSearchField() {
disableSearchButton()
emptyTextField()
animateSearchOptionsIn()
}
//MARK: - Animations
private func animateSearchOptionsIn() {
UIView.animate(withDuration: 0.5) {
self.searchOptionsView.isHidden = !self.searchOptionsView.isHidden
}
}
//MARK: - Activity indicator
private func showLoadingIndicator() {
if activityIndicator != nil {
self.view.addSubview(activityIndicator!)
activityIndicator!.startAnimating()
}
}
private func hideLoadingIndicator() {
if activityIndicator != nil {
activityIndicator!.removeFromSuperview()
activityIndicator!.stopAnimating()
}
}
private func showItemView(withItem: Item) {
let itemVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(identifier: "itemView") as! ItemViewController
itemVC.item = withItem
self.navigationController?.pushViewController(itemVC, animated: true)
}
}
extension SearchViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ItemTableViewCell
cell.generateCell(searchResults[indexPath.row])
return cell
}
//MARK: - UITableView Delegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
showItemView(withItem: searchResults[indexPath.row])
}
}
extension SearchViewController: EmptyDataSetSource, EmptyDataSetDelegate {
func title(forEmptyDataSet scrollView: UIScrollView) -> NSAttributedString? {
return NSAttributedString(string: "No itmes to display!")
}
func image(forEmptyDataSet scrollView: UIScrollView) -> UIImage? {
return UIImage(named: "emptyData")
}
func description(forEmptyDataSet scrollView: UIScrollView) -> NSAttributedString? {
return NSAttributedString(string: "Please check back later")
}
func buttonImage(forEmptyDataSet scrollView: UIScrollView, for state: UIControl.State) -> UIImage? {
return UIImage(named: "search")
}
func buttonTitle(forEmptyDataSet scrollView: UIScrollView, for state: UIControl.State) -> NSAttributedString? {
return NSAttributedString(string: "Start searching...")
}
func emptyDataSet(_ scrollView: UIScrollView, didTapButton button: UIButton) {
showSearchField()
}
}
自定义 ItemTableViewCell:
import UIKit
class ItemTableViewCell: UITableViewCell {
//MARK: IBOutlets
@IBOutlet weak var itemImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!
@IBOutlet weak var priceLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func generateCell(_ item: Item) {
nameLabel.text = item.name
descriptionLabel.text = item.description
priceLabel.text = convertToCurrency(item.price)
priceLabel.adjustsFontSizeToFitWidth = true
if item.imageLinks != nil && item.imageLinks.count > 0 {
downloadImages(imageUrls: [item.imageLinks.first!]) { (images) in
self.itemImageView.image = images.first as? UIImage
}
}
}
}
自定义 Item:
import Foundation
import UIKit
//import InstantSearchClient
class Item {
var id: String!
var categoryId: String!
var name: String!
var description: String!
var price: Double!
var imageLinks: [String]!
init() {
}
init(_dictionary: NSDictionary) {
id = _dictionary[kOBJECTID] as? String
categoryId = _dictionary[kCATEGORYID] as? String
name = _dictionary[kNAME] as? String
description = _dictionary[kDESCRIPTION] as? String
price = _dictionary[kPRICE] as? Double
imageLinks = _dictionary[kIMAGELINKS] as? [String]
}
}
//MARK: Save items func
func saveItemToFirestore(_ item: Item) {
FirebaseReference(.Items).document(item.id).setData(itemDictionaryFrom(item) as! [String : Any])
}
//MARK: Helper functions
func itemDictionaryFrom(_ item: Item) -> NSDictionary {
return NSDictionary(objects: [item.id, item.categoryId, item.name, item.description, item.price, item.imageLinks], forKeys: [kOBJECTID as NSCopying, kCATEGORYID as NSCopying, kNAME as NSCopying, kDESCRIPTION as NSCopying, kPRICE as NSCopying, kIMAGELINKS as NSCopying])
}
//MARK: Download Func
func downloadItemsFromFirebase(_ withCategoryId: String, completion: @escaping (_ itemArray: [Item]) -> Void) {
var itemArray: [Item] = []
FirebaseReference(.Items).whereField(kCATEGORYID, isEqualTo: withCategoryId).getDocuments { (snapshot, error) in
guard let snapshot = snapshot else {
completion(itemArray)
return
}
if !snapshot.isEmpty {
for itemDict in snapshot.documents {
itemArray.append(Item(_dictionary: itemDict.data() as NSDictionary))
}
}
completion(itemArray)
}
}
func downloadItems(_ withIds: [String], completion: @escaping (_ itemArray: [Item]) ->Void) {
var count = 0
var itemArray: [Item] = []
if withIds.count > 0 {
for itemId in withIds {
FirebaseReference(.Items).document(itemId).getDocument { (snapshot, error) in
guard let snapshot = snapshot else {
completion(itemArray)
return
}
if snapshot.exists {
itemArray.append(Item(_dictionary: snapshot.data()! as NSDictionary))
count += 1
}
if count == withIds.count {
completion(itemArray)
}
}
}
} else {
completion(itemArray)
}
}