You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何按ContactsUI样式的字母分组获取联系人?

实现与ContactsUI一致的联系人分组效果

嘿,完全可以做到和系统ContactsUI一模一样的联系人分组!苹果提供了专门的API来处理本地化排序、分组逻辑,涵盖你提到的设备语言、变音符号、数字符号等所有场景,不用自己从零造轮子。下面是具体的实现步骤:

1. 先让联系人按系统规则排序

首先在你获取联系人的fetchContacts方法里,一定要设置CNContactFetchRequest的排序规则为用户默认值,这样会完全遵循系统通讯录的排序逻辑(比如姓在前还是名在前),同时自动处理变音符号和本地化排序:

func fetchContacts() {
    let store = CNContactStore()
    // 定义你需要获取的联系人字段
    let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey] as [CNKeyDescriptor]
    let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
    
    // 关键:使用用户默认的排序规则,和系统通讯录保持一致
    fetchRequest.sortOrder = .userDefault
    
    do {
        try store.enumerateContacts(with: fetchRequest) { contact, stop in
            self.favoritableContacts.append(FavoritableContact(contact: contact, hasFavorited: false))
        }
    } catch {
        print("获取联系人失败:\(error.localizedDescription)")
    }
}

2. 用UILocalizedIndexedCollation生成系统级分组

UILocalizedIndexedCollation是苹果专门用来处理本地化列表分组和索引的类,它会自动根据当前设备的语言、区域设置生成对应的分组标题和索引栏,完美匹配ContactsUI的表现:

实现步骤:

func organizeContactsIntoSections() {
    // 获取当前设备的本地化排序器
    let collation = UILocalizedIndexedCollation.current()
    
    // 先把联系人按系统规则排序(用CNContactFormatter生成全名,保证和系统显示一致)
    let sortedContacts = favoritableContacts.sorted { contactA, contactB in
        guard let nameA = CNContactFormatter.string(from: contactA.contact, style: .fullName),
              let nameB = CNContactFormatter.string(from: contactB.contact, style: .fullName) else {
            return false
        }
        // 用collation的比较方法,确保排序逻辑和系统完全一致
        return collation.compare(nameA, to: nameB) == .orderedAscending
    }
    
    // 初始化分组数组,数量和collation的分组标题数一致
    var sections = Array(repeating: ExpandableNames(isExpanded: false, contacts: []), count: collation.sectionTitles.count)
    
    // 将每个联系人分配到对应的分组
    for contact in sortedContacts {
        guard let fullName = CNContactFormatter.string(from: contact.contact, style: .fullName) else { continue }
        // 获取当前联系人对应的分组索引
        let sectionIndex = collation.section(for: fullName, collationStringSelector: #selector(getter: NSString.description))
        sections[sectionIndex].contacts.append(contact)
    }
    
    // 过滤掉空分组(避免出现没有联系人的分组)
    twoDimensionalArray = sections.filter { !$0.contacts.isEmpty }
}

3. 自动处理特殊场景

这个方案会自动帮你搞定所有特殊情况:

  • 数字/符号开头的联系人:会被统一分到#分组,对应索引栏的#标识
  • 变音符号:比如西班牙语的ñ、法语的é,会根据当前语言的规则自动归类(西班牙语中ñ会单独成组,英语中会归到n组)
  • 非拉丁语言:中文、日文等语言会按照系统的拼音/假名排序规则生成对应的分组和索引栏,和系统通讯录完全一致

4. 配套TableView的索引栏

如果你的TableView需要显示右侧的索引栏,直接用collation.sectionIndexTitles即可:

override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
    return UILocalizedIndexedCollation.current().sectionIndexTitles
}

override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
    return UILocalizedIndexedCollation.current().section(forSectionIndexTitle: index)
}

这样处理后,你的联系人列表不管是分组逻辑、排序顺序还是索引栏表现,都会和系统ContactsUI完全一致,用户体验拉满~

内容的提问来源于stack exchange,提问作者user8169082

火山引擎 最新活动