Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change the title for UITableViewRowAction

Tags:

ios

swift

I have a use case where I need to change the title of the UITableViewRowAction. For example, I have a restaurant cell, and when swipe right I show "bookmark(104)" where "bookmark" is the action and 104 means there have been 104 people bookmarked it. When click on it, I want it to change to "bookmark(105)" because obviously there's a new user(the current user himself) has bookmarked it. How do I do that? Tried the below code and it doesn't work.

let likeAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "bookmark\n\(count)", handler:{(action, indexpath) -> Void in
        ....
        count++
        action.title = "bookmark\n\(count)"
    });
like image 526
Edmond Avatar asked Feb 09 '23 00:02

Edmond


2 Answers

Here's a quick and dirty example.

Say you have a class Restaurant with a name and likes value:

class Restaurant {
    var name: String?
    var likes: Int = 0
}

You initialize a bunch of Restaurant objects, and put them in an array called dataSource. Your table view data source methods will look like this:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.dataSource.count
}


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

    let cell = UITableViewCell(style: .Default, reuseIdentifier: "cell");
    cell.textLabel?.text = dataSource[indexPath.row].name

    return cell
}


// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    // This can be empty if you're not deleting any rows from the table with your edit actions
}

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

    // First, create a share action with the number of likes
    let shareAction = UITableViewRowAction(style: .Default, title: "\(self.dataSource[indexPath.row].likes)") { (action, indexPath) -> Void in

        // In your closure, increment the number of likes for the restaurant, and slide the cell back over
        self.dataSource[indexPath.row].likes++
        self.tableView.setEditing(false, animated: true)
    }

    return [shareAction] // return your array of edit actions for your cell.  In this case, we're only returning one action per row.
}

I'm not going to write a scrollable cell from scratch, since this question has a bunch of options you can use.

I was, however, intrigued by Andrew Carter's attempt to iterate through subviews to access the UIButton in the edit action directly. Here is my attempt:

First, create a reference to the UITableViewCell (or an array of cells), you wish to modify, for this example, I'll be using a single cell:

var cellRef: UITableViewCell?

// ...

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

    let cell = UITableViewCell(style: .Default, reuseIdentifier: "cell");
    cell.textLabel?.text = dataSource[indexPath.row].name

    cellRef = cell;

    return cell
}

In your share action, iterate through the button's subviews. We're looking for the UITableViewCellDeleteConfirmationView and _UITableViewCellActionButton objects (private headers linked for reference).

let shareAction = UITableViewRowAction(style: .Default, title: "\(self.dataSource[indexPath.row].likes)") { (action, indexPath) -> Void in

    var deleteConfirmationView: UIView? // UITableViewCellDeleteConfirmationView

        if let subviews = self.cellRef?.subviews {
            for subview in subviews {
                if NSClassFromString("UITableViewCellDeleteConfirmationView") != nil {

                    if subview.isKindOfClass(NSClassFromString("UITableViewCellDeleteConfirmationView")!) {
                        deleteConfirmationView = subview
                        break
                    }
                }
            }
        }

    if let unwrappedDeleteView = deleteConfirmationView {
        if unwrappedDeleteView.respondsToSelector("_actionButtons") {
            let actionbuttons = unwrappedDeleteView.valueForKey("_actionButtons") as? [AnyObject]
            if let actionButton = actionbuttons?.first as? UIButton { // _UITableViewCellActionButton
                actionButton.setTitle("newText", forState: .Normal)
            }
        }

    }
}
like image 78
JAL Avatar answered Feb 11 '23 00:02

JAL


This answer uses private API and it's NOT recommended to be used on the App Store. Apparently there is no way to change the title of the native UITableViewRowAction. You may have to implement your custom solution as suggested by others to achieve the result you want.

Here I'm traversing the subviews of UITableViewCell which contains private subviews and are subject to change so your code may crash on future iOS releases if Apple changes the view hierarchy. I found the header of UIButtonLabel here.

The current view hierarchy as per iOS 9.2 is

UITableViewCell
    UITableViewCellDeleteConfirmationView
        _UITableViewCellActionButton
            UIButtonLabel
                UIButton

Here is the code:

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
    let shareAction = UITableViewRowAction(style: .Default, title: "\(dataList[indexPath.row].likes)") { (action, indexPath) -> Void in
        self.dataList[indexPath.row].likes++
        let cell = tableView.cellForRowAtIndexPath(indexPath)
        let privateView = cell!.subviews[0].subviews[0].subviews[0]
        let privateButton = privateView.valueForKey("_button") as! UIButton
        privateButton.setTitle("\(self.dataList[indexPath.row].likes)", forState: .Normal)
    }

    return [shareAction]
} 
like image 40
Raphael Oliveira Avatar answered Feb 11 '23 00:02

Raphael Oliveira