.t9String.contains(t9String) }) } class func findWith(str: String) -> [PhoneContact] { return PhoneContactStore.instance .dataSource.filter({

iOSでT9検索を実装する方法

数年前、私は「 BOGmBank-モバイルバンキング 」私のiOS / Androidチームと。アプリには基本的な機能があり、モバイルバンキング機能を使用して、自分の携帯電話の後払い残高または連絡先の携帯電話残高を補充できます。

このモジュールの開発中に、iOSバージョンよりもAndroidバージョンのアプリの方が特定の連絡先を見つけるのがはるかに簡単であることに気付きました。どうして?この背後にある主な理由は、AppleデバイスにないT9検索です。

T9とは何か、なぜiOSの一部にならなかったのか、そしてその方法を説明しましょう。 iOS開発者 必要に応じて実装できます。



T9とは何ですか?

T9 は、携帯電話、特に物理的な3x4テンキーを含む携帯電話向けの予測テキストテクノロジーです。

テンキーでのT9検索の図

T9はもともとによって開発されました Tegic Communications 、および名前は 9キーのテキスト

T9がおそらくiOSに到達しなかった理由を推測できます。スマートフォン革命の間、T9入力は時代遅れになりました。これは、最近のスマートフォンの電話がタッチスクリーンディスプレイのおかげでフルキーボードに依存していたためです。 Appleは物理キーボードを備えた電話を持ったことがなく、T9の全盛期には電話ビジネスに携わっていなかったため、このテクノロジーがiOSから省略されたことは理解できます。

T9は、タッチスクリーンのない特定の安価な電話(いわゆるフィーチャーフォン)で引き続き使用されています。ただし、ほとんどのAndroidスマートフォンには物理キーボードが搭載されていませんが、最近のAndroidデバイスではT9入力がサポートされており、電話をかけようとしている連絡先の名前を入力することで連絡先にダイヤルできます。

実際のT9予測入力の例

テンキーを備えた電話機では、キー(1〜9)が押されるたびに(テキストフィールド内の場合)、アルゴリズムは、そのポイントまでに押されたキーの可能性が最も高い文字の推測を返します。

Xcodeのスクリーンショット

たとえば、「the」という単語を入力するには、ユーザーは8、4、3の順に押すと、ディスプレイに「t」、「th」、「the」の順に表示されます。あまり一般的ではない単語「fore」が意図されている場合(3673)、予測アルゴリズムは「Ford」を選択する場合があります。 「次へ」キー(通常は「*」キー)を押すと、「投与量」が表示され、最後に「前」が表示される場合があります。 「fore」が選択されている場合、次にユーザーがシーケンス3673を押すと、foreが最初に表示される単語である可能性が高くなります。ただし、「Felix」という単語を意図している場合は、33549と入力すると、ディスプレイに「E」、「De」、「Del」、「Deli」、「Felix」の順に表示されます。

これは、単語の入力中に文字が変化する例です。

iOSでのT9のプログラムによる使用

それでは、この機能について詳しく見ていき、iOS用のT9入力の簡単な例を書いてみましょう。まず、新しいプロジェクトを作成する必要があります。

私たちのプロジェクトに必要な前提条件は基本的です: MacにインストールされているXcodeおよびXcodeビルドツール。

新しいプロジェクトを作成するには、 Xcodeアプリケーション Macで、「Create a new Xcode project」を選択し、プロジェクトに名前を付けて、作成するアプリケーションのタイプを選択します。 「シングルビューアプリ」を選択し、「次へ」を押すだけです。

Xcodeのスクリーンショット

次の画面で、ご覧のとおり、提供する必要のある情報がいくつかあります。

注意: 開発者アカウントをお持ちでない場合は、シミュレーターでも実行できます。

[次へ]ボタンを押すと、開始する準備が整います。

シンプルなアーキテクチャ

ご存知のように、新しいアプリを作成すると、すでにMainViewControllerがあります。クラスとMain.Storyboard。もちろん、テスト目的でこのコントローラーを使用できます。

何かの設計を開始する前に、まず必要なすべてのクラスとファイルを作成して、ジョブのUI部分に移動するためのすべてのセットアップと実行が完了していることを確認しましょう。

プロジェクト内のどこかに、「」という名前の新しいファイルを作成するだけです。 PhoneContactsStore.swift 私の場合はこんな感じです。

T9検索ストーボードとアーキテクチャ

私たちの最初の仕事は、数字のキーボード入力のすべてのバリエーションでマップを作成することです。

import Contacts import UIKit fileprivate let T9Map = [ ' ' : '0', 'a' : '2', 'b' : '2', 'c' : '2', 'd' : '3', 'e' : '3', 'f' : '3', 'g' : '4', 'h' : '4', 'i' : '4', 'j' : '5', 'k' : '5', 'l' : '5', 'm' : '6', 'n' : '6', 'o' : '6', 'p' : '7', 'q' : '7', 'r' : '7', 's' : '7', 't' : '8', 'u' : '8', 'v' : '8', 'w' : '9', 'x' : '9', 'y' : '9', 'z' : '9', '0' : '0', '1' : '1', '2' : '2', '3' : '3', '4' : '4', '5' : '5', '6' : '6', '7' : '7', '8' : '8', '9' : '9' ]

それでおしまい。すべてのバリエーションを含む完全なマップを実装しました。それでは、「」という最初のクラスの作成に進みましょう。 PhoneContact 。」

ファイルは次のようになります。

画像の代替テキスト

まず、このクラスでは、A-Z + 0-9の正規表現フィルターがあることを確認する必要があります。

private let regex = try! NSRegularExpression(pattern: '[^ a-z()0-9+]', options: .caseInsensitive)

基本的に、ユーザーには表示する必要のあるデフォルトのプロパティがあります。

var firstName : String! var lastName : String! var phoneNumber : String! var t9String : String = '' var image : UIImage? var fullName: String! { get { return String(format: '%@ %@', self.firstName, self.lastName) } }

オーバーライドしたことを確認してくださいhashおよびisEqualリストフィルタリングのカスタムロジックを指定します。

また、文字列に数字以外のものが含まれないようにするには、replaceメソッドが必要です。

override var hash: Int { get { return self.phoneNumber.hash } } override func isEqual(_ object: Any?) -> Bool { if let obj = object as? PhoneContact { return obj.phoneNumber == self.phoneNumber } return false } private func replace(str : String) -> String { let range = NSMakeRange(0, str.count) return self.regex.stringByReplacingMatches(in: str, options: [], range: range, withTemplate: '') }

ここで、calculateT9に関連する連絡先を見つけるために、fullnameというもう1つのメソッドが必要です。またはphonenumber

func calculateT9() { for c in self.replace(str: self.fullName) { t9String.append(T9Map[String(c).localizedLowercase] ?? String(c)) } for c in self.replace(str: self.phoneNumber) { t9String.append(T9Map[String(c).localizedLowercase] ?? String(c)) } }

PhoneContactを実装した後オブジェクト、連絡先をメモリのどこかに保存する必要があります。この目的のために、PhoneContactStoreという新しいクラスを作成します。

2つのローカルプロパティがあります。

fileprivate let contactsStore = CNContactStore()

そして:

fileprivate lazy var dataSource = Set()

Setを使用していますこのデータソースの入力中に重複がないことを確認します。

final class PhoneContactStore { fileprivate let contactsStore = CNContactStore() fileprivate lazy var dataSource = Set() static let instance : PhoneContactStore = { let instance = PhoneContactStore() return instance }() }

ご覧のとおり、これはシングルトンクラスです。つまり、アプリが実行されるまでメモリに保持されます。シングルトンまたはデザインパターンの詳細については、次のように読むことができます。 ここに

現在、T9検索の完了に非常に近づいています。

すべてを一緒に入れて

Appleの連絡先リストにアクセスする前に、まず許可を求める必要があります。

class func hasAccess() -> Bool { let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts) return authorizationStatus == .authorized } class func requestForAccess(_ completionHandler: @escaping (_ accessGranted: Bool, _ error : CustomError?) -> Void) { let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts) switch authorizationStatus { case .authorized: self.instance.loadAllContacts() completionHandler(true, nil) case .denied, .notDetermined: weak var wSelf = self.instance self.instance.contactsStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (access, accessError) -> Void in var err: CustomError? if let e = accessError { err = CustomError(description: e.localizedDescription, code: 0) } else { wSelf?.loadAllContacts() } completionHandler(access, err) }) default: completionHandler(false, CustomError(description: 'Common Error', code: 100)) } }

連絡先へのアクセスを許可したら、システムからリストを取得するメソッドを作成できます。

fileprivate func loadAllContacts() { if self.dataSource.count == 0 { let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactThumbnailImageDataKey, CNContactPhoneNumbersKey] do { let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor]) request.sortOrder = .givenName request.unifyResults = true if #available(iOS 10.0, *) { request.mutableObjects = false } else {} // Fallback on earlier versions try self.contactsStore.enumerateContacts(with: request, usingBlock: {(contact, ok) in DispatchQueue.main.async { for phone in contact.phoneNumbers { let local = PhoneContact() local.firstName = contact.givenName local.lastName = contact.familyName if let data = contact.thumbnailImageData { local.image = UIImage(data: data) } var phoneNum = phone.value.stringValue let strArr = phoneNum.components(separatedBy: CharacterSet.decimalDigits.inverted) phoneNum = NSArray(array: strArr).componentsJoined(by: '') local.phoneNumber = phoneNum local.calculateT9() self.dataSource.insert(local) } } }) } catch {} } }

連絡先リストはすでにメモリにロードされています。つまり、簡単なメソッドを記述できます。

  1. findWith - t9String
  2. findWith - str
class func findWith(t9String: String) -> [PhoneContact] { return PhoneContactStore.instance.dataSource.filter({ $0.t9String.contains(t9String) }) } class func findWith(str: String) -> [PhoneContact] { return PhoneContactStore.instance .dataSource.filter({ $0.fullName.lowercased() .contains(str.lowercased()) }) } class func count() -> Int { let request = CNContactFetchRequest(keysToFetch: []) var count = 0; do { try self.instance.contactsStore.enumerateContacts( with: request, usingBlock: {(contact, ok) in count += 1; }) } catch {} return count }

それでおしまい。完了です。

これで、UIViewController内でT9検索を使用できます。

fileprivate let cellIdentifier = 'contact_list_cell' final class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var searchBar: UISearchBar! fileprivate lazy var dataSource = [PhoneContact]() fileprivate var searchString : String? fileprivate var searchInT9 : Bool = true override func viewDidLoad() { super.viewDidLoad() self.tableView.register( UINib( nibName: 'ContactListCell', bundle: nil ), forCellReuseIdentifier: 'ContactListCell' ) self.searchBar.keyboardType = .numberPad PhoneContactStore.requestForAccess { (ok, err) in } } func filter(searchString: String, t9: Bool = true) { } func reloadListSection(section: Int, animation: UITableViewRowAnimation = .none) { } }

フィルタメソッドの実装:

func filter(searchString: String, t9: Bool = true) { self.searchString = searchString self.searchInT9 = t9 if let str = self.searchString { if t9 { self.dataSource = PhoneContactStore.findWith(t9String: str) } else { self.dataSource = PhoneContactStore.findWith(str: str) } } else { self.dataSource = [PhoneContact]() } self.reloadListSection(section: 0) }

リストメソッドの実装をリロードします。

func reloadListSection(section: Int, animation: UITableViewRowAnimation = .none) { if self.tableView.numberOfSections <= section { self.tableView.beginUpdates() self.tableView.insertSections(IndexSet(integersIn:0..

そして、これが私たちの簡単なチュートリアルの最後の部分ですUITableView実装:

extension ViewController: UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return tableView.dequeueReusableCell(withIdentifier: 'ContactListCell')! } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.dataSource.count } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { guard let contactCell = cell as? ContactListCell else { return } let row = self.dataSource[indexPath.row] contactCell.configureCell( fullName: row.fullName, t9String: row.t9String, number: row.phoneNumber, searchStr: searchString, img: row.image, t9Search: self.searchInT9 ) } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 55 } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { self.filter(searchString: searchText) } }

まとめ

これでT9検索チュートリアルは終了です。iOSで簡単に実装できることを願っています。

しかし、なぜあなたはすべきですか?そして、なぜAppleはiOSにT9サポートを最初から含めなかったのですか?冒頭で指摘したように、T9は今日の携帯電話のキラー機能ではありません。これは、機械式のナンバーパッドを備えた「ダム」電話の時代への逆戻りです。

ただし、一貫性を保つため、またはアクセシビリティとユーザーエクスペリエンスを向上させるために、特定のシナリオでT9検索を実装する必要がある正当な理由はまだいくつかあります。もっと陽気な話ですが、あなたが懐かしい人なら、T9入力で遊んでみると、学生時代の懐かしい思い出がよみがえるかもしれません。

最後に、iOSでのT9実装の完全なコードは私の GitHubリポジトリ

基本を理解する

予測テキストとは何ですか?

予測テキストは、古い携帯電話で使用されている数字キーパッドなど、1つのキーまたはボタンが多くの文字を表す入力テクノロジです。また、特定のシナリオでアクセシビリティを向上させるためにも使用されます。

なぜT9はそれと呼ばれるのですか?

T9は、テキスト入力に9桁の数字キーパッドを使用しているため、9キーのテキストを表します。

キーボードでT9を使用するにはどうすればよいですか?

これが簡単な例です。 「HELLO」の場合は、4-3-5-5-6を押すだけです。これらは、「HELLO」を綴る文字を含む数字です。

.fullName.lowercased() .contains(str.lowercased()) }) } class func count() -> Int { let request = CNContactFetchRequest(keysToFetch: []) var count = 0; do { try self.instance.contactsStore.enumerateContacts( with: request, usingBlock: {(contact, ok) in count += 1; }) } catch {} return count }

それでおしまい。完了です。

これで、UIViewController内でT9検索を使用できます。

fileprivate let cellIdentifier = 'contact_list_cell' final class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var searchBar: UISearchBar! fileprivate lazy var dataSource = [PhoneContact]() fileprivate var searchString : String? fileprivate var searchInT9 : Bool = true override func viewDidLoad() { super.viewDidLoad() self.tableView.register( UINib( nibName: 'ContactListCell', bundle: nil ), forCellReuseIdentifier: 'ContactListCell' ) self.searchBar.keyboardType = .numberPad PhoneContactStore.requestForAccess { (ok, err) in } } func filter(searchString: String, t9: Bool = true) { } func reloadListSection(section: Int, animation: UITableViewRowAnimation = .none) { } }

フィルタメソッドの実装:

func filter(searchString: String, t9: Bool = true) { self.searchString = searchString self.searchInT9 = t9 if let str = self.searchString { if t9 { self.dataSource = PhoneContactStore.findWith(t9String: str) } else { self.dataSource = PhoneContactStore.findWith(str: str) } } else { self.dataSource = [PhoneContact]() } self.reloadListSection(section: 0) }

リストメソッドの実装をリロードします。

func reloadListSection(section: Int, animation: UITableViewRowAnimation = .none) { if self.tableView.numberOfSections <= section { self.tableView.beginUpdates() self.tableView.insertSections(IndexSet(integersIn:0..

そして、これが私たちの簡単なチュートリアルの最後の部分ですUITableView実装:

extension ViewController: UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return tableView.dequeueReusableCell(withIdentifier: 'ContactListCell')! } func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.dataSource.count } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { guard let contactCell = cell as? ContactListCell else { return } let row = self.dataSource[indexPath.row] contactCell.configureCell( fullName: row.fullName, t9String: row.t9String, number: row.phoneNumber, searchStr: searchString, img: row.image, t9Search: self.searchInT9 ) } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 55 } func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { self.filter(searchString: searchText) } }

まとめ

これでT9検索チュートリアルは終了です。iOSで簡単に実装できることを願っています。

しかし、なぜあなたはすべきですか?そして、なぜAppleはiOSにT9サポートを最初から含めなかったのですか?冒頭で指摘したように、T9は今日の携帯電話のキラー機能ではありません。これは、機械式のナンバーパッドを備えた「ダム」電話の時代への逆戻りです。

ただし、一貫性を保つため、またはアクセシビリティとユーザーエクスペリエンスを向上させるために、特定のシナリオでT9検索を実装する必要がある正当な理由はまだいくつかあります。もっと陽気な話ですが、あなたが懐かしい人なら、T9入力で遊んでみると、学生時代の懐かしい思い出がよみがえるかもしれません。

最後に、iOSでのT9実装の完全なコードは私の GitHubリポジトリ

基本を理解する

予測テキストとは何ですか?

予測テキストは、古い携帯電話で使用されている数字キーパッドなど、1つのキーまたはボタンが多くの文字を表す入力テクノロジです。また、特定のシナリオでアクセシビリティを向上させるためにも使用されます。

なぜT9はそれと呼ばれるのですか?

T9は、テキスト入力に9桁の数字キーパッドを使用しているため、9キーのテキストを表します。

キーボードでT9を使用するにはどうすればよいですか?

これが簡単な例です。 「HELLO」の場合は、4-3-5-5-6を押すだけです。これらは、「HELLO」を綴る文字を含む数字です。