Skip to content

Commit e9c46ad

Browse files
committed
[#234] Marker UI 컴포넌트 별 렌더링하는 코드 로직을 함수로 추출
- 마크업 주석으로 설명 추가
1 parent 8208f0c commit e9c46ad

File tree

1 file changed

+77
-99
lines changed
  • EATSSUDesign/EATSSUDesign/Sources/BrandDesignComponents/ESMarker

1 file changed

+77
-99
lines changed

EATSSUDesign/EATSSUDesign/Sources/BrandDesignComponents/ESMarker/ESMarker.swift

+77-99
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,36 @@
88
import NMapsMap
99
import UIKit
1010

11-
/// 네이버 지도(NMapsMap)에서 사용할 커스텀 마커 클래스
12-
/// 말풍선 형태의 UI를 제공하며, 좌우 텍스트를 포함할 수 있음
11+
/// `ESMarker`는 Naver 지도에 마커를 추가하고, 말풍선 형태의 UI를 렌더링하는 클래스입니다.
12+
/// - `leftText`: 말풍선의 왼쪽 텍스트
13+
/// - `rightText`: 말풍선의 오른쪽 텍스트
1314
public final class ESMarker {
1415
// MARK: - Properties
1516

16-
/// 네이버 지도(Naver Map)에 표시될 마커 객체
17+
/// Naver 지도에서 사용되는 마커 객체
1718
public private(set) var marker: NMFMarker
18-
/// 마커와 연결된 데이터 (예: 식당 정보, 위치 정보 등)
19+
/// 마커에 포함될 데이터 (유형이 고정되지 않음)
1920
private var data: Any
20-
/// 말풍선의 왼쪽에 표시될 텍스트
21+
/// 왼쪽 말풍선에 표시될 텍스트
2122
private var leftText: String
22-
/// 말풍선의 오른쪽에 표시될 텍스트
23+
/// 오른쪽 말풍선에 표시될 텍스트
2324
private var rightText: String
2425

25-
// MARK: - Layout Constants (말풍선 레이아웃 관련 상수)
26-
26+
/// UI 배치를 위한 레이아웃 상수
2727
private enum Layout {
28-
/// 말풍선 내부의 좌우 여백 (padding)
29-
static let horizontalPadding: CGFloat = 12
30-
/// 말풍선 내부의 상하 여백 (padding)
31-
static let verticalPadding: CGFloat = 12
32-
/// 왼쪽과 오른쪽 말풍선 사이의 간격
33-
static let spacing: CGFloat = 10
34-
/// 말풍선의 모서리를 둥글게 만드는 반경 (corner radius)
35-
static let cornerRadius: CGFloat = 15
36-
/// 왼쪽 말풍선이 시작되는 위치의 좌측 여백
37-
static let leftBubblePadding: CGFloat = 4
28+
static let horizontalPadding: CGFloat = 12 // 텍스트와 말풍선 가장자리 간격
29+
static let verticalPadding: CGFloat = 12 // 텍스트와 말풍선 위아래 간격
30+
static let spacing: CGFloat = 10 // 왼쪽 말풍선과 오른쪽 말풍선 간격
31+
static let cornerRadius: CGFloat = 15 // 말풍선의 둥근 정도
32+
static let leftBubblePadding: CGFloat = 4 // 왼쪽 말풍선의 추가 패딩
3833
}
3934

40-
// MARK: - Initializer (초기화 메서드)
41-
42-
/// ESMarker 인스턴스를 초기화하는 생성자
35+
/// `ESMarker` 초기화 메서드
4336
/// - Parameters:
44-
/// - position: 마커가 지도에 표시될 위치 (위도, 경도)
45-
/// - data: 마커에 연결할 데이터 (예: POI 정보)
46-
/// - leftText: 왼쪽 말풍선에 표시할 텍스트
47-
/// - rightText: 오른쪽 말풍선에 표시할 텍스트
37+
/// - position: 마커의 위치 (`NMGLatLng`)
38+
/// - data: 마커에 포함할 데이터
39+
/// - leftText: 왼쪽 말풍선 텍스트
40+
/// - rightText: 오른쪽 말풍선 텍스트
4841
public init(position: NMGLatLng, data: Any, leftText: String, rightText: String) {
4942
marker = NMFMarker()
5043
marker.position = position
@@ -54,42 +47,42 @@ public final class ESMarker {
5447
updateImage()
5548
}
5649

57-
// MARK: - Methods (기능 구현)
58-
59-
/// 마커의 위치를 변경하는 함수
60-
/// - Parameter newPosition: 새롭게 설정할 위치 (위도, 경도)
50+
/// 마커의 위치를 업데이트합니다.
51+
/// - Parameter newPosition: 새로운 위치 (`NMGLatLng`)
6152
public func updatePosition(newPosition: NMGLatLng) {
6253
marker.position = newPosition
6354
}
6455

65-
/// 왼쪽 말풍선의 텍스트를 업데이트하는 함수
66-
/// - Parameter newLeftText: 변경할 새로운 왼쪽 텍스트
56+
/// 왼쪽 말풍선의 텍스트를 변경합니다.
57+
/// - Parameter newLeftText: 새로운 왼쪽 텍스트
6758
public func updateLeftText(newLeftText: String) {
6859
leftText = newLeftText
6960
updateImage()
7061
}
7162

72-
/// 오른쪽 말풍선의 텍스트를 업데이트하는 함수
73-
/// - Parameter newRightText: 변경할 새로운 오른쪽 텍스트
63+
/// 오른쪽 말풍선의 텍스트를 변경합니다.
64+
/// - Parameter newRightText: 새로운 오른쪽 텍스트
7465
public func updateRightText(newRightText: String) {
7566
rightText = newRightText
7667
updateImage()
7768
}
7869

79-
/// 마커의 이미지를 업데이트하여 현재 텍스트가 반영되도록 설정
70+
/// 마커의 이미지를 업데이트합니다.
8071
private func updateImage() {
8172
let image = ESMarker.createESMarkerImage(leftText: leftText, rightText: rightText)
8273
marker.iconImage = NMFOverlayImage(image: image)
8374
marker.width = image.size.width
8475
marker.height = image.size.height
85-
marker.anchor = CGPoint(x: 0.5, y: 1.0) // 마커의 하단 중심을 지도 좌표에 맞춰 배치
76+
marker.anchor = CGPoint(x: 0.5, y: 1.0) // 마커의 기준점을 하단 중앙으로 설정
8677
}
8778

88-
/// 좌우 텍스트를 포함하는 말풍선 형태의 이미지를 생성
79+
// MARK: - UI Rendering
80+
81+
/// 말풍선 UI를 생성하는 정적 메서드
8982
/// - Parameters:
90-
/// - leftText: 왼쪽 말풍선의 텍스트
91-
/// - rightText: 오른쪽 말풍선의 텍스트
92-
/// - Returns: 생성된 UIImage 객체
83+
/// - leftText: 왼쪽 말풍선 텍스트
84+
/// - rightText: 오른쪽 말풍선 텍스트
85+
/// - Returns: 생성된 `UIImage`
9386
private static func createESMarkerImage(leftText: String, rightText: String) -> UIImage {
9487
let font = EATSSUDesignFontFamily.Pretendard.regular.font(size: 12)
9588

@@ -102,80 +95,65 @@ public final class ESMarker {
10295
.foregroundColor: UIColor.black,
10396
]
10497

105-
// 텍스트 크기 계산
10698
let leftTextSize = (leftText as NSString).size(withAttributes: leftAttributes)
10799
let rightTextSize = (rightText as NSString).size(withAttributes: rightAttributes)
108100

109-
// 말풍선 크기 계산
110101
let leftBubbleWidth = leftTextSize.width + Layout.horizontalPadding * 2
111102
let leftBubbleHeight = leftTextSize.height + Layout.verticalPadding * 2
112103
let rightBubbleWidth = rightTextSize.width + Layout.horizontalPadding * 2
113104
let rightBubbleHeight = rightTextSize.height + Layout.verticalPadding * 2
114105

115-
let totalWidth = Layout.leftBubblePadding
116-
+ leftBubbleWidth
117-
+ Layout.spacing
118-
+ rightBubbleWidth
119-
106+
let totalWidth = Layout.leftBubblePadding + leftBubbleWidth + Layout.spacing + rightBubbleWidth
120107
let bubbleHeight = max(leftBubbleHeight, rightBubbleHeight)
121-
122-
// 배경 높이를 말풍선보다 5 크게 설정
123108
let totalHeight = bubbleHeight + 5
124109

125-
// 이미지 렌더러 설정
126110
let renderer = UIGraphicsImageRenderer(size: CGSize(width: totalWidth, height: totalHeight))
127-
return renderer.image { _ in
128-
// 배경 (흰색 사각형)
129-
let backgroundRect = CGRect(x: 0, y: 0, width: totalWidth, height: totalHeight)
130-
let backgroundPath = UIBezierPath(
131-
roundedRect: backgroundRect,
132-
cornerRadius: Layout.cornerRadius
133-
)
134-
UIColor.white.setFill()
135-
backgroundPath.fill()
136-
137-
// 말풍선이 배경의 중앙에 오도록 Y축 위치 조정
111+
return renderer.image { context in
138112
let bubbleTop = (totalHeight - bubbleHeight) / 2
139113

140-
// 왼쪽 말풍선 (연한 민트색 배경)
141-
let leftBubbleRect = CGRect(
142-
x: Layout.leftBubblePadding,
143-
y: bubbleTop,
144-
width: leftBubbleWidth,
145-
height: bubbleHeight
146-
)
147-
let leftBubblePath = UIBezierPath(roundedRect: leftBubbleRect, cornerRadius: Layout.cornerRadius)
148-
UIColor.systemMint.withAlphaComponent(0.2).setFill()
149-
leftBubblePath.fill()
150-
151-
// 왼쪽 텍스트 그리기
152-
(leftText as NSString).draw(
153-
at: CGPoint(
154-
x: Layout.leftBubblePadding + Layout.horizontalPadding,
155-
y: bubbleTop + (bubbleHeight - leftTextSize.height) / 2
156-
),
157-
withAttributes: leftAttributes
158-
)
159-
160-
// 오른쪽 말풍선 (투명한 배경)
161-
let rightBubbleRect = CGRect(
162-
x: Layout.leftBubblePadding + leftBubbleWidth + Layout.spacing,
163-
y: bubbleTop,
164-
width: rightBubbleWidth,
165-
height: bubbleHeight
166-
)
167-
let rightBubblePath = UIBezierPath(roundedRect: rightBubbleRect, cornerRadius: Layout.cornerRadius)
168-
UIColor.clear.setFill()
169-
rightBubblePath.fill()
170-
171-
// 오른쪽 텍스트 그리기
172-
(rightText as NSString).draw(
173-
at: CGPoint(
174-
x: Layout.leftBubblePadding + leftBubbleWidth + Layout.spacing + Layout.horizontalPadding,
175-
y: bubbleTop + (bubbleHeight - rightTextSize.height) / 2
176-
),
177-
withAttributes: rightAttributes
178-
)
114+
// 배경 그리기
115+
drawBackground(in: context, width: totalWidth, height: totalHeight)
116+
117+
// 왼쪽 말풍선 그리기
118+
drawLeftBubble(in: context, x: Layout.leftBubblePadding, y: bubbleTop, width: leftBubbleWidth, height: bubbleHeight)
119+
120+
// 오른쪽 말풍선 그리기
121+
drawRightBubble(in: context, x: Layout.leftBubblePadding + leftBubbleWidth + Layout.spacing, y: bubbleTop, width: rightBubbleWidth, height: bubbleHeight)
122+
123+
// 텍스트 그리기
124+
drawText(in: context, text: leftText, at: CGPoint(x: Layout.leftBubblePadding + Layout.horizontalPadding, y: bubbleTop + (bubbleHeight - leftTextSize.height) / 2), attributes: leftAttributes)
125+
drawText(in: context, text: rightText, at: CGPoint(x: Layout.leftBubblePadding + leftBubbleWidth + Layout.spacing + Layout.horizontalPadding, y: bubbleTop + (bubbleHeight - rightTextSize.height) / 2), attributes: rightAttributes)
179126
}
180127
}
128+
129+
// MARK: - Component Rendering Methods
130+
131+
/// 배경을 렌더링합니다.
132+
private static func drawBackground(in _: UIGraphicsImageRendererContext, width: CGFloat, height: CGFloat) {
133+
let backgroundRect = CGRect(x: 0, y: 0, width: width, height: height)
134+
let backgroundPath = UIBezierPath(roundedRect: backgroundRect, cornerRadius: Layout.cornerRadius)
135+
UIColor.white.setFill()
136+
backgroundPath.fill()
137+
}
138+
139+
/// 왼쪽 말풍선을 렌더링합니다.
140+
private static func drawLeftBubble(in _: UIGraphicsImageRendererContext, x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) {
141+
let leftBubbleRect = CGRect(x: x, y: y, width: width, height: height)
142+
let leftBubblePath = UIBezierPath(roundedRect: leftBubbleRect, cornerRadius: Layout.cornerRadius)
143+
UIColor.systemMint.withAlphaComponent(0.2).setFill()
144+
leftBubblePath.fill()
145+
}
146+
147+
/// 오른쪽 말풍선을 렌더링합니다.
148+
private static func drawRightBubble(in _: UIGraphicsImageRendererContext, x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) {
149+
let rightBubbleRect = CGRect(x: x, y: y, width: width, height: height)
150+
let rightBubblePath = UIBezierPath(roundedRect: rightBubbleRect, cornerRadius: Layout.cornerRadius)
151+
UIColor.clear.setFill()
152+
rightBubblePath.fill()
153+
}
154+
155+
/// 텍스트를 렌더링합니다.
156+
private static func drawText(in _: UIGraphicsImageRendererContext, text: String, at point: CGPoint, attributes: [NSAttributedString.Key: Any]) {
157+
(text as NSString).draw(at: point, withAttributes: attributes)
158+
}
181159
}

0 commit comments

Comments
 (0)