나의 발자취

iOS Context menu란? (+Haptic Feedback) 본문

프로젝트

iOS Context menu란? (+Haptic Feedback)

달모드 2024. 12. 11. 03:25

 

contextMenu는 iOS에서 3D Touch나 길게 눌렀을 때 나타나는 팝업 메뉴다.

 

사진을 3d 터치로 눌러서 신고하기 버튼을 누른다고 할 때 나타나는 바로 여기다. 내 앱은

  • 이미지를 길게 누르거나 3D Touch하면
  • "신고하기" 옵션이 포함된 메뉴가 팝업으로 나타나고
  •  메뉴에서 "신고하기"를 선택하면 ReportView가 표시되는 구
  • 이다.

 

 

 

문제 상황

현재 ChallengeImagesGrid.swift에서 '신고하기'를 누르면 아래와 같이 바로 접수 화면으로 넘어가야하는데, 로딩이 오래걸리는지 뭔지 흰 화면만 뜨는것이다. 

 
지금 상황은
 

 

  • contextMenu 안에서 showReportView.toggle()을 호출
  • 그리고 별도의 .sheet modifier에서 ReportView를 표
  • 이 구조에서 contextMenu가 닫히기 전에 sheet가 표시되려고 해서 문제가 발생하는 것 같았다.
 
 
 

문제 원인 분석

현재 문제는 이 contextMenu(팝업 메뉴)가 아직 완전히 사라지기 전에 ReportView를 표시하려고 해서 발생하는 것 같다. 그래서 중간 상태 변수(shouldShowReport)를 두어 팝업 메뉴가 완전히 사라진 후에 ReportView를 표시하도록 하는 것이 해결 방법인 것 같았다.

 
 
 
 
 

해결 방법

struct ChallengeImagesGrid: View {
    // ... 기존 프로퍼티들 ...
    @State private var shouldShowReport: Bool = false  // 새로운 상태 변수 추가
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            // ... 기존 코드 ...
            LazyVGrid(...) {
                // ... 기존 코드 ...
                ForEach(challengeImages.indices, id: \.self) { index in
                    AsyncImage(url: URL(string: challengeImages[index].imageUrl)) { phase in
                        switch phase {
                        case .success(let image):
                            image
                                .resizable()
                                .aspectRatio(1, contentMode: .fit)
                                .contextMenu {
                                    Button(role: .destructive, action: {
                                        print("=== 신고하기 버튼 클릭 ===")
                                        print("선택된 이미지 ID:", challengeImages[index].id)
                                        print("이미지 업로더 ID:", challengeImages[index].userId)
                                        selectedImageId = challengeImages[index].id
                                        shouldShowReport = true  // contextMenu가 닫힌 후 실행될 상태 변수
                                    }) {
                                        Label("신고하기", systemImage: "exclamationmark.bubble.fill")
                                    }
                                }
                                .sensoryFeedback(.impact(weight: .medium), trigger: true)
                        // ... 나머지 case 문 ...
                        }
                    }
                }
            }
        }
        .onChange(of: shouldShowReport) { newValue in
            if newValue {
                showReportView = true
                shouldShowReport = false
            }
        }
        .sheet(isPresented: $showReportView) {
            if let imageId = selectedImageId {
                ReportView(targetType: "L", targetId: imageId)
            }
        }
    }
}
 
이렇게 중간 상태 변수를 두고 onChange modifier를 사용하면 contextMenu가 완전히 닫힌 후에 sheet가 표시가 온전히 되었다.
 
 
 
 
 

+ 3D 터치 햅틱

 
원래는 .sensoryFeedback(.impact(weight: .medium), trigger: true)을 썼었는데, 이건 iOS 17 이상에서만 사용 가능하다고, 자꾸 예외 상황도 처리하라고 빌드 에러가 나는 것이다. (3D 터치 절대 포기못해...) 
 
그래서 UIKit의 3D 터치 햅틱을 사용하기로 했다. 상단에 UIKit을 임포트 해주고, UIImpactFeedbackGenerator(style: .medium)
으로 대체해주었다.
let impactGenerator = UIImpactFeedbackGenerator(style: .medium)
impactGenerator.impactOccurred()​
 
추가로, 

햅틱 피드백과 3D Touch/Force Touch의 원은 기기별로 다르다.

 
 

1. 햅틱 피드백 (Haptic Feedback):

  • iPhone 7 이상 
  • SE 1세대는 지원
  • iPad는 미지원
 

2. 3D Touch/Force Touch:

  • iPhone 6S ~ iPhone XS 까지는 3D Touch 지
  • iPhone XR, 11  모델은 Haptic Touch  (길게 누르)
  • iPad는 길게 누르 동

 

 

지만 나는  기기에

1. 3D Touch가 는 기기에서는 3D Touch로

2. 없는 기기에서는 게 누르(Haptic Touch)로

3. 햅틱 피드백은 지원하 기기에

 

동작하도록 구현했다.
 
 
 
 
별거 없고 진짜 이게 끝이다.
import UIKit
let impactGenerator = UIImpactFeedbackGenerator(style: .medium)
impactGenerator.impactOccurred()

 

 

 

오늘 작업 내역

*홈: 백엔드에서 챌린지 기간 다되면 MyOngoingChallenge에서 사라지기
*챌린지 참여 전

- 챌린지 id별로 이미지 보여주기 / 챌린지 사진 꾹 누르면 3d 터치로 신고하기 기능 + 진동(햅틱)
- 홈화면에서 챌린지 랭킹 버그 픽스 

-챌린지 참여(백엔드 카운트) 인원 백엔드 업데이트(아래로 당기면 새로고침?? )

Comments