8
8
import NMapsMap
9
9
import UIKit
10
10
11
- /// 네이버 지도(NMapsMap)에서 사용할 커스텀 마커 클래스
12
- /// 말풍선 형태의 UI를 제공하며, 좌우 텍스트를 포함할 수 있음
11
+ /// `ESMarker`는 Naver 지도에 마커를 추가하고, 말풍선 형태의 UI를 렌더링하는 클래스입니다.
12
+ /// - `leftText`: 말풍선의 왼쪽 텍스트
13
+ /// - `rightText`: 말풍선의 오른쪽 텍스트
13
14
public final class ESMarker {
14
15
// MARK: - Properties
15
16
16
- /// 네이버 지도( Naver Map)에 표시될 마커 객체
17
+ /// Naver 지도에서 사용되는 마커 객체
17
18
public private( set) var marker : NMFMarker
18
- /// 마커와 연결된 데이터 (예: 식당 정보, 위치 정보 등 )
19
+ /// 마커에 포함될 데이터 (유형이 고정되지 않음 )
19
20
private var data : Any
20
- /// 말풍선의 왼쪽에 표시될 텍스트
21
+ /// 왼쪽 말풍선에 표시될 텍스트
21
22
private var leftText : String
22
- /// 말풍선의 오른쪽에 표시될 텍스트
23
+ /// 오른쪽 말풍선에 표시될 텍스트
23
24
private var rightText : String
24
25
25
- // MARK: - Layout Constants (말풍선 레이아웃 관련 상수)
26
-
26
+ /// UI 배치를 위한 레이아웃 상수
27
27
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 // 왼쪽 말풍선의 추가 패딩
38
33
}
39
34
40
- // MARK: - Initializer (초기화 메서드)
41
-
42
- /// ESMarker 인스턴스를 초기화하는 생성자
35
+ /// `ESMarker` 초기화 메서드
43
36
/// - Parameters:
44
- /// - position: 마커가 지도에 표시될 위치 (위도, 경도 )
45
- /// - data: 마커에 연결할 데이터 (예: POI 정보)
46
- /// - leftText: 왼쪽 말풍선에 표시할 텍스트
47
- /// - rightText: 오른쪽 말풍선에 표시할 텍스트
37
+ /// - position: 마커의 위치 (`NMGLatLng` )
38
+ /// - data: 마커에 포함할 데이터
39
+ /// - leftText: 왼쪽 말풍선 텍스트
40
+ /// - rightText: 오른쪽 말풍선 텍스트
48
41
public init ( position: NMGLatLng , data: Any , leftText: String , rightText: String ) {
49
42
marker = NMFMarker ( )
50
43
marker. position = position
@@ -54,42 +47,42 @@ public final class ESMarker {
54
47
updateImage ( )
55
48
}
56
49
57
- // MARK: - Methods (기능 구현)
58
-
59
- /// 마커의 위치를 변경하는 함수
60
- /// - Parameter newPosition: 새롭게 설정할 위치 (위도, 경도)
50
+ /// 마커의 위치를 업데이트합니다.
51
+ /// - Parameter newPosition: 새로운 위치 (`NMGLatLng`)
61
52
public func updatePosition( newPosition: NMGLatLng ) {
62
53
marker. position = newPosition
63
54
}
64
55
65
- /// 왼쪽 말풍선의 텍스트를 업데이트하는 함수
66
- /// - Parameter newLeftText: 변경할 새로운 왼쪽 텍스트
56
+ /// 왼쪽 말풍선의 텍스트를 변경합니다.
57
+ /// - Parameter newLeftText: 새로운 왼쪽 텍스트
67
58
public func updateLeftText( newLeftText: String ) {
68
59
leftText = newLeftText
69
60
updateImage ( )
70
61
}
71
62
72
- /// 오른쪽 말풍선의 텍스트를 업데이트하는 함수
73
- /// - Parameter newRightText: 변경할 새로운 오른쪽 텍스트
63
+ /// 오른쪽 말풍선의 텍스트를 변경합니다.
64
+ /// - Parameter newRightText: 새로운 오른쪽 텍스트
74
65
public func updateRightText( newRightText: String ) {
75
66
rightText = newRightText
76
67
updateImage ( )
77
68
}
78
69
79
- /// 마커의 이미지를 업데이트하여 현재 텍스트가 반영되도록 설정
70
+ /// 마커의 이미지를 업데이트합니다.
80
71
private func updateImage( ) {
81
72
let image = ESMarker . createESMarkerImage ( leftText: leftText, rightText: rightText)
82
73
marker. iconImage = NMFOverlayImage ( image: image)
83
74
marker. width = image. size. width
84
75
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 ) // 마커의 기준점을 하단 중앙으로 설정
86
77
}
87
78
88
- /// 좌우 텍스트를 포함하는 말풍선 형태의 이미지를 생성
79
+ // MARK: - UI Rendering
80
+
81
+ /// 말풍선 UI를 생성하는 정적 메서드
89
82
/// - Parameters:
90
- /// - leftText: 왼쪽 말풍선의 텍스트
91
- /// - rightText: 오른쪽 말풍선의 텍스트
92
- /// - Returns: 생성된 UIImage 객체
83
+ /// - leftText: 왼쪽 말풍선 텍스트
84
+ /// - rightText: 오른쪽 말풍선 텍스트
85
+ /// - Returns: 생성된 ` UIImage`
93
86
private static func createESMarkerImage( leftText: String , rightText: String ) -> UIImage {
94
87
let font = EATSSUDesignFontFamily . Pretendard. regular. font ( size: 12 )
95
88
@@ -102,80 +95,65 @@ public final class ESMarker {
102
95
. foregroundColor: UIColor . black,
103
96
]
104
97
105
- // 텍스트 크기 계산
106
98
let leftTextSize = ( leftText as NSString ) . size ( withAttributes: leftAttributes)
107
99
let rightTextSize = ( rightText as NSString ) . size ( withAttributes: rightAttributes)
108
100
109
- // 말풍선 크기 계산
110
101
let leftBubbleWidth = leftTextSize. width + Layout. horizontalPadding * 2
111
102
let leftBubbleHeight = leftTextSize. height + Layout. verticalPadding * 2
112
103
let rightBubbleWidth = rightTextSize. width + Layout. horizontalPadding * 2
113
104
let rightBubbleHeight = rightTextSize. height + Layout. verticalPadding * 2
114
105
115
- let totalWidth = Layout . leftBubblePadding
116
- + leftBubbleWidth
117
- + Layout. spacing
118
- + rightBubbleWidth
119
-
106
+ let totalWidth = Layout . leftBubblePadding + leftBubbleWidth + Layout. spacing + rightBubbleWidth
120
107
let bubbleHeight = max ( leftBubbleHeight, rightBubbleHeight)
121
-
122
- // 배경 높이를 말풍선보다 5 크게 설정
123
108
let totalHeight = bubbleHeight + 5
124
109
125
- // 이미지 렌더러 설정
126
110
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
138
112
let bubbleTop = ( totalHeight - bubbleHeight) / 2
139
113
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)
179
126
}
180
127
}
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
+ }
181
159
}
0 commit comments