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

问题描述

我正在尝试构建一个以 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 的联系人应用程序中一样。

标签: iosswiftuilocalizedcollation

解决方案


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

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

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

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

可以使用数组的一件事String是使用选择器NSString.description。这是可行的,因为 SwiftString可以桥接到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因为不再需要这些代码。


推荐阅读