[UIKit] Long Press ContextMenu 띄우기 (TableView & CollectionView)

728x90

TableView, CollectionView 에서 Delegate 사용

iOS 13+ 부터 가능함!

TableView or CollectionView는 따로 만들었다는 가정하에 Delegate를 사용하는 법만 확인

 

UITableViewDelegate or UICollectionViewDelegate에 있는 contextMenuConfigurationForItemsAt 함수를 정의해 만들 수 있다.

 

예제는 CollectionView지만 TableView도 동일하게 사용이 가능함!

extension HomeViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], point: CGPoint) -> UIContextMenuConfiguration? {
// 현재 섹션이 어떤건지 확인 (1번 섹션은 Add 메뉴만 뜨고, 2번 섹션은 Delete 메뉴만 뜨게 하기 위해)
        guard let indexPathSection = indexPaths.first?.section else { return nil }
        let section = HomeSection.allCases[indexPathSection]

        // iOS 13+ context menu configuration
        return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
            let addAction = UIAction(title: "즐겨찾기 추가", image: UIImage(systemName: "star")) { [weak self] action in
                self?.viewModel.addIndexPath.send(indexPaths.first)
            }
            let deleteAction = UIAction(title: "즐겨찾기 제거", image: UIImage(systemName: "trash"), attributes: .destructive) { [weak self] action in
                self?.viewModel.deleteIndexPath.send(indexPaths.first)
            }
            return UIMenu(
                title: "",
                children: section == .common ? [addAction] : [deleteAction]
            )
        }

    }
}

UIContextMenuConfiguration에 사용할 메뉴들을 정의해서 반환해주면 된다.

 

내가 사용한 방법은 하나만 꾹 선택하는 방식이지만, multiSelect하고 나오는 ContextMenu에 대한 처리는 IndexPath가 배열로 들어오기 때문에 이 예제와 비슷하게 해도 문제 없을 것 같다. (해봐야 알겠지만,,,)

 

UIContextMenuConfiguration 에는 3가지의 파라미터가 있다.

  1. identifier
    ContextMenu의 ID.
    nil을 넣어주면 UUID 값을 자동으로 생성해줌
  2. previewProvider
    꾹 눌렀을 때 highlight? 되는? preview를 넣어주면 된다. nil로 하면 그냥 선택된 셀을 preview로 보여준다.
    만약 ViewController 를 주입해서 사용할 경우 커스텀도 가능하다.
  3. actionProvider
    여기에 메뉴에 들어갈 액션을 만들어서 반환해주면 된다.

 

previewProvider를 사용하는법을 간략히 보고가자

...
return UIContextMenuConfiguration(identifier: nil, previewProvider: PreviewViewController(imageURL) { suggestedActions in
            ...
        }
...

final class PreviewViewController: UIViewController {

    private let imageThumbnail: String
    private let imageView: UIImageView = {
        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()

    init(_ imageURL: String) {
        self.imageThumbnail = thumbnailURL
        super.init(nibName: nil, bundle: nil)
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
    }

    private func setupView() {
        view = imageView
        view.backgroundColor = .white
        imageView.load(url: URL(string: imageThumbnail))
        preferredContentSize = imageView.frame.size
    }
}

임시로 아무 예제를 들고왔는데,

먼저 Preview로 사용할 ViewController를 생성해주고 파라미터에 넣어주면 된다.

Preview에 보여질 UI를 만들고 정의해주면 끝!!

 

Preview 크기의 경우 위 코드에서는 preferredContentSize = imageView.frame.size 를 사용해서 이미지 크기에 맞게 조절한걸 알 수 있다.

 

preferredContentSize? - 크기를 조정할 수 있는 컨트롤러에서 많이 사용됨. 원하는 크기를 지정하는 속성 (UIPopoverPresentationController, UISheetPresentationController 등에서도 많이 사용됨)

728x90