Skip to content

Commit 0d4a533

Browse files
committed
feat: enhance InfoWindow with platform-specific styling support and improved marker integration
1 parent e0b2e8b commit 0d4a533

5 files changed

Lines changed: 120 additions & 34 deletions

File tree

.claude/implementations/infowindow-implementation.md

Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,22 @@ import { NaverMapInfoWindow } from '@mj-studio/react-native-naver-map';
9090
backgroundColor="white"
9191
/>
9292

93-
// 마커에 연결된 InfoWindow (향후 구현)
93+
// 마커에 연결된 InfoWindow
94+
<NaverMapMarkerOverlay
95+
identifier="marker1"
96+
latitude={37.5666102}
97+
longitude={126.9783881}
98+
/>
9499
<NaverMapInfoWindow
100+
identifier="marker1"
95101
latitude={37.5666102}
96102
longitude={126.9783881}
97-
markerTag="marker1"
98-
align="Top"
99103
text="마커 정보"
104+
isOpen={true}
105+
// Android only: 커스텀 스타일
106+
fontWeight="bold"
107+
borderRadius={10}
108+
borderColor="#4263eb"
100109
/>
101110
```
102111

@@ -105,13 +114,75 @@ import { NaverMapInfoWindow } from '@mj-studio/react-native-naver-map';
105114
- [Android InfoWindow 공식 문서](https://navermaps.github.io/android-map-sdk/guide-ko/5-3.html)
106115
- [iOS NMFInfoWindow API](https://navermaps.github.io/maps.js.ncp/docs/naver.maps.InfoWindow.html)
107116

108-
## 향후 개선 사항
117+
## 플랫폼별 스타일 지원 현황
118+
119+
### Android ✅ (완전 지원)
120+
-`text`, `textSize`, `textColor`
121+
-`fontWeight` - Bold/Regular/Medium/Semibold (100-900)
122+
-`backgroundColor`
123+
-`borderRadius` - 둥근 모서리
124+
-`borderWidth`, `borderColor` - 테두리
125+
-`padding` - 내부 여백
126+
- ✅ 마커 연결 (`identifier`)
127+
- ✅ 열림/닫힘 제어 (`isOpen`)
128+
129+
**구현 방식:**
130+
```kotlin
131+
// GradientDrawable로 커스텀 스타일 구현
132+
val drawable = GradientDrawable().apply {
133+
setColor(backgroundColor)
134+
cornerRadius = borderRadius
135+
setStroke(borderWidth.toInt(), borderColor)
136+
}
137+
```
138+
139+
### iOS ⚠️ (텍스트만 지원)
140+
-`text` - 텍스트 내용
141+
- ✅ 마커 연결 (`identifier`)
142+
- ✅ 열림/닫힘 제어 (`isOpen`)
143+
-`textSize`, `textColor` - 무시됨
144+
-`fontWeight`, `borderRadius`, `borderWidth`, `borderColor`, `padding` - 무시됨
145+
146+
**제한 이유:**
147+
iOS의 `NMFInfoWindow`는 기본적으로 `NMFInfoWindowDefaultTextSource`를 사용하며, 이는 말풍선 스타일의 텍스트만 표시합니다.
109148

110-
1. **마커 연결 기능**: `markerTag`를 통한 마커 찾기 및 연결
111-
2. **커스텀 뷰 지원**: React 자식 컴포넌트를 InfoWindow 콘텐츠로 사용
112-
3. **더 많은 스타일 옵션**: 테두리 색상, 화살표 표시 등
113-
4. **애니메이션**: 열기/닫기 애니메이션
114-
5. **이벤트 확장**: `onOpen`, `onTap`, `onClose` 이벤트 등
149+
커스텀 스타일을 위해 `NMFOverlayImageDataSource`를 시도했으나:
150+
- `NMFInfoWindow`가 내부적으로 `toUIImage` 메서드 호출 (존재하지 않음)
151+
- 일반 오버레이와 달리 InfoWindow는 이미지 기반 커스터마이징 미지원
152+
- 구현 시도 시 에러 발생 내용: `-[NMFOverlayImage toUIImage]: unrecognized selector`
153+
154+
### iOS에서 커스텀 스타일이 필요한 경우
155+
156+
**Option 1: Marker의 Custom View 사용**
157+
```tsx
158+
<NaverMapMarkerOverlay latitude={37.5} longitude={126.5}>
159+
<View style={{
160+
backgroundColor: 'white',
161+
borderRadius: 10,
162+
padding: 10,
163+
borderWidth: 2,
164+
borderColor: '#4263eb'
165+
}}>
166+
<Text style={{ fontWeight: 'bold' }}>커스텀 정보</Text>
167+
</View>
168+
</NaverMapMarkerOverlay>
169+
```
170+
171+
**Option 2: 플랫폼별 조건부 렌더링**
172+
```tsx
173+
{Platform.OS === 'android' ? (
174+
<NaverMapInfoWindow
175+
identifier="marker1"
176+
text="마커 정보"
177+
fontWeight="bold"
178+
borderRadius={10}
179+
/>
180+
) : (
181+
<NaverMapMarkerOverlay identifier="info-marker">
182+
<CustomInfoView />
183+
</NaverMapMarkerOverlay>
184+
)}
185+
```
115186

116187
## 구현 패턴
117188

@@ -134,11 +205,19 @@ import { NaverMapInfoWindow } from '@mj-studio/react-native-naver-map';
134205
## 완료 상태
135206

136207
- ✅ TypeScript Spec 및 타입 정의
137-
- ✅ Android 네이티브 구현
138-
- ✅ iOS 네이티브 구현
208+
- ✅ Android 네이티브 구현 (모든 스타일 지원)
209+
- ✅ iOS 네이티브 구현 (기본 텍스트)
139210
- ✅ React Component 작성
140211
- ✅ Package 등록
141212
- ✅ Export 추가
142-
- ⏳ 마커 연결 기능 (향후)
143-
- ⏳ 커스텀 뷰 지원 (향후)
213+
- ✅ 마커 연결 기능 (`identifier`)
214+
- ✅ 열림/닫힘 제어 (`isOpen`)
215+
- ✅ Marker Registry 구현
216+
- ⚠️ iOS 커스텀 스타일 (API 제한으로 미지원)
217+
218+
## 사용 권장사항
219+
220+
- **간단한 텍스트만 필요**: InfoWindow 사용 (양쪽 플랫폼)
221+
- **커스텀 스타일 필요 (Android만)**: InfoWindow 사용, iOS는 기본 스타일
222+
- **커스텀 스타일 필요 (양쪽 플랫폼)**: Marker의 Custom View 사용
144223

ios/Overlay/InfoWindow/RNCNaverMapInfoWindow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
@property(nonatomic, strong) NMFInfoWindow* inner;
2929

3030
- (void)setCurrentMapView:(NMFMapView*)mapView;
31+
- (void)setParentMapViewImpl:(RNCNaverMapViewImpl*)mapViewImpl;
3132
- (void)updateInfoWindowState;
3233

3334
@end

ios/Overlay/InfoWindow/RNCNaverMapInfoWindow.mm

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ @implementation RNCNaverMapInfoWindow {
2020
NSString* _markerIdentifier;
2121
BOOL _shouldBeOpen;
2222
NMFMapView* _currentMapView;
23+
RNCNaverMapViewImpl* _parentMapViewImpl;
2324
NMFInfoWindowDefaultTextSource* _textDataSource;
2425
}
2526

@@ -38,7 +39,7 @@ - (instancetype)init {
3839
_inner = [NMFInfoWindow new];
3940
_shouldBeOpen = YES; // Default isOpen = true
4041

41-
// Create text data source
42+
// Create text data source (iOS only supports text for now)
4243
_textDataSource = [NMFInfoWindowDefaultTextSource dataSource];
4344
_textDataSource.title = @"";
4445
_inner.dataSource = _textDataSource;
@@ -61,15 +62,9 @@ - (void)setCurrentMapView:(NMFMapView*)mapView {
6162
[self updateInfoWindowState];
6263
}
6364

64-
- (RNCNaverMapViewImpl*)findMapView {
65-
UIView* current = self.superview;
66-
while (current) {
67-
if ([current isKindOfClass:[RNCNaverMapViewImpl class]]) {
68-
return (RNCNaverMapViewImpl*)current;
69-
}
70-
current = current.superview;
71-
}
72-
return nil;
65+
- (void)setParentMapViewImpl:(RNCNaverMapViewImpl*)mapViewImpl {
66+
_parentMapViewImpl = mapViewImpl;
67+
[self updateInfoWindowState];
7368
}
7469

7570
- (void)updateInfoWindowState {
@@ -81,15 +76,12 @@ - (void)updateInfoWindowState {
8176
if (!_currentMapView) return;
8277

8378
// Try to find marker by identifier first
84-
if (_markerIdentifier && _markerIdentifier.length > 0) {
85-
RNCNaverMapViewImpl* mapViewImpl = [self findMapView];
86-
if (mapViewImpl) {
87-
RNCNaverMapMarker* markerView = mapViewImpl.markerRegistry[_markerIdentifier];
88-
if (markerView) {
89-
// Open on marker (marker position is used automatically)
90-
[_inner openWithMarker:markerView.inner];
91-
return;
92-
}
79+
if (_markerIdentifier && _markerIdentifier.length > 0 && _parentMapViewImpl) {
80+
RNCNaverMapMarker* markerView = _parentMapViewImpl.markerRegistry[_markerIdentifier];
81+
if (markerView) {
82+
// Open on marker (marker position is used automatically)
83+
[_inner openWithMarker:markerView.inner];
84+
return;
9385
}
9486
}
9587

@@ -144,11 +136,15 @@ - (void)updateProps:(Props::Shared const&)props oldProps:(Props::Shared const&)o
144136
[self updateInfoWindowState];
145137
}
146138

147-
// Text content - use simple text source for now
139+
// Text content only (iOS custom styling is not supported by NMFInfoWindow API)
140+
// For custom styling on iOS, consider using Marker with custom view instead
148141
if (prev.text != next.text) {
149142
_textDataSource.title = getNsStr(next.text);
150143
}
151-
144+
145+
// Note: fontWeight, borderRadius, borderWidth, borderColor, padding
146+
// are ignored on iOS due to NMFInfoWindow limitations
147+
// These props work on Android only
152148

153149
[super updateProps:props oldProps:oldProps];
154150
}

ios/RNCNaverMapViewImpl.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ - (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
108108
} else if ([subview isKindOfClass:[RNCNaverMapInfoWindow class]]) {
109109
auto infoWindowView = static_cast<RNCNaverMapInfoWindow*>(subview);
110110
[infoWindowView setCurrentMapView:self.mapView];
111+
[infoWindowView setParentMapViewImpl:self];
111112
} else {
112113
NSArray<id<RCTComponent>>* childSubviews = [subview reactSubviews];
113114
for (int i = 0; i < childSubviews.count; i++) {

src/component/NaverMapInfoWindow.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ export interface NaverMapInfoWindowProps
128128
* InfoWindow는 마커의 위 또는 지도의 특정 지점에 부가적인 정보를 나타내기 위한 오버레이입니다.
129129
* 주로 말풍선 형태로 구성되어 텍스트를 표시하는 용도로 사용합니다.
130130
*
131+
* **플랫폼별 스타일 지원:**
132+
* - Android: 모든 스타일 속성 지원 (fontWeight, borderRadius, borderWidth, borderColor, padding)
133+
* - iOS: 기본 텍스트만 지원 (NMFInfoWindow API 제한)
134+
*
131135
* @example
132136
* ```tsx
133137
* // 1. 마커에 연결된 InfoWindow (권장)
@@ -140,6 +144,10 @@ export interface NaverMapInfoWindowProps
140144
* identifier="marker1"
141145
* text="마커 정보"
142146
* isOpen={true}
147+
* // Android only: 커스텀 스타일
148+
* fontWeight="bold"
149+
* borderRadius={10}
150+
* borderColor="#4263eb"
143151
* />
144152
*
145153
* // 2. 특정 좌표에 InfoWindow 직접 표시
@@ -154,6 +162,7 @@ export interface NaverMapInfoWindowProps
154162
* ```
155163
*
156164
* @see https://navermaps.github.io/android-map-sdk/guide-ko/5-3.html
165+
* @see https://navermaps.github.io/ios-map-sdk/guide-ko/5-3.html
157166
*/
158167
export const NaverMapInfoWindow = ({
159168
latitude,

0 commit comments

Comments
 (0)