Swift:点击一个单元格时更改所有 UITableViewCells 的文本颜色

Posted

技术标签:

【中文标题】Swift:点击一个单元格时更改所有 UITableViewCells 的文本颜色【英文标题】:Swift: Change text color of all UITableViewCells when one cell is tapped 【发布时间】:2015-06-30 01:20:13 【问题描述】:

我在 Swift for ios 中有一个 UITableView,它应该一次只允许选择一个单元格。我进行了设置,以便点击在选中和未选中之间切换,长按将其设置为永久选中,这意味着它在视图加载时默认开始选中。当单元格超出滚动视图可见部分的边界然后重新进入时,我很难保持单元格标签的颜色,因此我使用我的数据模型来跟踪选择和永久选择的单元格。通过这样做,我绕过了 Apple 通过在界面生成器中选择“Single Selection”来制作 tableview 只允许单个单元格选择的方式。现在,我正在尝试构建自己的功能,以在点击另一个单元格时更改屏幕上可见的所有单元格的颜色。我目前正在尝试通过在点击每个单元格时循环遍历每个单元格并在该自定义单元格上调用将更改文本颜色的方法来做到这一点。问题是,它不起作用。如果我将单元格移出可见滚动区域然后再移入,单元格将正确着色,但如果我选择一个单元格,然后选择另一个单元格,则第一个单元格在可见时不会改变颜色。

这是我的代码。我已经删除了一些不相关的代码,所以如果您还有其他想看的内容,请告诉我。

import Foundation
import UIKit

class AddDataViewController : UIViewController 

    @IBOutlet weak var locationTableView: UITableView!

    let locationTableViewController = LocationTableViewController()

    override func viewWillAppear(animated: Bool) 
        // Set the data source and delgate for the tables
        self.locationTableView.delegate = self.locationTableViewController
        self.locationTableView.dataSource = self.locationTableViewController

        // Refresh the tables
        self.locationTableView.reloadData()

        // Add the table view controllers to this view
        self.addChildViewController(locationTableViewController)
    

    override func viewDidLoad()

        super.viewDidLoad()

        // Create a tap gesture recognizer for dismissing the keyboard
        let tapRecognizer = UITapGestureRecognizer()

        // Set the action of the tap gesture recognizer
        tapRecognizer.addTarget(self, action: "dismissKeyboard")

        // Update cancels touches in view to allow touches in tableview to be detected
        tapRecognizer.cancelsTouchesInView = false

        // Add the tap gesture recognizer to the view
        self.view.addGestureRecognizer(tapRecognizer)
    



class LocationTableViewController : UITableViewController, UITableViewDelegate, UITableViewDataSource 

    var locationSelected : String = ""

    func colorAllCells() 
        for section in 0 ... self.tableView.numberOfSections() - 1 
            for row in 0 ... self.tableView.numberOfRowsInSection(section) - 1 
                if row != 0 
                    let indexPath = NSIndexPath(forRow: row, inSection: section)
                    let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! PlayerLocationCell
                    cell.indexPath = indexPath.row - 1
                    cell.setCellColor()
                
             
        
    

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) 
        if indexPath.row != 0 
            // Color all of the cells
            self.colorAllCells()
        
    

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

        let cell = PlayerLocationCell(style: UITableViewCellStyle.Value2, reuseIdentifier: "addDataCell")

        cell.selectionStyle = UITableViewCellSelectionStyle.None       

        if indexPath.row != 0 
            cell.textLabel!.text = pokerLibrary.currentLog.locationList[indexPath.row - 1].name
            cell.indexPath = indexPath.row - 1
            cell.setCellColor()
        

        return cell
    


class PlayerLocationCell : UITableViewCell 

    let selectedColor = UIColor.greenColor()
    let deselectedColor = UIColor.grayColor()
    let permanentlySelectedColor = UIColor.blueColor()
    var shouldHighlightText : Bool = true
    var indexPath : Int = 0

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: "cellPressed")
        longPressRecognizer.minimumPressDuration = 1.0
        longPressRecognizer.cancelsTouchesInView = false
        self.addGestureRecognizer(longPressRecognizer)

        let tapRecognizer = UITapGestureRecognizer(target: self, action: "cellTapped")
        tapRecognizer.cancelsTouchesInView = false
        self.addGestureRecognizer(tapRecognizer)
    

    func setCellColor() 
        if shouldHighlightText 
            if self.isPermanentlySelected() 
                self.textLabel!.textColor = self.permanentlySelectedColor
            
            else if self.isCurrentlySelected() 
                self.textLabel!.textColor = self.selectedColor
            
            else 
                self.textLabel!.textColor = self.deselectedColor
            
        
    

    func cellTapped() 
        if shouldHighlightText 
            self.setPermanentlySelected(false)

            if self.isCurrentlySelected() 
                self.setSelectedProperty(false)
                self.setCellColor()
            
            else 
                self.setSelectedProperty(true)
                self.setCellColor()
            
        
    

    func cellPressed() 
        if self.shouldHighlightText 
            if !self.isPermanentlySelected() 
                self.setPermanentlySelected(true)
                self.setCellColor()
            
        
    

    func setPermanentlySelected(b : Bool) 
        if b 
            // Set all other locations to false
            for i in 0 ... pokerLibrary.currentLog.locationList.count - 1 
                pokerLibrary.currentLog.locationList[i].selected = false
                pokerLibrary.currentLog.locationList[i].permanentlySelected = false
            
        
        pokerLibrary.currentLog.locationList[self.indexPath].permanentlySelected = b
    

    func isPermanentlySelected() -> Bool 
        return pokerLibrary.currentLog.locationList[self.indexPath].permanentlySelected
    

    func setSelectedProperty(b : Bool) 
        if b 
            // Set all other locations to false
            for i in 0 ... pokerLibrary.currentLog.locationList.count - 1 
                pokerLibrary.currentLog.locationList[i].selected = false
                pokerLibrary.currentLog.locationList[i].permanentlySelected = false
            
        
        pokerLibrary.currentLog.locationList[self.indexPath].selected = b
    

    func isCurrentlySelected() -> Bool 
        return pokerLibrary.currentLog.locationList[self.indexPath].selected
    


如您所见,我目前正在从 didSelectRowAtIndexPath 方法内部调用 LocationTableViewController 的 colorAllCells 方法。 colorAllCells 方法循环遍历所有单元格并调用每个单元格的 setCellColor 方法,该方法将确定单元格颜色应该是什么并设置文本颜色。我已经验证了每个单元格的 setCellColor 方法都从循环中调用,并且它正确地确定了要设置的颜色。颜色只是不会改变。在遍历每个单元格并设置颜色后,我也尝试调用 self.tableView.reloadData(),但这也不起作用。

对于这个问题的任何帮助,我将不胜感激。如果您需要我澄清任何事情,请告诉我。谢谢!

【问题讨论】:

您是否尝试使用模型中每个单元格的设置来设置 cellForRow 中的单元格颜色?任何时候发生变化,你都可以在你的 tableview 上调用 reloadData,这些单元格就会更新。 我在 cellForRowAtIndexPath 方法中调用单元格的 setCellColor 方法。你指的是这个吗? @Jason247 你在很多层面上与框架作斗争,以至于我不知道从哪里开始——你让你的开发人员的生活变得更加艰难。请阅读此developer.apple.com/library/prerelease/ios/documentation/… @magma 恐怕这份文件并没有真正帮助我理解我的问题出在哪里。 【参考方案1】:

我终于找到了解决办法。如果我在调用 tableView.cellForRowAtIndexPath 之前删除对 self 的引用,并将代码从 colorAllCells 方法移动到 didSelectRowAtIndexPath 方法中,那么它可以工作。

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) 
    if indexPath.row != 0 
        // Color all of the cells
        for section in 0 ... self.tableView.numberOfSections() - 1 
            for row in 0 ... self.tableView.numberOfRowsInSection(section) - 1 
                if row != 0 
                    let indexPath = NSIndexPath(forRow: row, inSection: section)
                    if let cell = tableView.cellForRowAtIndexPath(indexPath) as? PlayerLocationCell 
                        cell.setCellColor()
                    
                
            
        
    

此解决方案有效。但是,当我将代码放在 didSelectRowAtIndexPath 方法中时,我不明白为什么它会起作用,但是如果我将该代码取出并将其放入自己的函数中然后调用该函数,它就不起作用。有谁知道这是为什么?

【讨论】:

当您滚动并且单元格被回收到不同的行/部分时会发生什么?他们得到正确的颜色吗?【参考方案2】:

您需要一些方法来管理视图控制器中单元格选择的状态。我会用 Set 来做,像这样:

class LocationTableViewController: UITableViewController 

    var selectedIndexPaths = Set<NSIndexPath>()

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

        self.selectedIndexPaths.insert( indexPath )

        if let cell = tableView.cellForRowAtIndexPath( indexPath ) as? PlayerLocationCell 
            // Change color of the existing cell
        
    

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

        if let cell = tableView.dequeueReusableCellWithIdentifier( "addDataCell" )as? PlayerLocationCell 

            if self.selectedIndexPaths.contains( indexPath ) 
                // Change color of the cell just dequeued/created
            
            return cell
        
        fatalError( "Could not dequeue cell of type 'PlayerLocationCell'" )
    

此外,您应该使用dequeueReusableCellWithIdentifier: 来重用表格视图单元格,而不是在您的tableView:cellForRowAtIndexPath 方法中创建新单元格。

【讨论】:

感谢 dequeueRe... 建议。我在另一张桌子上有这样的东西,但忘了在这张桌子上改变它。但是,关于您建议管理单元格选择的方法,我已经在我的数据模型中使用数组来执行此操作。 pokerLibrary.currentLog.locationList[i].selected = false。这工作正常,单元格不会因为某种原因而改变颜色,而不会将单元格移出视图然后再移回。 好的,我尝试将 dispatch_async(dispatch_get_main_queue()) 放在单元格的 setCellColor 方法内的代码周围,但没有奏效。 @Jason247 没有任何迹象表明您明确使用了除主线程之外的任何线程。 @magma 你是对的。我没有使用其他线程。不确定 Apple 的某些框架是否出于某种原因使用了其他线程。【参考方案3】:

尝试在主线程上调用colorAllCells() 函数。

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) 
            if indexPath.row != 0 

                // Color all of the cells
                dispatch_async(dispatch_get_main_queue(),
                    self.colorAllCells()
                 )

            
        

然而,这会阻止你的 UI。

【讨论】:

没有迹象表明他不是

以上是关于Swift:点击一个单元格时更改所有 UITableViewCells 的文本颜色的主要内容,如果未能解决你的问题,请参考以下文章

点击 tableview 单元格时如何更改 UIImage 颜色?

Swift UI 列表:点击单元格时打开外部 URL

点击/ reloadData不更改单元格时TableView单元格消失?

使用swift 3点击单元格时如何在Avplayer中播放来自url的视频

在选择表格视图单元格时更改按钮的标题

UITableView 选择单元格时所有组件都更改了背景颜色