Search code examples
iosuitableviewnsnotificationcenteruitableviewrowaction

Edit actions in a table view similar to ios notifications center


I need to develop a tableview with edit actions on rows that look similar to the ones on iOS Notification center.
The resources I have used so far, say that I can only edit the background color and width of edit actions. What I want is the custom look like that of the notifications in iOS Notifications Center.

See the annotated portion. This is what I want.

See the annotated portion

This is what I have managed so far :|

This is what I have managed so far :|

Any help / guidance would be greatly appreciated!
Thanks in advance!


Solution

  • I ended up creating my own views as a part of the table view cell and adding custom animation..

    Sample Code:
    In my tableViewController:

    override func viewDidLoad() {
        super.viewDidLoad()
        ....
    
        let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(handleTap))
        tableView.addGestureRecognizer(tapGesture)
    
        let leftSwipeGesture = UISwipeGestureRecognizer.init(target: self, action: #selector(handleLeftSwipe(_:)))
        leftSwipeGesture.direction = .left
        tableView.addGestureRecognizer(leftSwipeGesture)
    
        let rightSwipeGesture = UISwipeGestureRecognizer.init(target: self, action: #selector(handleRightSwipe(_:)))
        rightSwipeGesture.direction = .right
        tableView.addGestureRecognizer(rightSwipeGesture)
    }
    
    func handleTap(_ gestureRecognizer: UISwipeGestureRecognizer) {
        let point = gestureRecognizer.location(in: self.tableView)
        if let indexPath = self.tableView.indexPathForRow(at: point) {
            var shouldSelectRow = false
            if indexPathBeingEdited != nil {
                if indexPath == indexPathBeingEdited {
                    shouldSelectRow = true
                } else {
                    //handle close of the cell being edited already
                    if let previousEditedCell = tableView.cellForRow(at: indexPathBeingEdited!) as? NotificationCenterTableViewCell {
                        previousEditedCell.closeEditActions()
                        indexPathBeingEdited = nil
                    }
                }
            } else {
                shouldSelectRow = true
            }
            if shouldSelectRow {
                tableView.selectRow(at: indexPath, animated: true, scrollPosition: .middle)
                tableView(tableView, didSelectRowAt: indexPath)
            }
        }
    }
    
    func handleLeftSwipe(_ gestureRecognizer: UISwipeGestureRecognizer) {
        let point = gestureRecognizer.location(in: self.tableView)
        if let indexPath = self.tableView.indexPathForRow(at: point) {
            if indexPathBeingEdited != nil {
                if indexPath == indexPathBeingEdited {
                    //Do nothing
                } else {
                    //handle close of the cell being edited already
                    if let previousEditedCell = tableView.cellForRow(at: indexPathBeingEdited!) as? NotificationCenterTableViewCell {
                        previousEditedCell.closeEditActions()
                        indexPathBeingEdited = nil
                    }
                }
            }
            //proceed with left swipe action
            if let cell = tableView.cellForRow(at: indexPath) as? NotificationCenterTableViewCell {
                cell.handleLeftSwipe(gestureRecognizer)
                let notification = notificationsArray[indexPath.section].notificationItems[indexPath.row]
    
                //Update the title of Read button
                if notification.isNotificationRead {
                    cell.readUnreadButtonLabel.text = "Unread"
                } else {
                    cell.readUnreadButtonLabel.text = "Read"
                }
                indexPathBeingEdited = indexPath
            }
        }
    }
    
    func handleRightSwipe(_ gestureRecognizer: UISwipeGestureRecognizer) {
        let point = gestureRecognizer.location(in: self.tableView)
        if let indexPath = self.tableView.indexPathForRow(at: point) {
            if indexPathBeingEdited != nil {
                if indexPath == indexPathBeingEdited {
                    if let cell = tableView.cellForRow(at: indexPath) as? NotificationCenterTableViewCell {
                        cell.closeEditActions()
                        indexPathBeingEdited = nil
                        //Update the title of Read button
                        cell.readUnreadButtonLabel.text = "Read"
                    }
                } else {
                    //handle close of the cell being edited already
                    if let previousEditedCell = tableView.cellForRow(at: indexPathBeingEdited!) as? NotificationCenterTableViewCell {
                        previousEditedCell.closeEditActions()
                        indexPathBeingEdited = nil
                        //Update the title of Read button
                        previousEditedCell.readUnreadButtonLabel.text = "Read"
                    }
                }
            }
        }
    }
    

    In my table view cell:

        func handleLeftSwipe(_ gestureRecognizer: UISwipeGestureRecognizer) {
        if !isBeingEdited {
            //Action to open the edit buttons
            UIView.animate(withDuration: 0.5, animations: {
                self.notificationHolderViewLeadingConstraint.constant -= 248
                self.notificationHolderViewTrailingConstraint.constant -= 248
    
                self.editActionsView.isHidden = false
                self.layoutIfNeeded()
            }, completion: { (success) in
    
            })
            isBeingEdited = true
        }
    }
    
    func closeEditActions() {
        if isBeingEdited {
            //Action to open the edit buttons
            UIView.animate(withDuration: 0.5, animations: {
                self.notificationHolderViewLeadingConstraint.constant += 248
                self.notificationHolderViewTrailingConstraint.constant += 248
    
                self.editActionsView.isHidden = true
                self.layoutIfNeeded()
            }, completion: { (success) in
    
            })
            isBeingEdited = false
        }
    }   
    
    
     override func draw(_ rect: CGRect) {
        if let viewToRound = editActionsView {
            let path = UIBezierPath(roundedRect:viewToRound.bounds,
                                    byRoundingCorners:[.topRight, .topLeft, .bottomRight, .bottomLeft],
                                    cornerRadii: CGSize(width: 20, height:  20))
    
            let maskLayer = CAShapeLayer()
    
            maskLayer.path = path.cgPath
            viewToRound.layer.mask = maskLayer
        }
    }    
    

    Just FYI, I have my edit buttons i.e., Read / View / Clear added to the editActionsView. The corresponding actions are hooked up with IBActions in my tableViewCell class.

    The outcome is this:

    Final look