使用 UILocalizedIndexedCollat​​ion 在 Swift 4.2 中创建 tableView 部分索引时出现错误“无法识别的选择器发送到实例”

Posted

技术标签:

【中文标题】使用 UILocalizedIndexedCollat​​ion 在 Swift 4.2 中创建 tableView 部分索引时出现错误“无法识别的选择器发送到实例”【英文标题】:Getting error "unrecognized selector sent to instance" while using UILocalizedIndexedCollation to create tableView section Indexes in Swift 4.2 【发布时间】:2019-01-25 04:52:50 【问题描述】:

我正在尝试构建一个仿照 iPhone 的联系人应用程序的应用程序。在那个过程中,我试图将部分索引放在我的联系人表格视图的右侧。 Apple 的 ios 文档表视图编程指南建议使用 UILocalizedIndexedCollat​​ion 类。问题似乎出在 collat​​ionStringSelector 上。在 Swift 4 中,选择器需要暴露给@objc,我无论如何都找不到这样做,我尝试过的所有操作都返回此错误消息:

*** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[NSTaggedPointerStringlocalizedTitle]:无法识别的选择器已发送到实例

我尝试将选择器对象放入它自己的类中,以便将其公开给@objc,但这不起作用。这是我尝试过的代码:

class SelectorObj: NSObject 

    @objc var selector: Selector

    init(selector: Selector) 
        self.selector = selector
    

回到基线代码。这是我正在尝试的示例代码。

class ObjectTableViewController: UITableViewController 

    let collation = UILocalizedIndexedCollation.current()
    var sections: [[AnyObject]] = []
    var objects: [AnyObject] = [] 
        didSet 
            let selector: Selector = #selector(getter: UIApplicationShortcutItem.localizedTitle)
            sections = Array(repeating: [], count: collation.sectionTitles.count)

            let sortedObjects = collation.sortedArray(from: objects, collationStringSelector: selector)
            for object in sortedObjects 
                let sectionNumber = collation.section(for: object, collationStringSelector: selector)
                sections[sectionNumber].append(object as AnyObject)
            

            self.tableView.reloadData()
        
    

    override func viewDidLoad() 
        super.viewDidLoad()
        objects = (["One", "Two", "Three"] as [AnyObject])
    

    // MARK: UITableViewDelegate

    override func numberOfSections(in tableView: UITableView) -> Int 
        return sections.count
    

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return sections[section].count

    

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let cellData = sections[indexPath.section][indexPath.row]
        cell.textLabel!.text = (cellData as! String)
        return cell
    

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        return collation.sectionTitles[section]
    

    override func sectionIndexTitles(for tableView: UITableView) -> [String]? 
        return collation.sectionIndexTitles
    

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

预期的结果是在 tableView 联系人列表的右侧获取部分索引(A...Z,#),就像在 iPhone 的联系人应用程序中一样。

【问题讨论】:

【参考方案1】:

您传递给sortedArray(from:collationStringSelector:) 的选择器必须是在被排序的对象类型上找到的选择器。您的数组中的对象没有 localizedTitle 属性。

通常,您会拥有一个要排序的类或结构数组,并且您会传递类或结构的某些属性的选择器。

但是您的数组是String 的数组。所以你需要一个String 的属性来返回用于排序的字符串。

而且由于选择器的这种使用基于旧的 Objective-C 框架,因此该属性需要暴露给 Objective-C。

使用String 数组的一件事是使用选择器NSString.description。这是因为 Swift String 可以桥接到 NSString

您需要做的另一件事是将数组类型从 AnyObject 更改为真正的类型 - String

var sections: [[String]] = []
var objects: [String] = [] 
    didSet 
        let selector: Selector = #selector(NSString.description)
        sections = Array(repeating: [], count: collation.sectionTitles.count)

        let sortedObjects = collation.sortedArray(from: objects, collationStringSelector: selector)
        for object in sortedObjects 
            let sectionNumber = collation.section(for: object, collationStringSelector: selector)
            sections[sectionNumber].append(object)
        

        self.tableView.reloadData()
    


override func viewDidLoad() 
    super.viewDidLoad()
    objects = ["One", "Two", "Three"]

更新您尝试转换这些值as String 的其他代码,因为不再需要它。

【讨论】:

感谢您的帮助 rmaddy!你真的帮助我理解了选择器。无论如何要对多个键进行排序?我一直在使用 CNContact,想先按 familyName 排序,然后按 givenName 排序? 你应该把它作为一个新问题发布。

以上是关于使用 UILocalizedIndexedCollat​​ion 在 Swift 4.2 中创建 tableView 部分索引时出现错误“无法识别的选择器发送到实例”的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)