Swift 4中基于Yelp自动补全API实现TableView搜索过滤
Hey there! Let's break down how to get your Yelp autocomplete results showing up under the search bar and properly filter your TableView. I'll walk you through step-by-step fixes and improvements:
First: Refactor Your Data Sources (Critical!)
Right now, you're using separate arrays (nameArray, locationArray, etc.) which makes filtering and maintenance messy. Let's switch to storing full business objects instead:
// Store all original businesses loaded on startup var originalBusinesses: [CDYelpBusiness] = [] // Filtered businesses for your main TableView var filteredBusinesses: [CDYelpBusiness] = [] // Autocomplete suggestions to show under the search bar var autocompleteSuggestions: [String] = []
When you first load your Yelp data, populate originalBusinesses with the full CDYelpBusiness objects—this lets you easily access names, addresses, images, and other properties later.
Step 1: Add an Autocomplete Dropdown TableView
The UISearchBar doesn't have a built-in dropdown, so we'll add a small UITableView to display suggestions right below it. Add this to your ViewController:
let autocompleteTableView = UITableView() override func viewDidLoad() { super.viewDidLoad() // Configure the autocomplete table autocompleteTableView.frame = CGRect( x: searchBar.frame.origin.x, y: searchBar.frame.origin.y + searchBar.frame.height, width: searchBar.frame.width, height: 200 // Adjust height as needed ) autocompleteTableView.delegate = self autocompleteTableView.dataSource = self autocompleteTableView.register(UITableViewCell.self, forCellReuseIdentifier: "AutocompleteCell") autocompleteTableView.isHidden = true // Hide until we have suggestions view.addSubview(autocompleteTableView) // Don't forget to enable the search bar's cancel button searchBar.showsCancelButton = true }
Step 2: Fix the searchBar(textDidChange) Method
Update your API call to populate the autocomplete list and handle state properly. We'll also add a small delay to avoid spamming the Yelp API as the user types:
var autocompleteTimer: Timer? func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { // Cancel any pending API requests autocompleteTimer?.invalidate() autocompleteSuggestions.removeAll() // Hide the dropdown if the search bar is empty guard !searchText.isEmpty else { autocompleteTableView.isHidden = true return } // Wait 0.3 seconds after the user stops typing to make the API call autocompleteTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false) { [weak self] _ in self?.fetchAutocompleteResults(for: searchText) } } private func fetchAutocompleteResults(for searchText: String) { yelpAPIClient.autocompleteBusinesses( byText: searchText, latitude: Double(Location.sharedInstance.latitude), longitude: Double(Location.sharedInstance.longitude), locale: CDYelpLocale.english_unitedStates ) { [weak self] response in guard let self = self, let response = response, let businesses = response.businesses else { self?.autocompleteTableView.isHidden = true return } // Populate autocomplete suggestions with business names self.autocompleteSuggestions = businesses.compactMap { $0.name } // Show the dropdown and refresh its data self.autocompleteTableView.isHidden = false self.autocompleteTableView.reloadData() // Optional: Pre-filter your main TableView with the autocomplete results self.filteredBusinesses = businesses self.yelpTableView.reloadData() } }
The [weak self] avoids memory leaks—always use this in async closures!
Step 3: Implement Autocomplete TableView Logic
Update your existing TableView delegate/dataSource methods to handle both the autocomplete dropdown and your main business list:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if tableView == autocompleteTableView { return autocompleteSuggestions.count } else { // Show filtered results if we're searching, else show all businesses return isFiltering() ? filteredBusinesses.count : originalBusinesses.count } } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if tableView == autocompleteTableView { let cell = tableView.dequeueReusableCell(withIdentifier: "AutocompleteCell", for: indexPath) cell.textLabel?.text = autocompleteSuggestions[indexPath.row] return cell } else { guard let cell = tableView.dequeueReusableCell(withIdentifier: "YelpCell") as? YelpCell else { return UITableViewCell() } // Get the correct business based on filtering state let business = isFiltering() ? filteredBusinesses[indexPath.row] : originalBusinesses[indexPath.row] // Populate cell using the full business object cell.YelpNameLbl.text = business.name cell.yelpAddressLbl.text = business.location?.address1 if let imageURL = business.imageURL { cell.yelpImg.downloadedFrom(url: imageURL) } cell.yelpDollarSignLbl.text = business.price if let distance = business.distance { // Convert meters to miles (divide by 1609.34) or keep as meters cell.distanceFromYou.text = String(format: "%.2f mi", distance / 1609.34) } return cell } } // Handle taps on autocomplete suggestions func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if tableView == autocompleteTableView { let selectedName = autocompleteSuggestions[indexPath.row] // Fill the search bar with the selected suggestion searchBar.text = selectedName // Hide the dropdown autocompleteTableView.isHidden = true // Filter main TableView to show only the selected business filteredBusinesses = originalBusinesses.filter { $0.name == selectedName } yelpTableView.reloadData() // Hide the keyboard searchBar.resignFirstResponder() } }
Step 4: Polish Filtering Logic
Update your isFiltering() and helper methods to work with the new data sources:
func searchBarIsEmpty() -> Bool { return searchBar.text?.isEmpty ?? true } func isFiltering() -> Bool { return !searchBarIsEmpty() && !filteredBusinesses.isEmpty } // Handle cancel button tap func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.text = "" autocompleteTableView.isHidden = true filteredBusinesses.removeAll() yelpTableView.reloadData() searchBar.resignFirstResponder() }
Quick Notes for Improvement
- Use AutoLayout: Instead of hardcoding the autocomplete table's frame, use AutoLayout constraints to make it adapt to different screen sizes.
- Error Handling: Add error cases for the Yelp API call (e.g., network failures) to inform the user.
- Scope Buttons: If you plan to use the scope buttons, update
selectedScopeButtonIndexDidChangeto filteroriginalBusinessesby category or price range.
内容的提问来源于stack exchange,提问作者Coding while Loading

