Filtering Korean words/characters? - ios

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.

Related

stackView.removeArrangedSubview isn't working perfecly

I have a register View controller. I have text field for the phone number, and another text field for the country number.
i need the country code text field to be on the left and behind it, we should have the phone number.
this is working when the app is on English lang, but when the user changes the language to Arabic, we will get the phone number text field on the left not the country code on the left.
so, i tried to re-arrange the stack view. and it worked, but the size of the stack just changed!
let language = getObjectFromUserDefault(forKey: userDefaultKeys.getLanguageKey) as? String
if KLanguageCode.Arabic.rawValue == language
{
self.mobiStack.removeArrangedSubview(self.mobiStack.arrangedSubviews.first!)
self.mobiStack.removeArrangedSubview(self.mobiStack.arrangedSubviews.first!)
self.mobiStack.setNeedsLayout()
self.mobiStack.layoutIfNeeded()
self.view.layoutIfNeeded()
self.mobiStack.insertSubview(self.txtMobile, at: 0)
self.mobiStack.insertSubview(self.countryCode, at: 1)
self.mobiStack.setNeedsLayout()
self.view.layoutIfNeeded()
}
You can simplify your code like this:
// properly unwrap optional
guard let v = self.mobiStack.arrangedSubviews.last else {
return
}
self.mobiStack.insertArrangedSubview(v, at: 0)
Notes:
when adding arranged subviews, you do NOT need to remove them first.
the above code will swap the two views.
You may want to explicitly set the order (instead of just swapping them)... So, you might use a function like this:
func orderFields(_ rtl: Bool) -> Void {
if rtl {
self.mobiStack.addArrangedSubview(self.txtMobile)
self.mobiStack.addArrangedSubview(self.countryCode)
} else {
self.mobiStack.addArrangedSubview(self.countryCode)
self.mobiStack.addArrangedSubview(self.txtMobile)
}
}
You can call that when you setup your views --
The fields will be ordered as instructed... adding them if they are not already arranged subviews, or re-ordering them if they are.
Also, unless you have something else going on that you have not shown us, there is no need to be calling:
self.mobiStack.setNeedsLayout()
self.view.layoutIfNeeded()

Using URL to display all system options for phone number in Swift

Swift has a class called UITextView, which has a property called dataDetectorTypes which would automatically scan the text for specified data types, (like phone numbers, maps locations etc.) highlight found occurrences, and provide options on user interaction. I am currently transitioning my app to use SwiftUI, and am struggling to reimplement a specific behavior of the data detector. Namely, when a phone number is tapped, it offers the option to call, but when force touched (or I assume long pressed) it offers multiple options from across the system, ranging from first party messaging apps to third party ones. The following two images show what I am referring to:
Call option on tap
System wide options on force touch
I have tried wrapping the UITextView inside a UIViewRepresentable, and this works for the most part, but it is ugly and makes integration with the rest of the app a bit more tricky. And after all, the way I am using it, there isn't really a need for all the power of a UITextView. It really is just a button with a link.
I am not sure what SwiftUI element to use, but have given the following considerations:
Text: though I don't think this can handle user interaction
Link: this might be relevant, but would mean I would need to set the minimum requirement to iOS 14
Button: this option seems the most promising. Using other SO answers, I have something like this:
Button("Click Me") {
guard let url = URL(string: "telprompt://\(self.phoneString)"),
UIApplication.shared.canOpenURL(url) else { return }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}.foregroundColor(.blue)
However this only achieves the first behavior, an option to call on tap, and not the second behavior. Is there a way to compose a phone URL in Swift to open multiple system generated options? Or should I be doing something different, using a different Xcode function?
AFAIK, there is no way to access that system action sheet programmatically. You could probably try to replicate all the buttons one-by-one, but honestly I would just use a UITextView wrapped in a UIViewRepresentable.
What problems are you having with that? I was having some layout issues, that were solved by using a SwiftUI Text view as a template.
Below is the class I was using. It detects all types of data, but you could easily modify it to just detect phone numbers.
struct DataDetectorText: View {
let text: String
init(_ text: String) {
self.text = text
}
var body: some View {
Text(text + "\n")
.fixedSize(horizontal: false, vertical: true)
.hidden()
.overlay(DataDetectorTextView(text: text))
}
}
private struct DataDetectorTextView: UIViewRepresentable {
let text: String
func makeUIView(context _: Context) -> UITextView {
let view = UITextView()
view.text = text
view.font = UIFont.preferredFont(forTextStyle: .body)
view.dataDetectorTypes = [.all]
view.isEditable = false
view.isSelectable = true
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
view.isScrollEnabled = false
view.textContainer.lineFragmentPadding = 0
return view
}
func updateUIView(_ uiView: UITextView, context _: Context) {
uiView.text = text
uiView.sizeToFit()
}
}

Error when searching in the new UITableViewDiffableDataSource [Snapshotting]

I know that this is a common error and has been discussed many times, but hear me out. I've read a LOT of those posts and none of them mentions my specific case and I haven't been able to figure the solution out by myself.
This is the error I'm getting:
[Snapshotting] Snapshotting a view (0x7fab10c300b0, _UIReplicantView) that has not been rendered at least once requires afterScreenUpdates:YES.
According to my observation it appears everytime I begin searching in the searchController. It seems to me, that the error pops up when the keyboard changes (from capital to small letters, vice versa...).
I've tried:
putting .layoutIfNeeded() everywhere I possibly could (tableView, cells, view, searchBar...)
all UI changes are happening on main thread
toying with different searchController.searchBar.searchTextField.becomeFirstResponder()
Needless to say, none of the above solved my issue.
Here is my code for searching:
func updateSearchResults(for searchController: UISearchController) {
let added = DBManager.shared.getAddedCurrencies()
DispatchQueue.main.async { searchController.searchBar.searchTextField.becomeFirstResponder() }
guard let filter = searchController.searchBar.text, !filter.isEmpty else {
filtering = false
filtered = []
updateDiffable(with: added)
return
}
filtering = true
filtered = added.filter({ (currency) -> Bool in
return currency.name.lowercased().contains(filter.lowercased()) || currency.code.lowercased().contains(filter.lowercased())
})
updateDiffable(with: filtered)
}
Updating diffable data source
func updateDiffable(with list: [Currency], animate: Bool = true) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Currency>()
snapshot.appendSections([.main])
snapshot.appendItems(list)
DispatchQueue.main.async {
self.diffableDataSource.apply(snapshot, animatingDifferences: animate)
}
}
As I said, error pops up precisely when I put first letter into search bar or when I remove it. Otherwise everything works just fine.
Any help or tips would be much appreciated!

Other language for UISearchBar

I have got a problem with UI SearchBar in swift. My problem is I want to use searchbar to find the word in Table View Cell. The word is not an English word.
Here is my code.
if ((searchText.contains("ၵ"))) {
filteredArray = homeDict.filter { $0._meaning.localizedCaseInsensitiveContains(searchText) }
}else{
filteredArray = homeDict.filter { $0._word.localizedCaseInsensitiveContains(searchText) }
}
if(filteredArray.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.tableView.reloadData()
That is work for only one word of "ၵ". I want to search, if the word in Searchbar the same as any word in Table View Cell, display result in cell row.
I believe this function uses the current Locale, not necessarily the locale that the letters might be in. Try testing with the device in the locale of the language the strings are in to check.
You could probably make your own contains with func lowercased(with locale: Locale?) -> String
You would need to know the locale the user intended the search to be in. You could also use this function to test what the lowercase version of a string is in various locales (to test the theory above).

How to show suggestions while searching in 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()
}
}

Resources