我填写表格视图时遇到问题。目前,我正在制作电话簿。该电话簿将简单地包含在Contacts框架的帮助下获取的联系人。这些联系人按字母顺序分成几个部分。
可悲的是,尽管我的联系人在表格视图中按字母顺序显示,但我的应用程序崩溃了,因为我没有考虑联系人在其给定名称和姓氏中可能拥有的所有可能性。 (假设标准英语字母表没有变音符号,符号,数字或其他任何内容)
我一直坚持尝试实施如何确保表情符号,不同语言和特殊字符的方法。我没有找到任何好的解决方案。
让我引导您完成我的代码以增强我的解释:
首先,我创建了一个ExpandableNames结构:
struct ExpandableNames{
var isExpanded: Bool
var contacts: [Contact]
}
struct Contact {
let contact: CNContact
}
其中isExpanded只是一个状态,表示该联系人所在的部分是扩展还是已关闭。
现在,我已经创建了这两个数组来填充我的行和部分:
var array2d = Array<[Contact]>(repeating: [Contact](), count: 27)
var twoDimensionalArray = [ExpandableNames]()
注意:27是字母表中的字母数加上数字,表情符号和其他特殊字符的部分。
一旦我完成了这个设置,我就创建了一个获取联系人的功能。这是一切开始变得有点复杂的地方。
在下面的代码中,我已尽可能详细地评论了所有内容。我已经考虑了所有ASCII字符以及联系人姓名信息的所有可能性。
private func fetchContacts(){
let store = CNContactStore()
store.requestAccess(for: (.contacts)) { (granted, err) in
if let err = err{
print("Failed to request access",err)
return
}
if granted {
print("Access granted")
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
var contacts = [Contact]()
do{
//var favoritableContacts = [FavoritableContact]()
try store.enumerateContacts(with: request, usingBlock: { (contact, stopPointerIfYouWantToStopEnumerating) in
contacts.append(Contact(contact: contact))
})
//Find the ascii value for "A" to use as your base
let aAscii = Int("A".unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0]) //This returns 65, btw, so you could also just hardcode
//MARK: Checking every possibility that the user can Input for the names.
//Go through your original array, find the first letter of each contact's family name, and append to the correct array
contacts.forEach { (contact) in
//FAMILY NAMES ONLY WITH GIVEN NAMES
if contact.contact.familyName != "" && contact.contact.givenName != ""{
//Get the ascii value for the first letter of the family name
let firstCharacterFamilyName = Int(contact.contact.familyName.prefix(1).uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])
// - prefix(1) means take the first character of the family name
// - Uppercase makes sure even that even if the chracter is lowercased, it will be compared to A since the lowercased will become Upper cased.
// - A view of a string’s contents as a collection of Unicode scalar values
// - filter all the characters that arent ASCII.
// - map will loop over the collection and returns an array containing the results of applying a mapping or transform function to each item.
// - [0] will get the first value of the array
//Making sure it is between A and Z
if (firstCharacterFamilyName >= 65 && firstCharacterFamilyName <= 90){
//Append to the array for this letter by subtracting the ascii value for "A" from the ascii value for the uppercased version of this letter.
self.array2d[firstCharacterFamilyName - aAscii].append(contact)
//Checking for space ! " # $ % & ' ( ) * + , - . /
//Checking for numbers
//Checking for : ; < = > ? @
}else if(firstCharacterFamilyName >= 32 && firstCharacterFamilyName <= 64){
self.array2d[26].append(contact)
//Checking for space [ \ ] ^ _ `
}else if(firstCharacterFamilyName >= 91 && firstCharacterFamilyName <= 96){
self.array2d[26].append(contact)
//Checking for space { | } ~
}else if(firstCharacterFamilyName >= 123 && firstCharacterFamilyName <= 126){
self.array2d[26].append(contact)
}
//GIVEN NAMES ONLY WITHOUT FAMILY NAMES
}else if contact.contact.givenName != "" && contact.contact.familyName == ""{
//Get the ascii value for the first letter of the given name
let firstCharacterGivenName = Int(contact.contact.givenName.prefix(1).uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])
//Making sure it is between A and Z
if (firstCharacterGivenName >= 65 && firstCharacterGivenName <= 90){
//Append to the array for this letter by subtracting the ascii value for "A" from the ascii value for the uppercased version of this letter.
self.array2d[firstCharacterGivenName - aAscii].append(contact)
//Checking for space ! " # $ % & ' ( ) * + , - . /
//Checking for numbers
//Checking for : ; < = > ? @
}else if(firstCharacterGivenName >= 32 && firstCharacterGivenName <= 64){
self.array2d[26].append(contact)
//Checking for space [ \ ] ^ _ `
}else if(firstCharacterGivenName >= 91 && firstCharacterGivenName <= 96){
self.array2d[26].append(contact)
//Checking for space { | } ~
}else if(firstCharacterGivenName >= 123 && firstCharacterGivenName <= 126){
self.array2d[26].append(contact)
}
//GIVEN NAMES ONLY WITHOUT FAMILY NAMES
}else if contact.contact.familyName != "" && contact.contact.givenName == ""{
//Get the ascii value for the first letter of the given name
let firstCharacterFamilyName = Int(contact.contact.familyName.prefix(1).uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])
//Making sure it is between A and Z
if (firstCharacterFamilyName >= 65 && firstCharacterFamilyName <= 90){
//Append to the array for this letter by subtracting the ascii value for "A" from the ascii value for the uppercased version of this letter.
self.array2d[firstCharacterFamilyName - aAscii].append(contact)
//Checking for space ! " # $ % & ' ( ) * + , - . /
//Checking for numbers
//Checking for : ; < = > ? @
}else if(firstCharacterFamilyName >= 32 && firstCharacterFamilyName <= 64){
self.array2d[26].append(contact)
//Checking for space [ \ ] ^ _ `
}else if(firstCharacterFamilyName >= 91 && firstCharacterFamilyName <= 96){
self.array2d[26].append(contact)
//Checking for space { | } ~
}else if(firstCharacterFamilyName >= 123 && firstCharacterFamilyName <= 126){
self.array2d[26].append(contact)
}
}
}
for index in self.array2d.indices{
//For console visual
let startingValue = Int(("A" as UnicodeScalar).value) // 65
print(Character(UnicodeScalar(index + startingValue)!))
print("Number of items at \(index): \(self.array2d[index].count)")
let names = ExpandableNames(isExpanded: true, contacts: self.array2d[index])
//If there is no element in that index then dont do anything. If there is then append names to twoDimensionalArray
if self.array2d[index].count != 0{
self.twoDimensionalArray.append(names)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let err{
print("Failed to enumerate contacts", err)
}
}else{
print("Access denied")
}
}
}
其余的是简单的UI配置,并确保我将正确的部分和行添加到tableview。当我运行我的代码时,它看起来像这样:
只要我给他们的姓名或姓氏的联系人添加表情符号,该应用程序就崩溃了:
let firstCharacterFamilyName = Int(contact.contact.familyName.prefix(1).uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])
哪个有道理。现在这意味着我可能会因为检查给定姓名和姓氏的第一个字母的方式而使自己复杂化。我尝试过不同的方法,但这个方法是最接近成功的方法。
答案 0 :(得分:0)
假设您的目标是按字母顺序对所有联系人排序,并将所有非按字母顺序排序的联系人放入“第26条”
我提供以下解决方案......可能不是最好的,显然,因为你的问题也与整体设计有关。但也许它会让你以新的方式思考......它可以摆脱崩溃。
代码中的主要问题是
let firstCharacterFamilyName = Int(contact.contact.familyName.prefix(1).uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])
仅检查“prefix(1)”IS ASCII的条件...并且不检查或知道如何处理非ASCII的情况。
所以...在我们允许执行该行代码之前,我们需要确保我们正在处理可排序的联系人(以a-z或A-Z开头)。
首先......将以下函数添加到您的代码中:
func charIsALetter(input: Character) -> Bool {
if (!(input >= "a" && input <= "z") && !(input >= "A" && input <= "Z") ) {
return false
}
else
{
return true}
}
这将允许您检查任何单个字符是否为字母a-z或A-Z。
然后......您可以在代码中使用该功能:
// get the first letter like you normally do
let firstCharacter = contact.contact.givenName.prefix(1)
//
let result = charIsALetter(input: Character(String(firstCharacter)))
if result == true {
let firstCharacterGivenName = Int(firstCharacter.uppercased().unicodeScalars.filter({ $0.isASCII }).map({ $0.value })[0])
// do all your normal ASCII sorting here
}
else {
// put this contact into SECTION 26
}
现在你不需要这一切:
//Checking for space ! " # $ % & ' ( ) * + , - . /
//Checking for numbers
//Checking for : ; < = > ? @
}else if(firstCharacterFamilyName >= 32 && firstCharacterFamilyName <= 64){
self.array2d[26].append(contact)
//Checking for space [ \ ] ^ _ `
}else if(firstCharacterFamilyName >= 91 && firstCharacterFamilyName <= 96){
self.array2d[26].append(contact)
//Checking for space { | } ~
}else if(firstCharacterFamilyName >= 123 && firstCharacterFamilyName <= 126){
self.array2d[26].append(contact)
}
希望有所帮助。