PROJECT

SpeechBubble

개인 라이브러리 · UIKit 말풍선 뷰 · 오픈소스(MIT)

2024.04 · v0.1.0–0.1.4 배포

iOS 13+ · SPM · CocoaPods

말풍선 드로잉을 부위별로 나누고, 사용할 속성을 API로 정리해 배포했습니다.

설계 포인트

무엇을 결정했나

Case 01 · 드로잉

본체와 꼬리를 나눠 그린다

문제

말풍선은 본체(둥근 사각형)와 꼬리(삼각형)의 테두리가 한 획처럼 이어져 보여야 합니다. 점선 스타일까지 지원하려면 layer 테두리만으로는 부족합니다.

접근

본체는 CAShapeLayer로 둥근 사각형과 점선을 그리고, 꼬리는 draw(_:)에서 UIBezierPath로 삼각형을 그렸습니다. 두 뷰는 테두리가 끊겨 보이지 않도록 borderWidth만큼 겹쳐 배치했습니다.

핵심 결정

꼬리를 4방향(top/bottom/left/right) 어디든, 변의 임의 비율 위치에 붙일 수 있습니다. positionRatio로 기준점을 잡고 arrowSize만큼 벌려 그립니다.

ArrowView.swift
// 모서리 길이 x 비율로 기준점
case .top:
    pointTip   = CGPoint(x: position, y: rect.height - arrowSize)
    pointLeft  = CGPoint(x: position - arrowSize, y: rect.height)
    pointRight = CGPoint(x: position + arrowSize, y: rect.height)

let path = UIBezierPath()
path.move(to: arrowPoints.left)
path.addLine(to: arrowPoints.tip)
path.addLine(to: arrowPoints.right)
if style.contains(.fill) { filledColor.setFill(); path.fill() }

Case 02 · API

조합과 위치를 타입으로 구분한다

문제

스타일(채움, 실선, 점선)은 서로 조합되고, 꼬리 위치(4방향)는 배타적입니다. 잘못된 조합을 아예 쓸 수 없도록 타입으로 막아야 합니다.

접근

함께 사용할 수 있는 스타일은 OptionSet(.fill / .line / .dottedLine)으로, 하나만 선택해야 하는 꼬리 위치는 enum(top/bottom/left/right)으로 구분했습니다.

핵심 결정

생성에 필요한 값은 text, style, arrowPosition 세 가지로 제한하고, 색상, 두께, 여백에는 기본값을 뒀습니다. 필요한 항목만 바꿔 사용할 수 있습니다.

SpeechBubbleStyleOptions.swift
public struct SpeechBubbleStyleOptions: OptionSet {
    public static let fill       = Self(rawValue: 1 << 0)
    public static let line       = Self(rawValue: 1 << 1)
    public static let dottedLine = Self(rawValue: 1 << 2)
}

// 사용: 채움 + 점선 테두리
SpeechBubbleView(text: "안녕하세요",
                 style: [.fill, .dottedLine],
                 arrowPosition: .top)

회고 · 개선 로드맵

배포 후 남은 개선점

배포 당시

2024년 배포 당시 테스트는 템플릿 스텁에 머물렀고, 접근성 라벨과 SwiftUI 지원을 넣지 못했습니다.

개선 방향

드로잉 스냅샷 테스트, SwiftUI 래퍼, 접근성 라벨을 추가하고 다크 모드 전환 시 테두리 색상이 갱신되도록 보완할 수 있습니다.

개선 로드맵
// 1. UIViewRepresentable 래퍼 → SwiftUI 지원
// 2. 합성 뷰 레벨 accessibilityLabel
// 3. traitCollectionDidChange → 테두리 재드로우
// 4. 스텁 테스트 → 드로잉 스냅샷 테스트
// 5. (교훈) 공개 API 네이밍은 배포 전에 확정

기록

SPM과 CocoaPods로 배포했습니다

v0.1.4

SPM · CocoaPods 이중 배포

UIKit 기반으로 구현해 MIT 라이선스로 공개했습니다.