Den Andreychuk
Den Andreychuk

Reputation: 488

Highlight UITableViewCell like iMessage when long pressed

I have simple chat created using UITableView. I want to add the ability to highlight a message after long press. In fact, I want to create the same feature like iMessage:

After a long press, we unhighlight background (more darker), highlight message, scroll to this message and show the actionSheet

For now I managed to add only longPress and actionSheet

Long press recongizer on viewDidLoad:

let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(onCellLongPressed(gesture:)))
messagesTableView.addGestureRecognizer(longPressRecognizer)

onCellLongPressed function:

@objc func onCellLongPressed(gesture: UILongPressGestureRecognizer) {

    if gesture.state == UIGestureRecognizerState.began {
        let touchPoint = gesture.location(in: self.messagesTableView)
        if let indexPath = messagesTableView.indexPathForRow(at: touchPoint) {
            self.messagesTableView.selectRow(at: indexPath, animated: true, scrollPosition: UITableViewScrollPosition.none)
            shareWithFriend()
        }
    }
}

@objc func shareWithFriend() {
    alert(style: .actionSheet, actions: [
        UIAlertAction(title: "Share with friend", style: .default, handler: { [weak self] (_) in
            print("SHARE HERE")
        }),
        UIAlertAction(title: "Cancel", style: .destructive),
        ])
}

func alert(_ title: String? = nil, message: String? = nil, style: UIAlertController.Style, actions: [UIAlertAction]) {
    let alertController = UIAlertController(title: title, message: message, preferredStyle: style)
    actions.forEach(alertController.addAction)
    present(alertController, animated: true)
}

Upvotes: 1

Views: 958

Answers (2)

GaétanZ
GaétanZ

Reputation: 4930

As you can see, the background color is above the navigation bar so I guess there is a secondary view controller astutely presented above the collection view when the user selects a cell.

I think this is two different view hierarchies which look like one:

  • One view controller contains the balloon list
  • One view controller contains a copy of the selected balloon and the few actions associated to it

Here is the road map :

  1. Detect a long press in the collection view cells
  2. Copy the selected balloon and present it inside a secondary view controller (ActionVC)
  3. Adjust the position of the selected balloon inside the ActionVC. If the selected balloon is under the future action button, it should be moved. If the selected balloon does not bother anyone, it should be presented without any change.
  4. Adjust the content offset of the collection view to reflect 3. The modification should be done alongside 3 to look like if the cell was actually moved.
  5. Detect any touch on the ActionVC
  6. Dismiss the ActionVC

Here is an example project.

enter image description here

  • To copy the balloon, I actually create a view of the same class as the one used in the collection view cell. But you could use a snapshot.
  • To adjust the selected balloon position inside the ActionVC, I used constraints with priorities. One claims "don't be under the action button", another one claims "be exactly where the cell was". I used a simple coordinates conversion to compute the expected selected balloon position inside the ActionVC.
  • To perform 3 alongside 4, I used the transitionCoordinator of the ActionVC, but you could use a simple animation block and present the ActionVC without animation.

Upvotes: 3

DJ-Glock
DJ-Glock

Reputation: 1411

I'm sorry if this answer will not fulfill your request completely, but hope it will help you.

Your initial code was right, but you have not set Scrolling type. So I suggest you use this method selectRow(at:animated:scrollPosition:) Just set scroll position:

self.messagesTableView.selectRow(at: indexPath, animated: true, scrollPosition: UITableViewScrollPosition.top)

This also will select this row and hence call the following method tableView(_:didSelectRowAt:). So you will need to implement highlighting logic here:

 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// your logic of changing color
    }

Then you should implement similar action, but to deselect the row using deselectRow(at:animated:)I believe this should be done when user finished his action. Also you need to implement similar function tableView(_:didDeselectRowAt:) to return color back:

override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
// change color back
}

Update: You may also use the setHighlighted(_:animated:) method to highlight a cell. In this way you may avoid using selectRowAt/didSelectRowAt/didDeselectRowAt and scroll tableView using

tableView?.scrollToRow(at: indexPath, at: UITableViewScrollPosition.top, animated: true).

Upvotes: 0

Related Questions