滚动表格视图时单元格变为空白(swift)

Posted

技术标签:

【中文标题】滚动表格视图时单元格变为空白(swift)【英文标题】:Cells become blank when scrolling tableview (swift) 【发布时间】:2016-05-05 03:29:04 【问题描述】:

我有一个奇怪的问题,我的表格视图中的单元格不一致。有时它们会显示为空白单元格,有时它们会加载正确的数据。请参阅下面的 GIF。

请注意第 1 部分中的空白单元格每次都会更改。

我在向 tableview 添加新单元格时也遇到了这个问题,但是关闭并重新打开应用程序总是可以解决这个问题。添加时它只是无法正确加载......但有时它确实可以正确加载。请参阅下面的 GIF。

有人建议我使用 reloadData(),但这似乎没有任何帮助。我希望有人看到这个会知道该怎么做。

见下面的代码

表视图控制器:(Swift)

import UIKit
import CoreData

class ListItemsTVC: UITableViewController, NSFetchedResultsControllerDelegate 

// MARK: - Constants and Variables

let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc: NSFetchedResultsController = NSFetchedResultsController()
//var sequeItem: createItem?

// MARK: - App loading Functions

override func viewDidLoad() 
    super.viewDidLoad()

    frc = getFCR()
    frc.delegate = self

    do 
        try frc.performFetch()
     catch 
        print("Failed to perform inital fetch")
    
    self.tableView.rowHeight = 62



override func viewWillAppear(animated: Bool) 
    super.viewWillAppear(animated)

    self.tableView.reloadData()



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


// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int 

    if let sections = frc.sections 
        return sections.count
    

    return 0



override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 

    if let sections = frc.sections 
        let currentSection = sections[section]
        return currentSection.numberOfObjects
    

    return 0



override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 

    return 28



override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? 

    if let sections = frc.sections 
        let currentSection = sections[section]
        return currentSection.name
    

    return nil



func controllerWillChangeContent(controller: NSFetchedResultsController) 

    tableView.beginUpdates()



func controllerDidChangeContent(controller: NSFetchedResultsController) 

    tableView.endUpdates()



override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

    let cell = tableView.dequeueReusableCellWithIdentifier("listContentCell", forIndexPath: indexPath) as! ListItemsTVCell
    let item = frc.objectAtIndexPath(indexPath) as! Items

    cell.separatorInset = UIEdgeInsets(top: 0, left: 78, bottom: 0, right: 0)
    cell.itemName.text = item.name
    cell.itemSection.text = item.section
    cell.itemQty.text = "Qty: \(item.qty!)"
    cell.itemSize.text = item.size
    cell.itemPrice.text = floatToCurrency(Float(item.price!))
    //cell.itemImage.image = UIImage(data: item.image!)
    cell.itemID.text = String(item.id!)

    return cell



override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? 

    let delete = UITableViewRowAction(style: .Destructive, title: "Delete")  (action, indexPath) in

        let item = self.frc.objectAtIndexPath(indexPath) as! Items
        let id = item.id!
        let request = self.fetchRequest()
        let pred = NSPredicate(format: "%K == %@", "id",id)
        request.predicate = pred
        var fetchResults = [AnyObject]()

        do 
            fetchResults = try self.moc.executeFetchRequest(request)
         catch 
            fatalError("Fetching Data to Delete Failed")
        

        self.moc.deleteObject(fetchResults[0] as! NSManagedObject)
        fetchResults.removeAtIndex(0)

        do 
            try self.moc.save()
         catch 
            fatalError("Failed to Save after Delete")
        

    

    return [delete]



func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) 

    switch type 
    case NSFetchedResultsChangeType.Delete:
        self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)
        break
    case NSFetchedResultsChangeType.Insert:
        self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)
        break
    /*case NSFetchedResultsChangeType.Update:
        tableView.reloadSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)
        break*/
    default:
        print("Default in didChangeSection was called")
        break
    



func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) 

    switch type 
    case NSFetchedResultsChangeType.Delete:
        self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic)
        break
    case NSFetchedResultsChangeType.Insert:
        self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
        break
    default:
        print("Default in didChangeObject was called")
        break
    



// MARK: - Custom Functions

func fetchRequest() -> NSFetchRequest 

    let fetchRequest = NSFetchRequest(entityName: "Items")
    let sortDesc1 = NSSortDescriptor(key: "section", ascending: true)
    let sortDesc2 = NSSortDescriptor(key: "isChecked", ascending: true)
    let sortDesc3 = NSSortDescriptor(key: "name", ascending: true)
    fetchRequest.sortDescriptors = [sortDesc1, sortDesc2, sortDesc3]

    return fetchRequest



func getFCR() -> NSFetchedResultsController 

    frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "section" , cacheName: nil)

    return frc



func floatToCurrency(flt: Float) -> String 

    let formatter = NSNumberFormatter()
    formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
    return String(formatter.stringFromNumber(flt)!)




添加按钮视图控制器:(Swift)

import UIKit
import CoreData

class AddItemListVC: UIViewController, NSFetchedResultsControllerDelegate 

// MARK: - Constants and Variables

let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var sendItem: Items?

// MARK: - App loading Functions

override func viewDidLoad() 
    super.viewDidLoad()

    // Do any additional setup after loading the view.


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


// MARK: - Outlets and Actions

@IBAction func addItem(sender: AnyObject) 

    let entityDesc = NSEntityDescription.entityForName("Items", inManagedObjectContext: moc)
    let item = Items(entity: entityDesc!, insertIntoManagedObjectContext: moc)

    if (NSUserDefaults.standardUserDefaults().objectForKey("nextItemID") == nil) 
        NSUserDefaults.standardUserDefaults().setObject(1, forKey: "nextItemID")
        NSUserDefaults.standardUserDefaults().synchronize()
    

    let id = NSUserDefaults.standardUserDefaults().integerForKey("nextItemID")

    item.id = id
    switch id 
    case 1..<10:
        item.name = "Item ID 00\(id)"
    case 10..<100:
        item.name = "Item ID 0\(id)"
    default:
        item.name = "Item ID \(id)"
    
    item.brand = "Brand \(id)"
    item.qty = 1
    item.price = 0
    item.size = "Size \(id)"
    let sec: Int = Int(arc4random_uniform(UInt32(4 - 1))) + 1
    item.section = "Section \(sec)"
    item.isChecked = false

    do 
        try moc.save()
        NSUserDefaults.standardUserDefaults().setObject(id + 1, forKey: "nextItemID")
        NSUserDefaults.standardUserDefaults().synchronize()
     catch 
        fatalError("New item save failed")
    

    navigationController!.popViewControllerAnimated(true)



【问题讨论】:

你可以用上面的代码创建demo项目并分享到这里下载,任何人都可以看看并为你提供解决方案。 实际上,这是我的演示项目,我在实际应用中遇到了这个问题,所以我尽可能简单地构建它来尝试找出问题的根源。这是来自 Dropbox 的 zip 文件。我很想知道其他人是否看到相同的结果。从我广泛的研究来看,似乎我做的一切都是正确的。但是我可以在网上找到的大多数示例都使用数组而不是 CoreData,所以我猜这就是问题所在。 【参考方案1】:

@Jason Brady,我刚刚下载了你的代码。

你的核心数据、数组或表格视图没有问题。

当我在 iPhone 5 / iPhone 6 / iPhone 6 Plus 8.1 中运行应用程序时,它运行良好,没有一个单元格或添加按钮被隐藏。

但是使用 9.2 的相同设备会出现问题。

解决方案

(1) 带有 dequeueReusableCellWithIdentifier 的自定义单元格

let cell : ListItemsTVCell! = tableView.dequeueReusableCellWithIdentifier("listContentCell", forIndexPath: indexPath)  as! ListItemsTVCell

(2) DidselectedRowAtIndex - 在这里您将获得单元格选择的信息,因此数据运行良好。

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)

        print("Select")

        let myCell = tableView.cellForRowAtIndexPath(indexPath) as! ListItemsTVCell
        print(myCell.itemName.text)


(3) 问题在于 AutoLayout,当我禁用它时,所有标签都转到 -X 位置,当我使用自动调整大小正确对齐它时,它现在工作正常。 (见附件截图)

所以你需要检查 AutoLayout,为什么它在 ios 9 和更新版本中会出现问题。

参考link

希望你能进一步弄清楚并解决。

一切顺利。

Download

【讨论】:

非常感谢您在这方面的帮助。我从没想过尝试在早期版本的 iOS 中运行它。只是为了确保我理解,AutoLayout 是控制我在 StoryBoard 中应用的约束的东西......所以如果我用代码来定位所有标签,这可能会解决我的问题吗?我肯定会更多地研究 AutoLayout 并研究它的作用以及如何打开和关闭它。再次感谢您! Jason,从drive.google.com/file/d/0B7ooURy3zGWKblQ3aG85OWJnOHM/…下载整个项目 解决 iOS 9 的 AutoLayout 问题,如果 AutoLayout 真的不需要,则禁用它并使用自动调整掩码。做出明智的决定。 再次感谢您! 我还有一些测试要做,但到目前为止,如果我关闭尺寸类,我可以让 autoLayout 保持打开状态。如果事实并非如此,我会报告。【参考方案2】:

而不是使用

tableView.dequeueReusableCellWithIdentifier("listContentCell", forIndexPath: indexPath)

尝试使用

tableView.dequeueReusableCellWithIdentifier("listContentCell")

【讨论】:

感谢您的建议,但尝试这样做并没有改变任何东西。我仍然遇到两种情况,滚动空白的更改并添加新项目显示为空白。只是为了确保我已经说过,所有单元格都不应该是空白的。一旦我退出并重新启动应用程序,它们都会自行修复。 我把文件发给你会有帮助吗?

以上是关于滚动表格视图时单元格变为空白(swift)的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 表格视图中的单元格出现在另一个单元格上

带有透明单元格并显示背景图像的 Swift 表格视图

使用 Swift 在表格视图中重新加载单元格数据

表格内容在使用子视图的自定义单元格的 TableView 滚动上消失 - Swift

表格视图单元格中的滚动视图将无法正确显示图像,并且在 swift 4 中的分页模式下有一个额外的页面

Swift - 找出哪个表格视图单元占据了大部分屏幕