react-native-attachment-kit 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +5 -2
- package/android/src/main/libs/rnattachmentkit-release.aar +0 -0
- package/android/src/newarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +31 -0
- package/android/src/oldarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +31 -0
- package/ios/RNAttachmentKit.swift +37 -0
- package/ios/RNAttachmentKit.xcframework/Info.plist +5 -5
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Assets.car +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.abi.json +436 -45
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.private.swiftinterface +5 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftinterface +5 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/RNAttachmentKit +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeResources +12 -12
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeSignature +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.abi.json +436 -45
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +5 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +5 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +436 -45
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +5 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +5 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/RNAttachmentKit +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeResources +20 -20
- package/package.json +1 -1
- package/src/index.ts +4 -3
- package/src/types.ts +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- **Viewer (iOS): a video page with no poster image now shows the video**. The player layer sits inside the page's image view, and that view only got a size from a loaded image, so with an empty poster URL or a failed poster download the layer stayed 0×0: the video played (sound, position moving) on an empty page. `ViewerVideoItemView` now sizes the area from `presentationSize` (rotation metadata included), or the page size until that is known. Once a poster arrives its size is used, as before.
|
|
6
|
+
- Checked on the iOS simulator (test harness): empty poster and 404 poster with local / remote / download-to-`cacheFile` videos, landscape, portrait and 90°-rotation-metadata files. Poster pages have the same frames as before; a poster arriving late, swiping away and back, pinch zoom, and the zoom close from the current frame all work.
|
|
7
|
+
|
|
8
|
+
## 1.1.2 — 2026-11-03
|
|
9
|
+
|
|
10
|
+
- **Viewer: `underlayViewTag` — the photo moves *behind* the app's translucent bar during the zoom open / close (iOS + Android `useOverlayViewer`)**. With 1.1.1's clip the moving photo was only cut at the bar, so the part under the bar was simply missing while it moved (and an app that kept its cell visible showed that part already in place). Now the app places an empty view right below the bar and passes its RN tag; the kit draws a copy of the moving photo and of the source mask inside that view: the bar's live blur shows the photo sliding under it and the source cell's under-bar part is hidden by the copied mask from the first frame until the close ends. The clipped photo in the viewer window stays as before (so it never covers the bar). The first version of this option (`closeBelowViewTag`, never published) inserted the copy into the bar's parent; that shifted React's child indexes and debug builds crashed with "Attempt to unmount a view which has a different index", so the copy now only goes inside the app's empty view.
|
|
11
|
+
- iOS: `ZoomTransitionAnimator.underView` (weak, the empty view) — under layer + copy mask (follows `updateMaskView`) + copy image animated with the same frames; removed when the close ends (zoom, fade or slide). The bridge resolves the tag with `viewWithTag` (Fabric sets component views' `tag` to the RN tag).
|
|
12
|
+
- Android: `ImageViewerOverlayView` finds the empty view by id (= RN tag) and adds a `FrameLayout` inside it, laid out by hand (RN view groups don't lay out foreign children); the copy image has `activityRoot`'s size/place so it reuses the dummy's matrix and outline each frame; the copy mask follows the mask (position, drawable, visibility). Removed in `cleanup()`.
|
|
13
|
+
- Android: the copy image also gets `clipBounds` = this frame's cell. The app's blur view captures the screen on a software canvas, where `clipToOutline` is ignored, so the whole uncut photo went into the blur and the bar turned lighter until the copy was removed at the end of the close (seen with a large single photo; iOS not affected). `clipBounds` applies in both drawing paths. Measured on an emulator, same card, dark mode, mean brightness of the title row: settled 24.1, last frames of the close 30.9 before; settled 24.6, last frames 24.7 – 24.9 after.
|
|
14
|
+
- Checked on the iOS simulator and an Android emulator (HeyTalk feed).
|
|
15
|
+
|
|
16
|
+
## 1.1.1 — 2026-11-03
|
|
17
|
+
|
|
18
|
+
- **Viewer: `sourceClipInsets` / `sourceClipRect` options — the zoom open / close stays behind translucent bars (iOS + Android `useOverlayViewer`)**. The moving photo and the `hideSourceImage` mask are drawn in the window above the app, so a thumbnail half under the app's translucent top bar flew back *over* the bar and the mask covered the bar too. With a clip area (screen-edge insets, a rect in `sourceRect` coordinates, or both = intersection) they are drawn only inside it: on open the photo starts cut at the bar and the clip widens to the full screen during the second half (the viewer itself is never cut); on close it narrows back in the first 20 % (the viewer background turns transparent at once on close, so the photo never covers the bar), and the part under the bar disappears behind it. Checked on the iOS simulator (HeyTalk feed): a cell half under the translucent top bar opens from behind the bar and closes back behind it; a cell clear of the bar animates as before. `updateSourceRect` takes the same keys (changed only when passed, `null` = no clip) for paging to another thumbnail. Without the options nothing changes.
|
|
19
|
+
- iOS: `ZoomTransitionAnimator.sourceClipRect` / `sourceClipInsets` (public) — the dummy image sits in a full-screen view whose `mask` view animates between the clip and the bounds; the source mask sits in a holder of the clip's size (`clipsToBounds`). Other users of the animator (gallery, media gallery) pass nothing.
|
|
20
|
+
- Android: `ImageViewerOverlayView` sets `clipBounds` on the dummy image (lerped with the open / close progress) and on the mask (its own coordinates, refreshed whenever the mask moves). Insets are measured from the window's decor view. The Activity / Dialog viewers ignore the options.
|
|
3
21
|
## 1.1.0 — 2026-10-27
|
|
4
22
|
|
|
5
23
|
- **New public API for native screens: `AttachmentProcessor` (iOS framework + Android aar)** — the "after picking" processing that used to live only inside the RN bridge (photo resize and save as webp / jpg / png, photo thumbnail, video export with `VideoExporter` and its poster, the processing-start slots with predicted sizes and posters, progress throttling, per-video cancel, temp files and the result model). An app's own native screen can now open the kit gallery and process the result with exactly the same code as `openGallery`, so both produce the same files (size, thumbnail, poster).
|
package/README.md
CHANGED
|
@@ -341,6 +341,9 @@ ref.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
|
341
341
|
| `sourceBorderCorners` | `('topLeft' \| 'topRight' \| 'bottomLeft' \| 'bottomRight')[]` | Which corners are rounded (chat-bubble groups). `[]` = none. |
|
|
342
342
|
| `sourceBackgroundColor` | `string` | Background behind the thumbnail during the animation. |
|
|
343
343
|
| `hideSourceImage` | `boolean` | A native mask hides the source thumbnail while the viewer is open. |
|
|
344
|
+
| `sourceClipInsets` | `{ top?, bottom?, left?, right? }` | Screen-edge insets of the area where the **moving photo and the `hideSourceImage` mask** are drawn during the zoom open / close (e.g. `{ top: headerHeight, bottom: tabBarHeight }` for translucent bars). A thumbnail half under a translucent bar then slides **behind** the bar instead of over it. The clip widens to the full screen in the second half of the open (when the viewer background covers the bar) and narrows in the first 20 % of the close, so the viewer itself is never cut. iOS + Android `useOverlayViewer`. |
|
|
345
|
+
| `sourceClipRect` | `SourceRect` | Same, as a rect in `sourceRect` coordinates (e.g. a list area inside a sheet). With both, the intersection is used. Omit both = no clipping (as before). |
|
|
346
|
+
| `underlayViewTag` | `number` | `findNodeHandle` of an **empty** view the app places right below its translucent bar: full screen, no children, `collapsable={false}`, `pointerEvents="none"`, rendered after the list and before the bar. During the zoom open / close a copy of the moving photo and of the source mask is drawn inside it, so the bar's live blur shows the photo sliding behind it — clipping alone only hides the part under the bar. Use with `sourceClipInsets` / `sourceClipRect`. Never pass a view that has React children: native views added there shift React's child indexes and the app can crash. iOS + Android `useOverlayViewer`. |
|
|
344
347
|
|
|
345
348
|
**Actions**
|
|
346
349
|
|
|
@@ -368,8 +371,8 @@ ref.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
|
368
371
|
| `useDialogViewer: true` | DialogFragment — no `AppState` blip, but background RN layout changes may not render until it closes. |
|
|
369
372
|
| `useOverlayViewer: true` | ⭐ View on top of the host Activity's window — no `AppState` blip, background changes render in real time. Wins over `useDialogViewer`. |
|
|
370
373
|
|
|
371
|
-
#### `updateSourceRect(rect:
|
|
372
|
-
Updates where the viewer closes to (e.g. after paging to another thumbnail or scrolling).
|
|
374
|
+
#### `updateSourceRect(rect: ViewerSourceUpdate): Promise<void>`
|
|
375
|
+
Updates where the viewer closes to (e.g. after paging to another thumbnail or scrolling). `ViewerSourceUpdate = SourceRect & { sourceBorderRadius?, sourceBorderCorners?, sourceClipRect?, sourceClipInsets?, underlayViewTag? }` — the clip keys change only when passed (`null` = stop clipping).
|
|
373
376
|
|
|
374
377
|
#### `injectImageCache(url: string, localPath: string): Promise<boolean>`
|
|
375
378
|
Seeds the viewer's image cache with a local file for a remote URL, so it opens instantly without network.
|
|
Binary file
|
|
@@ -212,12 +212,36 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
// 잘라내기 영역 — 넘긴 키만 바뀜 (useOverlayViewer 뷰어만 반영)
|
|
216
|
+
clipArrayOf(options, "sourceClipRect")?.let { intent.putExtra("sourceClipRect", it) }
|
|
217
|
+
clipArrayOf(options, "sourceClipInsets")?.let { intent.putExtra("sourceClipInsets", it) }
|
|
218
|
+
if (options.hasKey("underlayViewTag")) {
|
|
219
|
+
intent.putExtra("underlayViewTag", if (options.isNull("underlayViewTag")) 0 else options.getDouble("underlayViewTag").toInt())
|
|
220
|
+
}
|
|
215
221
|
androidx.localbroadcastmanager.content.LocalBroadcastManager.getInstance(reactApplicationContext)
|
|
216
222
|
.sendBroadcast(intent)
|
|
217
223
|
|
|
218
224
|
promise.resolve(null)
|
|
219
225
|
}
|
|
220
226
|
|
|
227
|
+
/**
|
|
228
|
+
* sourceClipRect {x,y,width,height} / sourceClipInsets {top,bottom,left,right} (dp) → 화면 px [l,t,r,b]
|
|
229
|
+
* (여백이면 각 변의 여백). 키가 없으면 null(안 바꿈), 값이 null 이면 빈 배열(자르지 않음).
|
|
230
|
+
*/
|
|
231
|
+
private fun clipArrayOf(options: ReadableMap, key: String): FloatArray? {
|
|
232
|
+
if (!options.hasKey(key)) return null
|
|
233
|
+
if (options.isNull(key)) return FloatArray(0)
|
|
234
|
+
val m = options.getMap(key) ?: return FloatArray(0)
|
|
235
|
+
fun px(k: String) = if (m.hasKey(k) && !m.isNull(k)) com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(m.getDouble(k).toFloat()) else 0f
|
|
236
|
+
return if (key == "sourceClipRect") {
|
|
237
|
+
val x = px("x")
|
|
238
|
+
val y = px("y")
|
|
239
|
+
floatArrayOf(x, y, x + px("width"), y + px("height"))
|
|
240
|
+
} else {
|
|
241
|
+
floatArrayOf(px("left"), px("top"), px("right"), px("bottom"))
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
221
245
|
override fun getName(): String = NAME
|
|
222
246
|
|
|
223
247
|
override fun init(licenseKey: String, promise: Promise) {
|
|
@@ -664,6 +688,13 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
|
|
|
664
688
|
if (options.hasKey("sourceBackgroundColor")) {
|
|
665
689
|
putString("sourceBackgroundColor", options.getString("sourceBackgroundColor"))
|
|
666
690
|
}
|
|
691
|
+
// 잘라내기 영역 (열림·닫힘 중 사진과 가림 판이 그려지는 곳) — 이 오버레이 뷰어만
|
|
692
|
+
clipArrayOf(options, "sourceClipRect")?.let { putFloatArray("sourceClipRect", it) }
|
|
693
|
+
clipArrayOf(options, "sourceClipInsets")?.let { putFloatArray("sourceClipInsets", it) }
|
|
694
|
+
// 앱 쪽 사본을 그릴 자리 — 앱이 둔 빈 RN 뷰 (태그 = view id)
|
|
695
|
+
if (options.hasKey("underlayViewTag") && !options.isNull("underlayViewTag")) {
|
|
696
|
+
putInt("underlayViewTag", options.getDouble("underlayViewTag").toInt())
|
|
697
|
+
}
|
|
667
698
|
// placeholderImages: 타입/뷰어 코드에는 있었지만 브릿지에서 한 번도 전달되지 않았던
|
|
668
699
|
// 필드. 네이티브 뷰어가 onlyRetrieveFromCache(true)로 첫 프레임을 즉시 그리려 할 때
|
|
669
700
|
// 쓰는 저해상도 대체 이미지 목록입니다(원본 다운로드 전 임시 표시용).
|
|
@@ -522,10 +522,34 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
|
|
|
522
522
|
intent.putStringArrayListExtra("sourceBorderCorners", corners)
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
|
+
// 잘라내기 영역 — 넘긴 키만 바뀜 (useOverlayViewer 뷰어만 반영)
|
|
526
|
+
clipArrayOf(options, "sourceClipRect")?.let { intent.putExtra("sourceClipRect", it) }
|
|
527
|
+
clipArrayOf(options, "sourceClipInsets")?.let { intent.putExtra("sourceClipInsets", it) }
|
|
528
|
+
if (options.hasKey("underlayViewTag")) {
|
|
529
|
+
intent.putExtra("underlayViewTag", if (options.isNull("underlayViewTag")) 0 else options.getDouble("underlayViewTag").toInt())
|
|
530
|
+
}
|
|
525
531
|
androidx.localbroadcastmanager.content.LocalBroadcastManager.getInstance(reactApplicationContext).sendBroadcast(intent)
|
|
526
532
|
promise.resolve(null)
|
|
527
533
|
}
|
|
528
534
|
|
|
535
|
+
/**
|
|
536
|
+
* sourceClipRect {x,y,width,height} / sourceClipInsets {top,bottom,left,right} (dp) → 화면 px [l,t,r,b]
|
|
537
|
+
* (여백이면 각 변의 여백). 키가 없으면 null(안 바꿈), 값이 null 이면 빈 배열(자르지 않음).
|
|
538
|
+
*/
|
|
539
|
+
private fun clipArrayOf(options: ReadableMap, key: String): FloatArray? {
|
|
540
|
+
if (!options.hasKey(key)) return null
|
|
541
|
+
if (options.isNull(key)) return FloatArray(0)
|
|
542
|
+
val m = options.getMap(key) ?: return FloatArray(0)
|
|
543
|
+
fun px(k: String) = if (m.hasKey(k) && !m.isNull(k)) com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(m.getDouble(k).toFloat()) else 0f
|
|
544
|
+
return if (key == "sourceClipRect") {
|
|
545
|
+
val x = px("x")
|
|
546
|
+
val y = px("y")
|
|
547
|
+
floatArrayOf(x, y, x + px("width"), y + px("height"))
|
|
548
|
+
} else {
|
|
549
|
+
floatArrayOf(px("left"), px("top"), px("right"), px("bottom"))
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
529
553
|
/** 동영상 변환 취소 (HUD 없이 앱이 말풍선에서). index -1 = 남은 동영상 전부. 진행 중인 처리가 없으면 false */
|
|
530
554
|
@ReactMethod
|
|
531
555
|
fun cancelVideoProcessing(index: Double, processingId: String, promise: Promise) {
|
|
@@ -673,6 +697,13 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
|
|
|
673
697
|
if (options.hasKey("sourceBackgroundColor")) {
|
|
674
698
|
putString("sourceBackgroundColor", options.getString("sourceBackgroundColor"))
|
|
675
699
|
}
|
|
700
|
+
// 잘라내기 영역 (열림·닫힘 중 사진과 가림 판이 그려지는 곳) — 이 오버레이 뷰어만
|
|
701
|
+
clipArrayOf(options, "sourceClipRect")?.let { putFloatArray("sourceClipRect", it) }
|
|
702
|
+
clipArrayOf(options, "sourceClipInsets")?.let { putFloatArray("sourceClipInsets", it) }
|
|
703
|
+
// 앱 쪽 사본을 그릴 자리 — 앱이 둔 빈 RN 뷰 (태그 = view id)
|
|
704
|
+
if (options.hasKey("underlayViewTag") && !options.isNull("underlayViewTag")) {
|
|
705
|
+
putInt("underlayViewTag", options.getDouble("underlayViewTag").toInt())
|
|
706
|
+
}
|
|
676
707
|
// placeholderImages: 타입/뷰어 코드에는 있었지만 브릿지에서 한 번도 전달되지 않았던
|
|
677
708
|
// 필드. 네이티브 뷰어가 onlyRetrieveFromCache(true)로 첫 프레임을 즉시 그리려 할 때
|
|
678
709
|
// 쓰는 저해상도 대체 이미지 목록입니다(원본 다운로드 전 임시 표시용).
|
|
@@ -175,6 +175,16 @@ class RNAttachmentKit: RCTEventEmitter {
|
|
|
175
175
|
} else if let r = optDict["sourceBorderRadius"] as? Double {
|
|
176
176
|
self.zoomAnimator.sourceBorderRadius = CGFloat(r)
|
|
177
177
|
}
|
|
178
|
+
// 잘라내기 영역 — 키가 있을 때만 바꿈 (null = 자르지 않음)
|
|
179
|
+
if optDict.keys.contains("sourceClipRect") {
|
|
180
|
+
self.zoomAnimator.sourceClipRect = RNAttachmentKit.parseRect(optDict["sourceClipRect"])
|
|
181
|
+
}
|
|
182
|
+
if optDict.keys.contains("sourceClipInsets") {
|
|
183
|
+
self.zoomAnimator.sourceClipInsets = RNAttachmentKit.parseInsets(optDict["sourceClipInsets"])
|
|
184
|
+
}
|
|
185
|
+
if optDict.keys.contains("underlayViewTag") {
|
|
186
|
+
self.zoomAnimator.underView = RNAttachmentKit.viewForTag(optDict["underlayViewTag"])
|
|
187
|
+
}
|
|
178
188
|
self.zoomAnimator.updateMaskView()
|
|
179
189
|
|
|
180
190
|
resolve(true)
|
|
@@ -203,6 +213,26 @@ class RNAttachmentKit: RCTEventEmitter {
|
|
|
203
213
|
return CGRect(x: x, y: y, width: w, height: h)
|
|
204
214
|
}
|
|
205
215
|
|
|
216
|
+
/// RN 뷰 태그(findNodeHandle) → 앱 창의 네이티브 뷰. Fabric 은 컴포넌트 뷰의 tag 에 RN 태그를 넣음. 없으면 nil (메인 스레드)
|
|
217
|
+
private static func viewForTag(_ v: Any?) -> UIView? {
|
|
218
|
+
guard let n = v as? NSNumber, n.intValue > 0 else { return nil }
|
|
219
|
+
let tag = n.intValue
|
|
220
|
+
for scene in UIApplication.shared.connectedScenes {
|
|
221
|
+
guard let ws = scene as? UIWindowScene else { continue }
|
|
222
|
+
for w in ws.windows {
|
|
223
|
+
if let found = w.viewWithTag(tag) { return found }
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return nil
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/// { top, bottom, left, right } (빠진 값 0). 객체가 아니면 nil
|
|
230
|
+
private static func parseInsets(_ v: Any?) -> UIEdgeInsets? {
|
|
231
|
+
guard let d = v as? NSDictionary else { return nil }
|
|
232
|
+
return UIEdgeInsets(top: cgFloat(d["top"]) ?? 0, left: cgFloat(d["left"]) ?? 0,
|
|
233
|
+
bottom: cgFloat(d["bottom"]) ?? 0, right: cgFloat(d["right"]) ?? 0)
|
|
234
|
+
}
|
|
235
|
+
|
|
206
236
|
/// 말풍선 이미지(정적 지도 스냅샷) 로드. 메모리/디스크 캐시 → file:// → 다운로드 순.
|
|
207
237
|
/// 실패하거나 너무 오래 걸리면 nil (이미지 없이 지도만 엶)
|
|
208
238
|
/// completion(이미지, 마커 뺀 이미지인지). renderLocationThumbnail 결과면 마커 뺀 짝을 씀 → 뷰어가 마커를 따로 그려
|
|
@@ -495,6 +525,9 @@ class RNAttachmentKit: RCTEventEmitter {
|
|
|
495
525
|
parsedRect = CGRect(x: x, y: y, width: w, height: h)
|
|
496
526
|
}
|
|
497
527
|
|
|
528
|
+
let sourceClipRect = RNAttachmentKit.parseRect(options["sourceClipRect"])
|
|
529
|
+
let sourceClipInsets = RNAttachmentKit.parseInsets(options["sourceClipInsets"])
|
|
530
|
+
|
|
498
531
|
var sourceBorderRadius: CGFloat = 0
|
|
499
532
|
if let radiusNum = options["sourceBorderRadius"] as? NSNumber {
|
|
500
533
|
sourceBorderRadius = CGFloat(truncating: radiusNum)
|
|
@@ -551,6 +584,10 @@ class RNAttachmentKit: RCTEventEmitter {
|
|
|
551
584
|
self.zoomAnimator.sourceBorderRadius = sourceBorderRadius
|
|
552
585
|
self.zoomAnimator.sourceBorderCorners = mask
|
|
553
586
|
self.zoomAnimator.sourceBackgroundColor = sourceBackgroundColor
|
|
587
|
+
// 애니메이터는 재사용되므로 이번 호출 값으로 덮어씀 (안 주면 자르지 않음)
|
|
588
|
+
self.zoomAnimator.sourceClipRect = sourceClipRect
|
|
589
|
+
self.zoomAnimator.sourceClipInsets = sourceClipInsets
|
|
590
|
+
self.zoomAnimator.underView = RNAttachmentKit.viewForTag(options["underlayViewTag"])
|
|
554
591
|
// Reset any sourceImage left over from a previous openViewer() call - it's only
|
|
555
592
|
// meant to carry this call's local placeholder (see below), and must not leak
|
|
556
593
|
// into a later call that has no placeholder of its own.
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>RNAttachmentKit.framework/RNAttachmentKit</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>RNAttachmentKit.framework</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
-
<string>x86_64</string>
|
|
18
17
|
</array>
|
|
19
18
|
<key>SupportedPlatform</key>
|
|
20
19
|
<string>ios</string>
|
|
21
|
-
<key>SupportedPlatformVariant</key>
|
|
22
|
-
<string>simulator</string>
|
|
23
20
|
</dict>
|
|
24
21
|
<dict>
|
|
25
22
|
<key>BinaryPath</key>
|
|
26
23
|
<string>RNAttachmentKit.framework/RNAttachmentKit</string>
|
|
27
24
|
<key>LibraryIdentifier</key>
|
|
28
|
-
<string>ios-
|
|
25
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
29
26
|
<key>LibraryPath</key>
|
|
30
27
|
<string>RNAttachmentKit.framework</string>
|
|
31
28
|
<key>SupportedArchitectures</key>
|
|
32
29
|
<array>
|
|
33
30
|
<string>arm64</string>
|
|
31
|
+
<string>x86_64</string>
|
|
34
32
|
</array>
|
|
35
33
|
<key>SupportedPlatform</key>
|
|
36
34
|
<string>ios</string>
|
|
35
|
+
<key>SupportedPlatformVariant</key>
|
|
36
|
+
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|