Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Keychy/Keychy.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3235,7 +3235,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.0;
MARKETING_VERSION = 1.1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.KeychyOfficial.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -3284,7 +3284,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.0;
MARKETING_VERSION = 1.1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.KeychyOfficial.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -3324,7 +3324,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.1.0;
MARKETING_VERSION = 1.1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.KeychyOfficial.app.WidgetKeychy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -3367,7 +3367,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.1.0;
MARKETING_VERSION = 1.1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.KeychyOfficial.app.WidgetKeychy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class BundleViewModel {
// MARK: - 뭉치 캡쳐 이미지

var bundleCapturedImage: Data?
var bundleWidgetImage: Data?

// MARK: - 현재 선택된 뭉치

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ extension BundleCreateView {
carabinerFrontURL = nil
}

// 씬 캡처
// 1. 배경 포함 캡처 (앱용)
if let pngData = await MultiKeyringCaptureScene.captureBundleImage(
keyringDataList: keyringDataList,
backgroundImageURL: background.backgroundImage,
Expand All @@ -121,6 +121,23 @@ extension BundleCreateView {
}
}

// 2. 배경 없이 캡처 (위젯용 - 투명 여백 제거)
let widgetData = await MultiKeyringCaptureScene.captureBundleImage(
keyringDataList: keyringDataList,
backgroundImageURL: nil,
carabinerBackImageURL: carabinerBackURL,
carabinerFrontImageURL: carabinerFrontURL,
carabinerType: carabinerType,
carabinerId: carabiner.id ?? "",
carabinerX: carabiner.carabinerX,
carabinerY: carabiner.carabinerY,
carabinerWidth: carabiner.carabinerWidth,
trimTransparentEdges: true
)
await MainActor.run {
bundleVM.bundleWidgetImage = widgetData
}

// 캡처 완료 후 다음 화면으로 이동
await MainActor.run {
isCapturing = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,24 @@
//

import SwiftUI
import Nuke

// MARK: - 데이터 초기화
extension BundleCreateView {

/// 초기 데이터 로딩
func initializeData() async {
// 사용자가 소유한 배경과 카라비너 데이터를 가져옴
await loadUserOwnedItems()
// 시트 이미지 프리페칭 (백그라운드에서 Nuke 캐시에 미리 로드)
prefetchSheetImages()
}

/// 배경/카라비너 썸네일을 Nuke 캐시에 미리 로드
private func prefetchSheetImages() {
let bgURLs = bundleVM.backgroundViewData.compactMap { URL(string: $0.background.backgroundImage) }
let cbURLs = bundleVM.carabinerViewData.compactMap { URL(string: $0.carabiner.carabinerImage[0]) }
let prefetcher = ImagePrefetcher()
prefetcher.startPrefetching(with: bgURLs + cbURLs)
}

/// 화면이 다시 나타날 때 데이터 새로고침
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ extension BundleCreateView {
isSelected: selectedPosition == index,
action: {
selectedPosition = index
showKeyringSheet = true
if showItemSheet {
// 배경/카라비너 시트 닫기 → 닫힌 후 키링 시트 표시
showItemSheet = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
showKeyringSheet = true
}
} else {
showKeyringSheet = true
}
}
)
.position(x: viewX, y: viewY)
Expand All @@ -45,50 +53,56 @@ extension BundleCreateView {
var sheetContent: some View {
ZStack(alignment: .bottom) {
Color.clear

if showItemSheet {
// 시트가 있을 때: 셀렉터 + 시트가 함께 움직임
VStack(spacing: 0) {
BundleSheetToggleButtons(
showItemSheet: $showItemSheet,
isBackgroundMode: $isBackgroundMode
)
.padding(.bottom, 10)

DraggableSheet(
sheetHeight: $sheetHeight,
header: BundleSheetFilterBar(viewModel: bundleVM),
content: itemSheetContent,
onDismiss: {
showItemSheet = false
}
)

// 시트 레이어 (항상 존재, 오프셋으로 숨김 → 즉시 반응)
DraggableSheet(
sheetHeight: $sheetHeight,
header: BundleSheetFilterBar(viewModel: bundleVM),
content: itemSheetContent,
onDismiss: {
showItemSheet = false
}
.transition(.move(edge: .bottom))
)
.offset(y: showItemSheet ? 0 : sheetHeight)
.allowsHitTesting(showItemSheet)

// 버튼 레이어 (matchedGeometryEffect로 위치만 보간)
if showItemSheet {
BundleSheetToggleButtons(
showItemSheet: $showItemSheet,
isBackgroundMode: $isBackgroundMode
)
.matchedGeometryEffect(id: "toggleButtons", in: sheetButtonNamespace)
.padding(.bottom, sheetHeight + 10)
} else {
// 시트가 없을 때: 셀렉터만 하단에 고정
BundleSheetToggleButtons(
showItemSheet: $showItemSheet,
isBackgroundMode: $isBackgroundMode
)
.matchedGeometryEffect(id: "toggleButtons", in: sheetButtonNamespace)
.padding(.bottom, 50)
.transition(.identity)
}
}
.animation(.easeInOut(duration: 0.25), value: showItemSheet)
.animation(.easeOut(duration: 0.2), value: showItemSheet)
.onChange(of: showItemSheet) { _, isShowing in
if isShowing {
sheetHeight = UIScreen.main.bounds.height * 0.4
}
}
}

@ViewBuilder
var itemSheetContent: some View {
if isBackgroundMode {
ZStack(alignment: .top) {
SelectBackgroundSheet(
viewModel: bundleVM,
selectedBG: bundleVM.newSelectedBackground,
onBackgroundTap: { bg in
bundleVM.newSelectedBackground = bg
}
)
} else {
.opacity(isBackgroundMode ? 1 : 0)
.allowsHitTesting(isBackgroundMode)

SelectCarabinerSheet(
viewModel: bundleVM,
selectedCarabiner: bundleVM.newSelectedCarabiner,
Expand All @@ -97,6 +111,8 @@ extension BundleCreateView {
bundleVM.newSelectedCarabiner = carabiner
}
)
.opacity(isBackgroundMode ? 0 : 1)
.allowsHitTesting(!isBackgroundMode)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ struct BundleCreateView<Route: BundleRoute>: View {
@Bindable var bundleVM: BundleViewModel

// 시트 활성화 상태
@Namespace var sheetButtonNamespace
@State var showItemSheet: Bool = false
@State var isBackgroundMode: Bool = true // true: 배경, false: 카라비너
@State var showKeyringSheet: Bool = false

// 시트 높이
@State var sheetHeight: CGFloat = 360
// 시트 높이 (DraggableSheet.onAppear에서 mediumHeight로 갱신됨)
@State var sheetHeight: CGFloat = UIScreen.main.bounds.height * 0.4

// 키링 선택 상태
@State var selectedKeyrings: [Int: Keyring] = [:]
Expand Down Expand Up @@ -97,6 +98,9 @@ struct BundleCreateView<Route: BundleRoute>: View {
keyringButtons(carabiner: cb.carabiner)
}
.blur(radius: showPurchaseSuccessAlert || isCapturing ? 10 : 0)
.onTapGesture {
if showItemSheet { showItemSheet = false }
}

// 하단 셀렉터 + 시트
sheetContent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ extension BundleNameInputView {
// Firebase 저장 성공 후 ViewModel의 이미지를 캐시에 저장
bundleVM.saveBundleImageToCache(
bundleId: bundleId,
bundleName: bundleNameToSave
bundleName: bundleNameToSave,
widgetImageData: bundleVM.bundleWidgetImage
)

isUploading = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,26 @@

import SwiftUI
import FirebaseFirestore
import Nuke

extension BundleEditView {
func initializeData() async {
resetSceneState()

await loadUserKeyring()

await loadBackgroundAndCarabiner()


// 시트 이미지 프리페칭 (백그라운드에서 Nuke 캐시에 미리 로드)
prefetchSheetImages()
}

/// 배경/카라비너 썸네일을 Nuke 캐시에 미리 로드
private func prefetchSheetImages() {
let bgURLs = bundleVM.backgroundViewData.compactMap { URL(string: $0.background.backgroundImage) }
let cbURLs = bundleVM.carabinerViewData.compactMap { URL(string: $0.carabiner.carabinerImage[0]) }
let prefetcher = ImagePrefetcher()
prefetcher.startPrefetching(with: bgURLs + cbURLs)
}

func resetSceneState() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,55 @@ extension BundleEditView {
ZStack(alignment: .bottom) {
Color.clear

if showItemSheet {
// 시트가 있을 때: 셀렉터 + 시트가 함께 움직임
VStack(spacing: 0) {
BundleSheetToggleButtons(
showItemSheet: $showItemSheet,
isBackgroundMode: $isBackgroundMode
)
.padding(.bottom, 10)

DraggableSheet(
sheetHeight: $sheetHeight,
header: BundleSheetFilterBar(viewModel: bundleVM),
content: itemSheetContent,
onDismiss: {
showItemSheet = false
}
)
// 시트 레이어 (항상 존재, 오프셋으로 숨김 → 즉시 반응)
DraggableSheet(
sheetHeight: $sheetHeight,
header: BundleSheetFilterBar(viewModel: bundleVM),
content: itemSheetContent,
onDismiss: {
showItemSheet = false
}
.transition(.move(edge: .bottom))
)
.offset(y: showItemSheet ? 0 : sheetHeight)
.allowsHitTesting(showItemSheet)

// 버튼 레이어 (matchedGeometryEffect로 위치만 보간)
if showItemSheet {
BundleSheetToggleButtons(
showItemSheet: $showItemSheet,
isBackgroundMode: $isBackgroundMode
)
.matchedGeometryEffect(id: "toggleButtons", in: sheetButtonNamespace)
.padding(.bottom, sheetHeight + 10)
} else {
// 시트가 없을 때: 셀렉터만 하단에 고정
BundleSheetToggleButtons(
showItemSheet: $showItemSheet,
isBackgroundMode: $isBackgroundMode
)
.matchedGeometryEffect(id: "toggleButtons", in: sheetButtonNamespace)
.padding(.bottom, 50)
.transition(.identity)
}
}
.animation(.easeInOut(duration: 0.25), value: showItemSheet)
.animation(.easeOut(duration: 0.2), value: showItemSheet)
.onChange(of: showItemSheet) { _, isShowing in
if isShowing {
sheetHeight = UIScreen.main.bounds.height * 0.4
}
}
}

@ViewBuilder
private var itemSheetContent: some View {
if isBackgroundMode {
ZStack(alignment: .top) {
SelectBackgroundSheet(
viewModel: bundleVM,
selectedBG: bundleVM.newSelectedBackground,
onBackgroundTap: { bg in
bundleVM.newSelectedBackground = bg
}
)
} else {
.opacity(isBackgroundMode ? 1 : 0)
.allowsHitTesting(isBackgroundMode)

SelectCarabinerSheet(
viewModel: bundleVM,
selectedCarabiner: bundleVM.newSelectedCarabiner,
Expand All @@ -63,6 +69,8 @@ extension BundleEditView {
showChangeCarabinerAlert = true
}
)
.opacity(isBackgroundMode ? 0 : 1)
.allowsHitTesting(!isBackgroundMode)
}
}

Expand Down
Loading