Today I Learn

Today I Learn (4) : UIButton.AddTarget 데이터 전달 문제 리팩토링

Goniii 2025. 3. 5. 23:59

https://soo-hyn.tistory.com/124

 

Today I Learn(3): 동적으로 뷰 추가 및 삭제

보호되어 있는 글입니다. 내용을 보시려면 비밀번호를 입력하세요.

soo-hyn.tistory.com

 

이전 포스팅에서 사용한 UITapGestureRecognizer를 활용한 데이터 전달 문제를 리팩토링 하려고 한다

 

 

UUID 데이터 전달 문제 리팩토링

ContentView의 내부 프로퍼티 사용 시 문제점 (기존 방식)

  • ContentView 내부 id 값과 UITapGestureRecognizer로 넘겨준 id 값이 같은 뷰를 찾기 위해 contentStackView.arrangedSubviews를 모두 순회해야 한다
  • 뷰의 개수가 증가할수록 성능 저하가 발생할 수 있다
        // contentStackView를 순회하면서 제스처 ID와 같으면 뷰 삭제
        createMemberCardView.contentStackView.arrangedSubviews.forEach{ view in
            if (view as? ContentView)?.id == id {
                view.removeFromSuperview()
            }
        }

 

개선 방법: Dictionary 활용하는 방법

  1. [UUID : ContentView] 형태의 딕셔너리를 생성하여 관리
  2. ContentView 추가 시 UUID를 생성하여 딕셔너리에 저장
    • 제스처를 통해 ID 전달은 동일
  3. 삭제 시 딕셔너리에 해당 UUID의 뷰를 찾아 제거
class CreateMemberCardViewController: UIViewController {
    private let createMemberCardView = CreateMemberCardView()
    private var contentViews: [UUID : ContentView] = [:]
    
    ...
    
    // Add Content 버튼 액션
    @objc private func touchUpInsideAddContentButton() {
        // ContentView 생성
        let contentView = ContentView()
        let id = UUID() // 컨텐츠 ID 생성
        contentViews[id] = contentView // 딕셔너리에 저장
        
        // 삭제 제스처 추가
        let removeButtonTapGesutre = CustomTapGesture(target: self, action: #selector(removeButtonTapGesture(_:)))
        
        // 탭 제스처에 id 값 추가
        removeButtonTapGesutre.id = id
        
        // 삭제 버튼에 삭제 제스처 추가
        contentView.titleView.removeButton.addGestureRecognizer(removeButtonTapGesutre)
        
        // 생성한 View, StackView에 추가
        createMemberCardView.contentStackView.addArrangedSubview(contentView)
        
        // 딜리게이트 설정 (텍스트 뷰)
        contentView.contentsView.textView.delegate = self
        
        // 스크롤뷰 이동
        createMemberCardView.scrollView.scroll(to: .bottom)
    }
    
    // ContentView 삭제 버튼 액션
    @objc private func removeButtonTapGesture(_ gesture: CustomTapGesture) {
        guard let id = gesture.id else { return } // 제스처에 저장된 ID 값 추출
        
        // 제스처의 ID와 동일한 ContentView를 딕셔너리에서 찾아 삭제 (서버 저장 시 순서 보장 X)
        if let removeView = contentViews[id] {
            // 0.4초 동안 view의 투명도를 0으로 만들고, 이후 뷰 삭제
            UIView.animate(withDuration: 0.4, animations: {
                removeView.alpha = 0
            }){ _ in
                removeView.removeFromSuperview()
            }
            
            // 컨텐츠 뷰 딕셔너리에 값 제거
            contentViews.removeValue(forKey: id)
        }
}

 

기존 방식과 개선 방식 비교

비교 항목 기존 방식 (Subview 순회) 개선 방식 (Dictionary 활용)

삭제 방식 arrangedSubviews를 순회하여 UUID 비교 후 제거 UUID로 딕셔너리에서 직접 조회 후 제거
성능 뷰 개수가 많아질수록 성능 저하 딕셔너리 조회는 O(1)로 빠름
코드 가독성 contentStackView 순회 코드가 필요함 UUID 키 기반으로 관리하여 직관적
순서 유지 arrangedSubviews의 순서를 그대로 유지할 수 있어 서버 저장 시 순서가 보장됨 Dictionary는 순서를 보장하지 않으므로 서버 전송 시 순서가 유지되지 않음

 

결론

  • 순서가 중요한 경우 → 기존 방식 유지 (arrangedSubviews 사용)
  • 빠른 조회와 성능이 중요한 경우 → 딕셔너리 활용 ([UUID : ContentView])