在强制转换中,无法将类型为“[Record]”的值转换为类型“Record”。

3
我正在将我用C#和Xamarin制作的iOS应用程序重写为Swift,原因是Xamarin的定价和文档不足。按照这篇教程在我的UITableView上包含一个UISearchBar时,我遇到了这个错误:Cannot convert value of type '[Record]' to type 'Record' in coercion。Record.swift是我创建的一个结构体文件,用于存储我将使用cloudkit检索的数据,但我不确定这个错误是如何产生的。

以下是MasterViewController.swift中有问题的部分:

func updateSearchResultsForSearchController(searchController: UISearchController) {
    self.filteredRecords.removeAll(keepCapacity: false)

    let searchPredicate = NSPredicate(format: "SELF CONTAINS [c] %@", searchController.searchBar.text!)
    // Here is where the error shows
    let array = (self.records as Record).filteredArrayUsingPredicate(searchPredicate)
    self.filteredRecords = array as! [Record]

    self.tableView.reloadData()
}

这里是完整的MasterViewController.swift文件:
import UIKit

class MasterViewController: UITableViewController, UISearchResultsUpdating {

    var detailViewController: DetailViewController? = nil
    var resultSearchController = UISearchController()
    var records = [Record]()
    var filteredRecords = [Record]()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.navigationItem.leftBarButtonItem = self.editButtonItem()

        let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
        self.navigationItem.rightBarButtonItem = addButton
        if let split = self.splitViewController {
            let controllers = split.viewControllers
            self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
        }

        // #REMOVE - Testing implementation until iCloud is setup
        self.records = [
            Record(album: "The Awakening", artist: "P.O.D.",  genre: "Hardcore Rock", year: 2015, speed: "33 1/3", size: 12),
            Record(album: "Attack of The Killer B-Sides", artist: "A Day to Remember",  genre: "Post-Hardcore", year: 2010, speed: "33 1/3", size: 7),
            Record(album: "All I Want", artist: "A Day to Remember",  genre: "Post-Hardcore", year: 2011, speed: "33 1/3", size: 7),
            Record(album: "The White Stripes", artist: "The White Stripes",  genre: "Rock", year: 2003, speed: "45", size: 7),
            Record(album: "Save Rock and Roll", artist: "Fall Out Boy",  genre: "Punk Rock", year: 2013, speed: "33 1/3", size: 10),
            Record(album: "A Perfect Sky", artist: "Yellowcard",  genre: "Pop-Punk", year: 2015, speed: "33 1/3", size: 10),
            Record(album: "Noise vs Beauty", artist: "Bassnectar",  genre: "Dubstep", year: 2014, speed: "33 1/3", size: 12)
        ]

        // Configure the search bar controller
        self.resultSearchController = UISearchController(searchResultsController: nil)
        self.resultSearchController.searchResultsUpdater = self
        self.resultSearchController.dimsBackgroundDuringPresentation = false
        self.resultSearchController.searchBar.sizeToFit()

        self.tableView.tableHeaderView = self.resultSearchController.searchBar

        // Reload the data upon startup
        self.tableView.reloadData()
    }

    override func viewWillAppear(animated: Bool) {
        self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed
        super.viewWillAppear(animated)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

//    func insertNewObject(sender: AnyObject) {
//        objects.insert(NSDate(), atIndex: 0)
//        let indexPath = NSIndexPath(forRow: 0, inSection: 0)
//        self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
//    }

    // MARK: - Segues

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "showDetail" {
            if let indexPath = self.tableView.indexPathForSelectedRow {
                let object = records[indexPath.row]
                let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
                controller.detailItem = object
                controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
                controller.navigationItem.leftItemsSupplementBackButton = true
            }
        }
    }

    // MARK: - Table View

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if (self.resultSearchController.active) {
            return self.filteredRecords.count
        } else {
            return self.records.count
        }
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

        if (self.resultSearchController.active) {
            let object = filteredRecords[indexPath.row]
            cell.textLabel!.text = object.album
            cell.detailTextLabel!.text = object.artist
            cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
        } else {
            let object = records[indexPath.row]
            cell.textLabel!.text = object.album
            cell.detailTextLabel!.text = object.artist
            cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
        }

        return cell
    }

    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return true
    }

    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            records.removeAtIndex(indexPath.row)
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
        }
    }

    func updateSearchResultsForSearchController(searchController: UISearchController) {
        self.filteredRecords.removeAll(keepCapacity: false)

        let searchPredicate = NSPredicate(format: "SELF CONTAINS [c] %@", searchController.searchBar.text!)
        let array = (self.records as Record).filteredArrayUsingPredicate(searchPredicate)
        self.filteredRecords = array as! [Record]

        self.tableView.reloadData()
    }
}

并且Record.swift:

import Foundation

struct Record {
    let album : String
    let artist : String
    let genre : String
    let year : Int
    let speed : String
    let size : Int
}

希望这只是一个简单的修复。如果不是,有什么教程建议吗?或者我应该到其他地方寻求帮助?

2个回答

2

因为我认为你的self.records类型是[Record],而你尝试将其转换为单个Record,这不是一个Array。因此这是不可能的。

请尝试这样做:

let array = (self.records as [Record]).filteredArrayUsingPredicate(searchPredicate)

更新:

请按照以下方式更新您的updateSearchResultsForSearchController方法:

func updateSearchResultsForSearchController(searchController: UISearchController)
{
    self.filteredRecords.removeAll(keepCapacity: false)


    self.filteredRecords = self.records.filter() {
        ($0.album.lowercaseString).containsString(searchController.searchBar.text!.lowercaseString)
    }
    self.tableView.reloadData()
}

如果您想同时搜索albumartist,请将此行替换为:
($0.album.lowercaseString).containsString(searchController.searchBar.text!.lowercaseString)

使用这条命令:

($0.album.lowercaseString).containsString(searchController.searchBar.text!.lowercaseString) || ($0.artist.lowercaseString).containsString(searchController.searchBar.text!.lowercaseString)

我之前尝试过,但是当时出现了这个错误:类型“[Record]”的值没有成员“filteredArrayUsingPredicate” - hightekjonathan
这里有两个选项可以帮助你解决问题:https://dev59.com/Z4Xca4cB1Zd3GeqPESvj 和 https://dev59.com/o2Af5IYBdhLWcg3wsUXT。 - Dharmesh Kheni
代码中应该不是 let array = (self.records as NSArray).filteredArrayUsingPredicate(searchPredicate)。因为 self.records[Record] 类型,所以你不需要将其转换类型,但是你需要将其转换成 NSArray 类型。 - sbarow
它确实会抛出一个错误,这就是我开始修改教程的原因。 - hightekjonathan
那么,他运行这段代码时它是如何有效的呢?教程说它是Xcode 7,应该是Swift 2对吧? - hightekjonathan
显示剩余4条评论

2

filteredArrayUsingPredicate是一个NSArray方法,所以你需要将它转换成NSArray:

let array = (self.records as NSArray).filteredArrayUsingPredicate(searchPredicate)

但这仍然会生成一个错误:“[Record]无法转换为NSArray”。原因是NSArray只能包含Objective-C对象,而Record是Swift结构体,不能转换为对象。有两种解决方案。
解决方案1:
将Record声明为对象。
class Record: NSObject {
    let album : String
    let artist : String
    let genre : String
    let year : Int
    let speed : String
    let size : Int

    init(album : String, artist : String, genre : String, year : Int, speed : String, size : Int) {
        self.album = album
        self.artist = artist
        self.genre = genre
        self.year = year
        self.speed = speed
        self.size = size
    }
}

接下来的代码应该可以正常工作。
let searchPredicate = NSPredicate(format: "SELF.artist CONTAINS [c] %@", searchController.searchBar.text!)
let array = (self.records as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredRecords = array as! [Record]

请注意,原始谓词不合理,会导致运行时错误,因为 Record 不是字符串,所以我进行了替换。

解决方案2:

使用 Swift 的过滤方式:

let searchText = searchController.searchBar.text!.lowercaseString
filteredRecords = self.records.filter { (aRecord) -> Bool in
    return aRecord.artist.lowercaseString.containsString(searchText)
}

我该如何让解决方案2搜索专辑和艺术家? - hightekjonathan
返回 aRecord.artist.lowercaseString.containsString(searchText) || aRecord.album.lowercaseString.containsString(searchText) - Cosyn
啊,太简单了。谢谢,我明天试一下。 - hightekjonathan
解决方案2本质上与Dharmesh Kheni编辑的答案相同。 - Cosyn
非常感谢!这个问题困扰了我一整晚。 - hightekjonathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接