스와이프로 셀 삭제하기

hyun·2025년 9월 19일
0

iOS

목록 보기
54/54

1. UIPanGestureRecognizer 설정

private func setupPanGesture() {
    panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
    panGesture.delegate = self
    addGestureRecognizer(panGesture)
}

2. 스와이프 액션 UI 구성

  • 빨간색 배경: 삭제 액션을 나타내는 배경 뷰
  • 삭제 아이콘: 휴지통 아이콘으로 삭제 의도를 명확히 표시
  • 버튼 영역: 실제 삭제 동작을 처리하는 투명 버튼
private let swipeActionView = UIView().then {
    $0.backgroundColor = .error
    $0.layer.cornerRadius = Metrics.cornerRadius
    $0.isHidden = true  // 기본적으로 숨김
}

private let deleteIconView = UIImageView().then {
    let image = UIImage(named: "trash")?.withRenderingMode(.alwaysTemplate)
    $0.image = image
    $0.tintColor = .appWhite
    $0.isHidden = true
}

3. 스와이프 제스처 처리 로직

제스처 상태별 처리

@objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
    let translation = gesture.translation(in: self)
    let velocity = gesture.velocity(in: self)

    switch gesture.state {
    case .began:
        originalTransform = mainContentView.transform
        
    case .changed:
        // 왼쪽으로만 스와이프 허용
        guard translation.x <= 0 else { return }
        
        // 최대 이동 거리 제한 (안전구역 고려)
        let leftInset = frame.minX
        let safeLeft = window?.safeAreaInsets.left ?? 0
        let maxTranslation: CGFloat = -(80 + leftInset + safeLeft)
        let clampedTranslation = max(translation.x, maxTranslation)
        mainContentView.transform = CGAffineTransform(translationX: clampedTranslation, y: 0)
        
        // 스와이프 액션 표시 조건
        let shouldShowAction = translation.x < -25
        if shouldShowAction != isSwipeActionVisible {
            isSwipeActionVisible = shouldShowAction
            swipeActionView.isHidden = !shouldShowAction
            deleteIconView.isHidden = !shouldShowAction
        }
        
    case .ended, .cancelled:
        // 스와이프 거리나 속도에 따라 액션 결정
        if translation.x < -40 || velocity.x < -400 {
            showSwipeAction()  // 삭제 액션 표시
        } else {
            resetSwipeAction()  // 원래 위치로 복원
        }
    }
}

4. 애니메이션 처리

삭제 액션 표시

private func showSwipeAction() {
    UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.85, initialSpringVelocity: 0.6) {
        let leftInset = self.frame.minX
        let safeLeft = self.window?.safeAreaInsets.left ?? 0
        self.mainContentView.transform = CGAffineTransform(translationX: -(80 + leftInset + safeLeft), y: 0)
    }
    swipeActionView.isHidden = false
    deleteIconView.isHidden = false
}

원래 위치로 복원

private func resetSwipeAction() {
    UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.85, initialSpringVelocity: 0.6) {
        self.mainContentView.transform = .identity
    }
    deleteIconView.isHidden = true
    isSwipeActionVisible = false
    swipeActionView.isHidden = true
}

5. 삭제 확인 및 실행

셀에서 뷰컨트롤러로 이벤트 전달

// 셀에서 콜백 설정
cell.onDeleteTapped = { [weak self] in
    guard let self = self else { return }
    let timer = self.timers[indexPath.item]
    self.showDeleteConfirmation(for: timer, at: indexPath)
}

삭제 확인 다이얼로그

private func showDeleteConfirmation(for timer: TimerModel, at indexPath: IndexPath) {
    let presenter = navigationController ?? self
    PodoAlertController.presentDeleteTimerAlert(
        from: presenter,
        title: "이 타이머를 삭제할까요?",
        message: "삭제한 타이머는 복구할 수 없어요.",
        cancelTitle: "취소",
        confirmTitle: "삭제하기"
    ) { [weak self] in
        self?.deleteTimer(timer, at: indexPath)
    }
}

실제 삭제 실행

private func deleteTimer(_ timer: TimerModel, at indexPath: IndexPath) {
    do {
        try repository.delete(id: timer.timerID)
        // 데이터 삭제 후 UI 업데이트
        timers.remove(at: indexPath.item)
        collectionView.deleteItems(at: [indexPath])
        updateUI()
        updateHeaderTotalFocusTime()
    } catch {
        // 에러 처리
        PodoAlertController.presentErrorAlert(
            from: presenter,
            title: "삭제 실패",
            message: "타이머 삭제 중 오류 발생"
        )
    }
}

0개의 댓글