How to show suggestions while searching in iOS? - ios

I am working with Swift 3. I am basically using an API to search for images from the web. I want to add suggestions to my searchBar when any word is being entered. For example when I search "Sa", I get suggestions like "Sand", "Samsung", "Sail", etc. Any suggestions as to how I can implement this?
The output I get after executing the query is in JSON format.
I want my Search Bar to look something like this

There are multiple ways.
You can use the KeyboardAccessory. The shows a view over the keyboard (where auto complete is normally places) which you can use this way you like, e.g. add three buttons which will trigger the input of the correct suggestion. Apple provides an example here: https://developer.apple.com/library/content/samplecode/KeyboardAccessory/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009462-Intro-DontLinkElementID_2
Another way is just by placing an view (most likely table view) on your current view, place just below the input filed, either with a fixed height of e.g. 3 items or till the keyboard. For this approach you can have a look into this tutorial: https://www.raywenderlich.com/336/auto-complete-tutorial-for-ios-how-to-auto-complete-with-custom-values

Try like this to search i am searching in array of dictionary with name i show the all result search in my tableview below the UISearchBar
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
{
let namePredicate = NSPredicate(format: "name contains[c] %#", searchText)
if HotelData.count > 0
{
searchData = (HotelData as NSArray).filtered(using: namePredicate) as [AnyObject]
tlbSearch.reloadData()
}
}

Related

How to create an XCUIElement query for the last UITableViewCell in a UITableView?

I am creating an XCTestCase. For one of the test methods, call it func testConfirmLastCellContent() { ... }, I want to confirm that there exists a staticText child element having a value of "98765" within a UITableViewCell (I put the text value in in the textLabel.text property of the table view cell). There are 4 table view cells listed above this cell. Each of them has a String value of "12345" for its textLabel.text, except for the second cell, which has the same text value as the last: "98765".
The String values for the textLabel.text property of each cell, then, looks like this:
"12345"
"98765"
"12345"
"12345"
"98765"
These textLabel.text values are the only visual sub-elements within the table view cells respectively.
If I create a simple XCUIElementQuery like so...
let staticTextForLastCell = XCUIApplication().staticTexts["98765"]
...and assert its existence as true like so...
XCTAssert(staticTextForLastCell.exists == true)
...then the test will pass, but how do I know I got the last cell? Obviously, I cannot know from that query, because it could be pulling from the 2nd cell.
Thus far, I cannot find an XCTest class/method to get ahold of the last UITableViewCell specifically and query its details.
My goal is to get the last cell and confirm its textLabel.text value is something I expect. How can I do this?
Note: I cannot add/subtract/change textual, visual, etc. elements on any of the table view cells.
Edit:
I should also note that the UITableViewCells are generated dynamically by the app being tested. So, I cannot tag them or populate the accessibilityIdentifier property for the textLabel or the cell.
My setup is the following:
Operating System: macOS Mojave 10.14.6
IDE: Xcode 10.3
SDK: iOS 12.4 SDK
Deploy Target: iOS 12.0
Language: Swift 5
You should probably add an extension to XCUIElementQuery
extension XCUIElementQuery {
var lastMatch : XCUIElement {return self.element(boundBy: self.count - 1)}
}
Then you can assert your cell like this:
XCTAssertEqual(app.cells.lastMatch.staticTexts.element.label,
"98765",
"Unexpected text of the last cell")
Here is something that works...
let app = XCUIApplication()
let cells = app.cells
let lastCell = cells.element(boundBy: cells.count - 1)
let textOfLastCell = lastCell.staticTexts["98765"]
XCTAssert(textOfLastCell.exists)
Note: Smart Monkey's answer used an extension, which may be advisable. The above is a more inline way.
Edit: I previously stated that I could not get it to work without querying for tables before querying for cells. It does work without the tables query.
Edit: Changed XCTAssert(textOfLastCell.exists == true) to XCTAssert(textOfLastCell.exists) based on comment from Chris. A bit cleaner.

Filtering Korean words/characters?

I'm a beginner and learning through doing. I made a list of universities on a tableview and I had a searchbar to filter the list. It is still working perfectly with English letters, but not for the Korean letters.
var filteredUniversities = University.generateUniversities()
override func viewDidLoad() {
super.viewDidLoad()
self.searchBarSetup()
}
func searchBarSetup() {
let searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: (UIScreen.main.bounds.width), height: 70))
searchBar.delegate = self
self.tableView.tableHeaderView = searchBar
}
//MARK: Search Bar Delegate
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
//called when text changes (or clears)
if searchText.isEmpty {
filteredUniversities = University.generateUniversities()
self.tableView.reloadData()
}else {
filterTableView(text: searchText)
}
}
func filterTableView(text:String) {
filteredUniversities = filteredUniversities.filter({ (mod) -> Bool in
return (mod.name.contains(text))
})
self.tableView.reloadData()
}
Does anyone know how to make the .filter recognize Korean letters? My universities are listed as follows. (there are many more, but they all follow the same format)
class func generateUniversities() -> [University]{
var universities: [University] = []
universities.append(University(name: "파고다 어학원", location: "부산", photo: UniversityPhotoDictionary["PAG"]!))
universities.append(University(name: "부산대학교", location: "부산", photo: UniversityPhotoDictionary["PNU"]!))
universities.append(University(name: "동아대학교", location: "부산", photo: UniversityPhotoDictionary["DAU"]!))
return universities.sorted(by: {$0.name < $1.name} )
}
Strangely, the sorted() method can handle the Korean characters, but it seems like the filter method is breaking down. (only for Korean characters, the English ones are fine)
So if I type in 파고다, it filters everything out and there are no universities to choose from.
Thanks a lot!
I assume you're working on a Korean keyboard, and when you type 파, I assume you first press ㅍ and then press ㅏ. (Forgive me if I'm wrong about the specifics of how you're typing here; my Korean is horrible, but I suspect you're doing something similar to this.)
Pressing ㅍ calls your filter routine (since it changes the text field). None of the strings contain ㅍ, so it removes everything. You then press ㅏ and it composes the character 파 and filters again, but everything's already gone.
You don't want to keep reassigning filteredUniversities. You want to filter every time on the full list. That way, even though you get nothing for ㅍ, you'll get a list for 파.
If you copy 파고다 and then paste it into this field (rather than typing it), I bet it works, since the filtering routine will only be called once.

Getting tag or index for a tap gesture recognizer

I have a uitapgesturerecognizer, for a uiview. I have 30 of these uiviews in a scrollview. Also in each of these views there is a label with different words in it for each view. Now when I hit a tap gesture recognizer, I want to access the text from the label for that particular view. Whats happening is I'm printing the words of the last view in the index. Can someone show me how I can access each view in each index.
open var Name: UILabel!
open func ynCategoryButtonClicked(_ sender: UITapGestureRecognizer) {
guard let text = Name.text else { return }
ynSearch.appendSearchHistories(value: text)
self.delegate?.ynCategoryButtonClicked(text: text)
}
The place where you are assigning your tapGesture to your "UIButtonView", add this line:
yourButton.accessibilityHint = "YourText"
And In your "buttonTapped()" method get the text like this:
let yourText = "\(sender.accessibilityHint)"
Hope this will help!
Honestly, it sounds like you should be using a UITableView. Either way though, you should alter your delegate method ynCategoryButtonClicked to also pass a reference to itself (so something like delegate?.ynCategoryButtonClicked(text: text, ynCategory: self)). Then if you go the TableView route you can find the index by using indexPath(for:UITableViewCell). Or, if you stick with the scrollview, you should be able to maintain an array of the views, and just search through them for the ynCategory.

Text search too slow for large array with search Bar & tableview on iPhone

I have a large array of size around 100000 entries. I am displaying it when user search in UISearchBar and it display filtered data on tableview. However the search bar is too slow while doing dynamic search. i.e I am filtering the table everytime the user puts in a character on search bar. As a result it gives entered text on searchBar as very late response. I just need to wait for it to appear on searchBar.
This is what I am trying to filter my array.
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
filtered = data.filter({ (text) -> Bool in
let tmp: NSString = text
let range = tmp.rangeOfString(searchText, options: [NSStringCompareOptions.CaseInsensitiveSearch ,
NSStringCompareOptions.AnchoredSearch])
return range.location != NSNotFound
})
if(filtered.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.tableView.reloadData()
}
Have you ever try query with Realm.io?
I Knew that faster than any DB manager in iOS now.
Realm.io
Just make sure that you are not blocking the main_queue. Maybe you are calling sqlite functions from main queue. If so, try using FMDatabaseQueue and call the methods from background queue and update the tableview from main queue. This will handle everything for you.

Displaying two sets of results in one TableViewController

I know this is a startlingly stupid question, but I can't figure it out. Every answer involves a UISearchBar, which is not what I've got.
I'm trying to display 2 sets of results on one TableViewController.
Results to display
1) everything in my managedObjectContext which is set up in my viewDidLoad
2) a filtered set of results if a predicate is selected.
On MyTableViewController, I have a popover which instantiates when I click a UIBarButtonItem on MyTableViewController. On the popover, I set a predicate on MyTableViewController.
Basically, I'd like to toggle what's displayed and that display toggle is driven by whether my variable is nil (everything displays) or filtered (variable sets a predicate).
Have two NSArray properties allValues and filteredValues. Set up all your delegate/dataSource properties using your filteredValues array.
Next, do something like this when you first get all your data:
self.allValues = [someController fetchAllValues];
self.filteredValues = self.allValues;
[self.myView.tableView reloadData];
Last, alter your filteredValues array whether or not a predicate is selected:
if (self.selectedPredicate) {
self.filteredValues = [self.allValues filteredArrayUsingPredicate:self.selectedPredicate];
} else {
self.filteredValues = self.allValues;
}
[self.myView.tableView reloadData];

Resources