[TIL] 시스템 알럿 창에 강조 효과 추가 트러블슈팅

Eden·2025년 1월 27일
0

TIL

목록 보기
105/132
이미지1이미지2이미지2
아이폰 SE아이폰 15 Pro아이폰 16 Pro

문제

alertHighlightView의 centerY를 Safe Area Insets 또는 Navigation Bar의 높이를 기준으로 설정하려 했으나, 특정 디바이스에서 값이 예상대로 동작하지 않았음.

  1. Safe Area Insets의 값이 0으로 반환됨
  2. Navigation Bar 숨김 처리 시 예상치 못한 동작
  3. 디바이스별 상단 영역 차이

원인

앱에서 사용자 권한 설정을 요청하는 UIAlertController를 표시하면서, 사용자에게 권한 요청을 강조하기 위해 alertHighlightView와 화살표(arrowImageView)를 알럿 주변에 배치하려고 했다. 그러나 디바이스와 화면 크기마다 알럿의 위치가 다르기 때문에 정확히 alertHighlightViewcenterY를 계산하는 데 어려움을 겪었다.


시도했던 접근 방법

1. 하드코딩된 오프셋 값 사용

permissionView.alertHighlightView.snp.makeConstraints {
    $0.centerY.equalToSuperview().offset(-100)
}
  • 아이디어: 고정된 값을 사용하여 대략적인 위치를 설정.
  • 문제: 화면 크기가 다른 디바이스에서는 alertHighlightView의 위치가 알럿과 겹치거나, 너무 멀리 떨어지는 문제가 발생.

2. Navigation Bar 기반 계산

permissionView.alertHighlightView.snp.makeConstraints {
    $0.centerY.equalToSuperview().offset((navigationController?.navigationBar.frame.minY ?? 0) / 2)
}
  • 아이디어: Navigation Bar의 높이를 기준으로 알럿의 위치를 계산하여 상대적으로 alertHighlightView를 배치.
  • 문제: Navigation Bar의 높이는 디바이스마다 다르며, 일부 디바이스에서는 Navigation Bar가 없거나 frame.minY 값이 0으로 반환되는 문제가 발생.

3. SafeAreaInsets 기반 계산

permissionView.alertHighlightView.snp.makeConstraints {
    $0.centerY.equalToSuperview().offset(view.safeAreaInsets.top / 2)
}
  • 아이디어: SafeAreaInsets를 활용해 상단 여백을 계산하고 이를 반영하여 위치를 조정.
  • 문제: viewDidAppear 이전에는 safeAreaInsets 값이 0으로 반환되므로, 정확한 위치를 계산할 수 없음.

해결 방법

🌟 UIAlertController의 Frame을 기반으로 동적 위치 계산

UIAlertController가 렌더링된 후 실제 위치와 높이를 가져와 동적으로 alertHighlightView를 배치하는 방법으로 해결할 수 있었다.

이미지1이미지2
아이폰 SE아이폰 16 Pro

  • 핵심 아이디어: 화면의 중앙과 알럿의 중앙 간의 오프셋을 계산하여 alertHighlightView의 위치를 동적으로 조정.
  • 장점: 디바이스 크기와 화면 비율에 관계없이 알럿 위치와 alertHighlightView가 항상 정확히 정렬.

private func showPermissionSystemAlert() {
        let alert = UIAlertController(
            title: "'STAR' 앱이 스크린타임에 접근하려고 함",
            message: """
                    'STAR'에 스크린타임 접근을 허용하면,
                    이 앱이 사용자의 활동 데이터를 보고,
                    콘텐츠를 제한하며, 앱 및 웹사이트의
                    사용을 제한할 수도 있습니다.
                    """,
            preferredStyle: .alert
        )
        
        let denyAction = UIAlertAction(title: "허용 안 함", style: .cancel, handler: { _ in
            self.navigateToStarList()
        })
        alert.addAction(denyAction)
        
        let allowAction = UIAlertAction(title: "허용", style: .default, handler: { _ in
            print("허용")
        })
        alert.addAction(allowAction)
        
        alert.preferredAction = allowAction
        
        /// 다크모드
        if #available(iOS 13.0, *) {
            alert.overrideUserInterfaceStyle = .dark
        }
        
        **present(alert, animated: true) {
            let alertHeight = alert.view.frame.height
            let alertCenterY = alert.view.frame.midY
            self.permissionView.updateAlertHighlightViewY(alertCenterY: alertCenterY, alertHeight: alertHeight)
        }**
    }
func updateAlertHighlightViewY(alertCenterY: CGFloat, alertHeight: CGFloat) {
        let screenHeight = UIScreen.main.bounds.height
        let offset = alertCenterY - (screenHeight / 2)
        
        alertHighlightView.snp.makeConstraints{
            $0.centerY.equalToSuperview().offset(offset)
        }
        
        layoutIfNeeded()
    }

💡 alert.view.frame 값을 반영할 수 있었음

  • present(alert, animated: true) 완료 핸들러 안에서 alert.view.frame 값을 읽으면 UIAlertController가 화면에 렌덜이된 후의 프레임을 가져온다고 함

  • 그래서 시스템 알럿의 실제 높이와 중앙 Y 좌표를 계산할 수 있었음

    문제 전: 알럿 렌더링 되기 전에 값을 읽으려 해서 해결이 안되었던 것 같음

    문제 해결 후: 렌더링 완료 후의 값을 사용해서 정확한 높이와 중앙 Y좌표 값을 가져올 수 있었따.

💡 offset 계산이 정확히 이루어질 수 있었음

  • 화면의 중앙(screenHeight / 2)이랑 시스템 알럿의 중앙(alertCenterY) 간의 차이를 계산해, 그 차이를 offset으로 정의 후 alertHighlightView의 위치에 반영할 수 있었삼.
  • SnapKit의 centerY 제약 조건을 동적으로 업데이트하면서, 알럿과 alertHighlightView의 상대적 위치를 일관되게 유지할 수 있었음.
let offset = alertCenterY - (screenHeight / 2)
  • alertCenterY 시스템 알럿 중앙 좌표
  • screenHeight / 2 화면의 중앙 좌표
  • offset 시스템 알럿 중앙이 화면 중앙에서 얼마나 벗어나 있는지 계산… 첨부터 중앙에 있을 것이지…..

💡 SnapKit 제약 조건 재설정

  • updateAlertHighlightViewY메서드 생성 후 alertHighlightView.snp.makeConstraints를 다시 호출해, 기존 제약 조건에서 업데이트 해줌.
  • 기존 제약 조건
    alertHighlightView.snp.makeConstraints {
                $0.centerX.equalToSuperview()
                $0.width.equalTo(270 + 28)
                $0.height.equalTo(195 + 28)
            }
  • 업데이트
    func updateAlertHighlightViewY(alertCenterY: CGFloat, alertHeight: CGFloat) {
            let screenHeight = UIScreen.main.bounds.height
            let offset = alertCenterY - (screenHeight / 2)
            
            **alertHighlightView.snp.makeConstraints{
                $0.centerY.equalToSuperview().offset(offset)
            }**
            
            layoutIfNeeded()
        }

💡 layoutIfNeeded()로 즉시 적용

  • layoutIfNeeded()를 호출하여, 변경된 제약 조건을 즉시 화면에 반영함
  • 이를 통해 제약 조건 업데이트가 다음 렌더링 주기를 기다리지 않고 즉시 실행되었기 때문에, 사용자는 위치 변경을 즉각적으로 볼 수 있었음
profile
Frontend 🌐 and iOS  🫶🏻

0개의 댓글

관련 채용 정보