【问题标题】:Refactor cellForRowIndexPath in UITableView Swift在 UITableView Swift 中重构 cellForRowIndexPath
【发布时间】:2023-03-30 08:36:01
【问题描述】:

我有一个相当长的cellForRowAtIndexPath 函数。我使用 parse 作为我的后端并且有很多事情要做。我想提取很多这些条件并将它们放在自己的函数中。尤其是 PFUser 查询,但不幸的是,我不知道最好的方法是什么,因为我不知道如何访问我想要编写的那些函数中每个单元格的元素。

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

    let cell = tableView.dequeueReusableCellWithIdentifier("PostCells", forIndexPath: indexPath) as! NewsFeedTableCellTableViewCell

    // Configure the cell...

    // A drive is a post
    let drive: PFObject = self.timelineData[indexPath.row] as PFObject
    var driverId = drive.objectForKey("driver")!.objectId!
    var currentUserObjectId = PFUser.currentUser()!.objectId
    if(driverId != currentUserObjectId){
        cell.requestButton.layer.borderWidth = 1
        cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
        cell.requestButton.tintColor = UIColor.orangeColor()
        cell.requestButton.layer.borderColor = UIColor.orangeColor().CGColor
        cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)

    }
    else {

        cell.requestButton.layer.borderWidth = 1
        cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
        cell.requestButton.tintColor = UIColor.grayColor()
        cell.requestButton.layer.borderColor = UIColor.lightGrayColor().CGColor
        cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
        cell.requestButton.enabled = false

    }


    // Setting up the attributes of the cell for the news feed
    cell.driveTitleTextField.text = drive.objectForKey("title") as! String
    cell.wayTextField.text = drive.objectForKey("way") as! String

    var departureDate = NSDate()
    departureDate = drive.objectForKey("departureDate") as! NSDate
    var dateFormat = NSDateFormatter()
    dateFormat.dateFormat = "M/dd hh:mm a"
    cell.departureDateTextField.text = dateFormat.stringFromDate(departureDate)

    if((drive.objectForKey("way")!.isEqualToString("Two Way")))
    {
        var returnDate = NSDate()
        returnDate = drive.objectForKey("returnDate") as! NSDate
        cell.returningDateTextField.text = dateFormat.stringFromDate(returnDate)
    }
    else if((drive.objectForKey("way")!.isEqualToString("One Way")))
    {
        cell.returningDateTextField.enabled = false
        cell.returningDateTextField.userInteractionEnabled = false
        cell.returningDateTextField.hidden = true
        cell.returningLabel.hidden = true
    }

    var seatNumber = NSNumber()
    seatNumber = drive.objectForKey("seatNumber") as! NSInteger
    var numberFormat = NSNumberFormatter()
    numberFormat.stringFromNumber(seatNumber)
    cell.seatNumberTextField.text = numberFormat.stringFromNumber(seatNumber)


    // this is a PFUser query so we can get the users image and name and email from the User class 
    var findDrive = PFUser.query()
    var objectId: AnyObject? = drive.objectForKey("driver")!.objectId!
    findDrive?.whereKey("objectId", equalTo: objectId!)
    findDrive?.findObjectsInBackgroundWithBlock{
        (objects:[AnyObject]?, error:NSError?)->Void in
        if (error == nil){
            if let actualObjects = objects {
                let possibleUser = (actualObjects as NSArray).lastObject as? PFUser
                if let user = possibleUser {
                    cell.userProfileNameLabel.text = user["fullName"] as? String
                    cell.userEmailLabel.text = user["username"] as? String

                    //Profile Image
                    cell.profileImage.alpha = 0
                    if let profileImage = user["profilePicture"] as? PFFile {
                        profileImage.getDataInBackgroundWithBlock{
                            (imageData:NSData? , error:NSError?)-> Void in

                            if(error == nil) {
                                if imageData != nil{
                                    let image:UIImage = UIImage (data: imageData!)!
                                    cell.profileImage.image = image
                                }
                            }
                        }

                    }

                    UIView.animateWithDuration(0.5, animations: {
                        cell.driveTitleTextField.alpha = 1
                        cell.wayTextField.alpha = 1
                        cell.profileImage.alpha = 1
                        cell.userProfileNameLabel.alpha = 1
                        cell.userEmailLabel.alpha = 1
                        cell.seatNumberTextField.alpha = 1
                        cell.returningDateTextField.alpha = 1
                        cell.departureDateTextField.alpha = 1

                    })

                }
            }

        }
    }
    return cell
}

编辑 1

我想出了一种重构我的代码的方法,我希望得到批评!

1.我提取了很多单元格配置并将它们放入函数中,一个用于单元格上的按钮,另一个用于解析的所有数据。

func configureDataTableViewCell(cell:NewsFeedTableCellTableViewCell, drive: PFObject)
{
    cell.driveTitleTextField.text = drive.objectForKey("title") as! String
    cell.wayTextField.text = drive.objectForKey("way") as! String
    cell.userEmailLabel.text = drive.objectForKey("username") as? String
    cell.userProfileNameLabel.text = drive.objectForKey("name") as? String

    var departureDate = NSDate()
    departureDate = drive.objectForKey("departureDate") as! NSDate
    var dateFormat = NSDateFormatter()
    dateFormat.dateFormat = "M/dd hh:mm a"
    cell.departureDateTextField.text = dateFormat.stringFromDate(departureDate)

    if((drive.objectForKey("way")!.isEqualToString("Two Way")))
    {
        var returnDate = NSDate()
        returnDate = drive.objectForKey("returnDate") as! NSDate
        cell.returningDateTextField.text = dateFormat.stringFromDate(returnDate)
    }
    else if((drive.objectForKey("way")!.isEqualToString("One Way")))
    {
        cell.returningDateTextField.enabled = false
        cell.returningDateTextField.userInteractionEnabled = false
        cell.returningDateTextField.hidden = true
        cell.returningLabel.hidden = true
    }

    var seatNumber = NSNumber()
    seatNumber = drive.objectForKey("seatNumber") as! NSInteger
    var numberFormat = NSNumberFormatter()
    numberFormat.stringFromNumber(seatNumber)
    cell.seatNumberTextField.text = numberFormat.stringFromNumber(seatNumber)


}

func configureButtonTableViewCell(cell:NewsFeedTableCellTableViewCell, userID: String)
{
    var currentUserObjectId = PFUser.currentUser()!.objectId
    if(userID != currentUserObjectId){
        cell.requestButton.layer.borderWidth = 1
        cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
        cell.requestButton.tintColor = UIColor.orangeColor()
        cell.requestButton.layer.borderColor = UIColor.orangeColor().CGColor
        cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
        println("orange")
    }
    else {

        cell.requestButton.layer.borderWidth = 1
        cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
        cell.requestButton.tintColor = UIColor.grayColor()
        cell.requestButton.layer.borderColor = UIColor.lightGrayColor().CGColor
        cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
        cell.requestButton.enabled = false
        println("gray")
    }

}

2.然后我将第 1 步中的函数传入我的cellForRowIndexPath

    // A drive is a post
    let drive: PFObject = self.timelineData[indexPath.row] as PFObject
    var driverId : String = drive.objectForKey("driver")!.objectId!!
    configureButtonTableViewCell(cell, userID: driverId)
    configureDataTableViewCell(cell, drive: drive)

3. 我在保存时将所有 PFUser 数据存储到我的对象中,而不是查询用户类。因此,当他们保存帖子时,我会得到 PFUser.currentUser() 用户名、全名和个人资料图片。

我的负载数据已被修改。我将所有个人资料图片存储在自己的数组中。

func loadData(){


    var findItemData:PFQuery = PFQuery(className:"Posts")

    findItemData.addDescendingOrder("createdAt")

    findItemData.findObjectsInBackgroundWithBlock{
        (objects:[AnyObject]? , error:NSError?) -> Void in
            if error == nil
            {
                self.timelineData.removeAll(keepCapacity: false)
                self.profilePictures.removeAll(keepCapacity: false)
                self.timelineData = objects as! [PFObject]
                for object in objects! {

                    self.profilePictures.append(object.objectForKey("profilePicture") as! PFFile)


                }

                self.newsFeedTableView.reloadData()


            }

    }
}

最后,这是我更新的cellForRowIndexPath

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

    var cell = tableView.dequeueReusableCellWithIdentifier("PostCells", forIndexPath: indexPath) as! NewsFeedTableCellTableViewCell

    // Configure the cell...

    // A drive is a post
    let drive: PFObject = self.timelineData[indexPath.row] as PFObject
    var driverId : String = drive.objectForKey("driver")!.objectId!!
    configureButtonTableViewCell(cell, userID: driverId)
    configureDataTableViewCell(cell, drive: drive)
    println(PFUser.currentUser()?.objectForKey("username"))


    if let profileImage = drive["profilePicture"] as? PFFile {
        profileImage.getDataInBackgroundWithBlock{
            (imageData:NSData? , error:NSError?)-> Void in

            if(error == nil) {
                if imageData != nil{
                    let image:UIImage = UIImage (data: imageData!)!
                    cell.profileImage.image = image
                }
            }
        }

    }


    return cell
}

让我知道你们的想法,我想让我的代码更具可读性、速度和内存效率。

【问题讨论】:

  • 日期格式化程序可能会很慢,如果您可以将它们移出 cellForRow,或者将它们作为字符串设置在您获取的对象上,那可能是最好的。除此之外,看起来真的很不错。
  • @BeauYoung 谢谢!你能给我一个从那里取出日期格式化程序的例子吗?
  • 你有出发日期对吗?为什么不在 Parse 上使用您需要的格式?如果你不能这样做,我会在 PFObject 上创建一个新密钥,并在你完成获取它们后立即将其添加到那里。把它们扔进一个循环。
  • 这些答案对您有帮助吗?
  • @BeauYoung 我喜欢你提到的内容,并同意我应该在获取它们时对其进行格式化,但不确定到底是在哪里,如果我在查询块中或在查询之后进行操作?另外,您是什么意思“只需在 PFObject 上创建一个新密钥”?谢谢!

标签: ios swift uitableview parse-platform refactoring


【解决方案1】:

您不应该在 cellForRow 中做任何繁重的模型工作。 您当前尝试执行的操作会大大降低您的 UI 速度。

在大多数情况下,您会希望您的模型对象设置好,并在您到达 cellForRow 之前做好准备。

这意味着在 viewDidLoad 等某处执行 Parse 查询,将这些结果保存在一个数组中,并在需要时将它们应用到 cellForRow 中的单元格。这样,当用户滚动时,不会为进入视图的每个新单元格调度新查询。它已经可用了。

除此之外,如果您需要在获取这些项目后对其进行任何更改,您可以这样做,并且即使在用户滚动时也让它们保持不变。

【讨论】:

    【解决方案2】:

    重构,让你有一些数据类型或一组实例变量作为视图模型。避免进行使 cellForRowAtIndexPath 中的单元格发生变异的异步调用。而是让您的数据访问方法发生变异或重新创建视图模型,并在回调结束时将 dispatch_async 发送到主队列。给它一个闭包,告诉你的表视图重新加载数据以及你需要做的任何其他事情来让视图显示新数据。

    这里有一段伪代码来描述我的意思:

    func loadData() {
        parseQueryWithCallback() { data in
            self.viewModel = doWhateverTransformsAreNeeded(data)
            dispatch_async(dispatch_get_main_queue(), self.tableView.reloadData)
        }
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) {
        let cell = dequeue(...)
        cell.thingOne = self.viewModel.things[indexPath.row].thingOne
        cell.thingTwo = self.viewModel.things[indexPath.row].thingTwo
        return cell
    }
    

    【讨论】:

    • 你能给我一些代码吗,你需要更多的问题上下文吗?
    • parseQueryWithCallback() 来自哪里?
    • 这是伪代码,因此您可以将其替换为用于从 Parse 获取数据的任何真实代码。
    • 好的,谢谢!我想开始创建一个模型,对于这种情况,我只需要一个模型来发布帖子,因为这就是单元格试图显示的内容?
    • 是的。如果这就是您所需要的,它可能是一个 Array 。视图模型基本上是您在视图控制器上声明的任何实例变量,它只保存用于填充视图的数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-24
    相关资源
    最近更新 更多