feat: 新增图片源选择功能以增强头像设置体验
- 添加AppImageSource枚举以定义图片源类型(相机和相册)。 - 在AppSettingFeature中新增状态和Action以管理图片源选择。 - 更新AppSettingView以支持图片源选择的ActionSheet和头像选择逻辑。 - 优化ImagePickerWithPreviewView以处理相机和相册选择的取消操作。
This commit is contained in:
@@ -1,6 +1,12 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ComposableArchitecture
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
// 图片源选择枚举
|
||||||
|
enum AppImageSource: Equatable {
|
||||||
|
case camera
|
||||||
|
case photoLibrary
|
||||||
|
}
|
||||||
|
|
||||||
@Reducer
|
@Reducer
|
||||||
struct AppSettingFeature {
|
struct AppSettingFeature {
|
||||||
@ObservableState
|
@ObservableState
|
||||||
@@ -37,6 +43,10 @@ struct AppSettingFeature {
|
|||||||
}
|
}
|
||||||
// 新增:TCA驱动图片选择弹窗
|
// 新增:TCA驱动图片选择弹窗
|
||||||
var showImagePicker: Bool = false
|
var showImagePicker: Bool = false
|
||||||
|
// 新增:图片源选择 ActionSheet
|
||||||
|
var showImageSourceActionSheet: Bool = false
|
||||||
|
// 新增:选择的图片源
|
||||||
|
var selectedImageSource: AppImageSource? = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Action: Equatable {
|
enum Action: Equatable {
|
||||||
@@ -73,6 +83,10 @@ struct AppSettingFeature {
|
|||||||
case testPushTapped
|
case testPushTapped
|
||||||
// 新增:TCA驱动图片选择弹窗
|
// 新增:TCA驱动图片选择弹窗
|
||||||
case setShowImagePicker(Bool)
|
case setShowImagePicker(Bool)
|
||||||
|
// 新增:图片源选择 ActionSheet
|
||||||
|
case setShowImageSourceActionSheet(Bool)
|
||||||
|
// 新增:图片源选择
|
||||||
|
case selectImageSource(AppImageSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Dependency(\.apiService) var apiService
|
@Dependency(\.apiService) var apiService
|
||||||
@@ -254,6 +268,15 @@ struct AppSettingFeature {
|
|||||||
case .setShowImagePicker(let show):
|
case .setShowImagePicker(let show):
|
||||||
state.showImagePicker = show
|
state.showImagePicker = show
|
||||||
return .none
|
return .none
|
||||||
|
case .setShowImageSourceActionSheet(let show):
|
||||||
|
state.showImageSourceActionSheet = show
|
||||||
|
return .none
|
||||||
|
case .selectImageSource(let source):
|
||||||
|
state.showImageSourceActionSheet = false
|
||||||
|
state.showImagePicker = true
|
||||||
|
state.selectedImageSource = source
|
||||||
|
// 这里可以传递选择的源到 ImagePickerWithPreviewView
|
||||||
|
return .none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,24 +16,14 @@ struct AppSettingView: View {
|
|||||||
initialState: ImagePickerWithPreviewReducer.State(inner: ImagePickerWithPreviewState(selectionMode: .single)),
|
initialState: ImagePickerWithPreviewReducer.State(inner: ImagePickerWithPreviewState(selectionMode: .single)),
|
||||||
reducer: { ImagePickerWithPreviewReducer() }
|
reducer: { ImagePickerWithPreviewReducer() }
|
||||||
)
|
)
|
||||||
@State private var showNicknameAlert = false
|
|
||||||
@State private var nicknameInput = ""
|
|
||||||
@State private var showImagePickerSheet = false
|
|
||||||
@State private var showActionSheet = false
|
|
||||||
@State private var showPhotoPicker = false
|
|
||||||
@State private var showCamera = false
|
|
||||||
@State private var selectedPhotoItems: [PhotosPickerItem] = []
|
|
||||||
@State private var selectedImages: [UIImage] = []
|
|
||||||
@State private var cameraImage: UIImage? = nil
|
|
||||||
@State private var previewIndex: Int = 0
|
|
||||||
@State private var showPreview = false
|
|
||||||
@State private var isLoading = false
|
|
||||||
@State private var errorMessage: String? = nil
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
WithPerceptionTracking {
|
WithPerceptionTracking {
|
||||||
mainView()
|
mainView()
|
||||||
}
|
}
|
||||||
|
.onAppear {
|
||||||
|
store.send(.onAppear)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@@ -63,7 +53,7 @@ struct AppSettingView: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text(LocalizedString("app_settings.title", comment: "设置"))
|
Text(LocalizedString("appSetting.title", comment: "编辑"))
|
||||||
.font(.system(size: 18, weight: .medium))
|
.font(.system(size: 18, weight: .medium))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
@@ -78,76 +68,92 @@ struct AppSettingView: View {
|
|||||||
|
|
||||||
// 主要内容区域
|
// 主要内容区域
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 0) {
|
||||||
// 头像设置区域
|
// 头像设置区域
|
||||||
avatarSection()
|
avatarSection()
|
||||||
|
.padding(.top, 20)
|
||||||
|
|
||||||
// 个人信息设置区域
|
// 个人信息设置区域
|
||||||
personalInfoSection()
|
personalInfoSection()
|
||||||
|
.padding(.top, 30)
|
||||||
|
|
||||||
// 其他设置区域
|
// 其他设置区域
|
||||||
otherSettingsSection()
|
otherSettingsSection()
|
||||||
|
.padding(.top, 20)
|
||||||
|
|
||||||
|
Spacer(minLength: 40)
|
||||||
|
|
||||||
// 退出登录按钮
|
// 退出登录按钮
|
||||||
logoutSection()
|
logoutSection()
|
||||||
|
.padding(.bottom, 40)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
.padding(.top, 20)
|
|
||||||
.padding(.bottom, 40)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
// 头像选择器
|
// 图片源选择 ActionSheet
|
||||||
.sheet(isPresented: $showImagePickerSheet) {
|
.confirmationDialog(
|
||||||
ImagePickerWithPreviewView(
|
"请选择图片来源",
|
||||||
store: pickerStore,
|
isPresented: Binding(
|
||||||
onUpload: { images in
|
get: { store.showImageSourceActionSheet },
|
||||||
if let firstImage = images.first,
|
set: { store.send(.setShowImageSourceActionSheet($0)) }
|
||||||
let imageData = firstImage.jpegData(compressionQuality: 0.8) {
|
),
|
||||||
store.send(AppSettingFeature.Action.avatarSelected(imageData))
|
titleVisibility: .visible
|
||||||
}
|
) {
|
||||||
showImagePickerSheet = false
|
Button(LocalizedString("app_settings.take_photo", comment: "拍照")) {
|
||||||
},
|
store.send(.selectImageSource(AppImageSource.camera))
|
||||||
onCancel: {
|
// 直接触发相机
|
||||||
showImagePickerSheet = false
|
pickerStore.send(.inner(.selectSource(.camera)))
|
||||||
}
|
}
|
||||||
)
|
Button(LocalizedString("app_settings.select_from_album", comment: "从相册选择")) {
|
||||||
|
store.send(.selectImageSource(AppImageSource.photoLibrary))
|
||||||
|
// 直接触发相册
|
||||||
|
pickerStore.send(.inner(.selectSource(.photoLibrary)))
|
||||||
|
}
|
||||||
|
Button(LocalizedString("common.cancel", comment: "取消"), role: .cancel) { }
|
||||||
}
|
}
|
||||||
// 相机拍照
|
// 头像选择器
|
||||||
.sheet(isPresented: $showCamera) {
|
.sheet(isPresented: Binding(
|
||||||
|
get: { store.showImagePicker },
|
||||||
|
set: { store.send(.setShowImagePicker($0)) }
|
||||||
|
)) {
|
||||||
ImagePickerWithPreviewView(
|
ImagePickerWithPreviewView(
|
||||||
store: pickerStore,
|
store: pickerStore,
|
||||||
onUpload: { images in
|
onUpload: { images in
|
||||||
if let firstImage = images.first,
|
if let firstImage = images.first,
|
||||||
let imageData = firstImage.jpegData(compressionQuality: 0.8) {
|
let imageData = firstImage.jpegData(compressionQuality: 0.8) {
|
||||||
store.send(AppSettingFeature.Action.avatarSelected(imageData))
|
store.send(.avatarSelected(imageData))
|
||||||
}
|
}
|
||||||
showCamera = false
|
store.send(.setShowImagePicker(false))
|
||||||
},
|
},
|
||||||
onCancel: {
|
onCancel: {
|
||||||
showCamera = false
|
store.send(.setShowImagePicker(false))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// 昵称编辑弹窗
|
// 昵称编辑弹窗
|
||||||
.alert(LocalizedString("app_settings.edit_nickname", comment: "编辑昵称"), isPresented: $showNicknameAlert) {
|
.alert(LocalizedString("appSetting.nickname", comment: "编辑昵称"), isPresented: Binding(
|
||||||
TextField(LocalizedString("app_settings.nickname_placeholder", comment: "请输入昵称"), text: $nicknameInput)
|
get: { store.isEditingNickname },
|
||||||
Button(LocalizedString("app_settings.cancel", comment: "取消")) {
|
set: { store.send(.nicknameEditAlert($0)) }
|
||||||
showNicknameAlert = false
|
)) {
|
||||||
nicknameInput = ""
|
TextField(LocalizedString("appSetting.nickname", comment: "请输入昵称"), text: Binding(
|
||||||
|
get: { store.nicknameInput },
|
||||||
|
set: { store.send(.nicknameInputChanged($0)) }
|
||||||
|
))
|
||||||
|
Button(LocalizedString("common.cancel", comment: "取消")) {
|
||||||
|
store.send(.nicknameEditAlert(false))
|
||||||
}
|
}
|
||||||
Button(LocalizedString("app_settings.confirm", comment: "确认")) {
|
Button(LocalizedString("common.confirm", comment: "确认")) {
|
||||||
let trimmed = nicknameInput.trimmingCharacters(in: .whitespacesAndNewlines)
|
let trimmed = store.nicknameInput.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
if !trimmed.isEmpty {
|
if !trimmed.isEmpty {
|
||||||
store.send(.nicknameEditConfirmed(trimmed))
|
store.send(.nicknameEditConfirmed(trimmed))
|
||||||
}
|
}
|
||||||
showNicknameAlert = false
|
store.send(.nicknameEditAlert(false))
|
||||||
nicknameInput = ""
|
|
||||||
}
|
}
|
||||||
} message: {
|
} message: {
|
||||||
Text(LocalizedString("app_settings.nickname_tip", comment: "请输入新的昵称"))
|
Text(LocalizedString("appSetting.nickname", comment: "请输入新的昵称"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,34 +165,41 @@ struct AppSettingView: View {
|
|||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
// 头像
|
// 头像
|
||||||
Button(action: {
|
Button(action: {
|
||||||
showImagePickerSheet = true
|
store.send(.setShowImageSourceActionSheet(true))
|
||||||
}) {
|
}) {
|
||||||
AsyncImage(url: URL(string: store.userInfo?.avatar ?? "")) { image in
|
ZStack {
|
||||||
image
|
AsyncImage(url: URL(string: store.userInfo?.avatar ?? "")) { image in
|
||||||
.resizable()
|
image
|
||||||
.aspectRatio(contentMode: .fill)
|
.resizable()
|
||||||
} placeholder: {
|
.aspectRatio(contentMode: .fill)
|
||||||
Image(systemName: "person.circle.fill")
|
} placeholder: {
|
||||||
.resizable()
|
Image(systemName: "person.circle.fill")
|
||||||
.aspectRatio(contentMode: .fill)
|
.resizable()
|
||||||
.foregroundColor(.gray)
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
|
.frame(width: 100, height: 100)
|
||||||
|
.clipShape(Circle())
|
||||||
|
|
||||||
|
// 相机图标覆盖
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Circle()
|
||||||
|
.fill(Color.purple)
|
||||||
|
.frame(width: 32, height: 32)
|
||||||
|
.overlay(
|
||||||
|
Image(systemName: "camera")
|
||||||
|
.font(.system(size: 16, weight: .medium))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: 100, height: 100)
|
||||||
}
|
}
|
||||||
.frame(width: 80, height: 80)
|
|
||||||
.clipShape(Circle())
|
|
||||||
.overlay(
|
|
||||||
Circle()
|
|
||||||
.stroke(Color.white.opacity(0.3), lineWidth: 2)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(LocalizedString("app_settings.tap_to_change_avatar", comment: "点击更换头像"))
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.foregroundColor(.white.opacity(0.7))
|
|
||||||
}
|
}
|
||||||
.padding(.vertical, 20)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.background(Color.white.opacity(0.1))
|
|
||||||
.cornerRadius(12)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,28 +211,13 @@ struct AppSettingView: View {
|
|||||||
// 昵称设置
|
// 昵称设置
|
||||||
SettingRow(
|
SettingRow(
|
||||||
icon: "person",
|
icon: "person",
|
||||||
title: LocalizedString("app_settings.nickname", comment: "昵称"),
|
title: LocalizedString("appSetting.nickname", comment: "昵称"),
|
||||||
subtitle: store.userInfo?.nick ?? LocalizedString("app_settings.not_set", comment: "未设置"),
|
subtitle: store.userInfo?.nick ?? LocalizedString("app_settings.not_set", comment: "未设置"),
|
||||||
action: {
|
action: {
|
||||||
nicknameInput = store.userInfo?.nick ?? ""
|
store.send(.nicknameEditAlert(true))
|
||||||
showNicknameAlert = true
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Divider()
|
|
||||||
.background(Color.white.opacity(0.2))
|
|
||||||
.padding(.leading, 50)
|
|
||||||
|
|
||||||
// 用户ID
|
|
||||||
SettingRow(
|
|
||||||
icon: "number",
|
|
||||||
title: LocalizedString("app_settings.user_id", comment: "用户ID"),
|
|
||||||
subtitle: "\(store.userInfo?.uid ?? 0)",
|
|
||||||
action: nil
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.background(Color.white.opacity(0.1))
|
|
||||||
.cornerRadius(12)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,8 +228,8 @@ struct AppSettingView: View {
|
|||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
SettingRow(
|
SettingRow(
|
||||||
icon: "hand.raised",
|
icon: "hand.raised",
|
||||||
title: LocalizedString("app_settings.personal_info_permissions", comment: "个人信息权限"),
|
title: LocalizedString("appSetting.personalInfoPermissions", comment: "个人信息与权限"),
|
||||||
subtitle: LocalizedString("app_settings.manage_permissions", comment: "管理权限"),
|
subtitle: "",
|
||||||
action: { store.send(.personalInfoPermissionsTapped) }
|
action: { store.send(.personalInfoPermissionsTapped) }
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -241,8 +239,8 @@ struct AppSettingView: View {
|
|||||||
|
|
||||||
SettingRow(
|
SettingRow(
|
||||||
icon: "questionmark.circle",
|
icon: "questionmark.circle",
|
||||||
title: LocalizedString("app_settings.help", comment: "帮助"),
|
title: LocalizedString("appSetting.help", comment: "帮助"),
|
||||||
subtitle: LocalizedString("app_settings.get_help", comment: "获取帮助"),
|
subtitle: "",
|
||||||
action: { store.send(.helpTapped) }
|
action: { store.send(.helpTapped) }
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -252,8 +250,8 @@ struct AppSettingView: View {
|
|||||||
|
|
||||||
SettingRow(
|
SettingRow(
|
||||||
icon: "trash",
|
icon: "trash",
|
||||||
title: LocalizedString("app_settings.clear_cache", comment: "清除缓存"),
|
title: LocalizedString("appSetting.clearCache", comment: "清除缓存"),
|
||||||
subtitle: LocalizedString("app_settings.free_up_space", comment: "释放空间"),
|
subtitle: "",
|
||||||
action: { store.send(.clearCacheTapped) }
|
action: { store.send(.clearCacheTapped) }
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -263,8 +261,8 @@ struct AppSettingView: View {
|
|||||||
|
|
||||||
SettingRow(
|
SettingRow(
|
||||||
icon: "arrow.clockwise",
|
icon: "arrow.clockwise",
|
||||||
title: LocalizedString("app_settings.check_updates", comment: "检查更新"),
|
title: LocalizedString("appSetting.checkUpdates", comment: "检查更新"),
|
||||||
subtitle: LocalizedString("app_settings.latest_version", comment: "最新版本"),
|
subtitle: "",
|
||||||
action: { store.send(.checkUpdatesTapped) }
|
action: { store.send(.checkUpdatesTapped) }
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -274,8 +272,8 @@ struct AppSettingView: View {
|
|||||||
|
|
||||||
SettingRow(
|
SettingRow(
|
||||||
icon: "person.crop.circle.badge.minus",
|
icon: "person.crop.circle.badge.minus",
|
||||||
title: LocalizedString("app_settings.deactivate_account", comment: "注销账号"),
|
title: LocalizedString("appSetting.deactivateAccount", comment: "注销账号"),
|
||||||
subtitle: LocalizedString("app_settings.permanent_deletion", comment: "永久删除"),
|
subtitle: "",
|
||||||
action: { store.send(.deactivateAccountTapped) }
|
action: { store.send(.deactivateAccountTapped) }
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -285,13 +283,11 @@ struct AppSettingView: View {
|
|||||||
|
|
||||||
SettingRow(
|
SettingRow(
|
||||||
icon: "info.circle",
|
icon: "info.circle",
|
||||||
title: LocalizedString("app_settings.about_us", comment: "关于我们"),
|
title: LocalizedString("appSetting.aboutUs", comment: "关于我们"),
|
||||||
subtitle: LocalizedString("app_settings.app_info", comment: "应用信息"),
|
subtitle: "",
|
||||||
action: { store.send(.aboutUsTapped) }
|
action: { store.send(.aboutUsTapped) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.background(Color.white.opacity(0.1))
|
|
||||||
.cornerRadius(12)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,16 +295,19 @@ struct AppSettingView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func logoutSection() -> some View {
|
private func logoutSection() -> some View {
|
||||||
WithPerceptionTracking {
|
WithPerceptionTracking {
|
||||||
Button(action: {
|
VStack(spacing: 12) {
|
||||||
store.send(.logoutTapped)
|
// 退出登录按钮
|
||||||
}) {
|
Button(action: {
|
||||||
Text(LocalizedString("app_settings.logout", comment: "退出登录"))
|
store.send(.logoutTapped)
|
||||||
.font(.system(size: 16, weight: .medium))
|
}) {
|
||||||
.foregroundColor(.white)
|
Text(LocalizedString("appSetting.logoutAccount", comment: "退出账户"))
|
||||||
.frame(maxWidth: .infinity)
|
.font(.system(size: 16, weight: .medium))
|
||||||
.padding(.vertical, 16)
|
.foregroundColor(.white)
|
||||||
.background(Color.red.opacity(0.8))
|
.frame(maxWidth: .infinity)
|
||||||
.cornerRadius(12)
|
.padding(.vertical, 16)
|
||||||
|
.background(Color.red.opacity(0.8))
|
||||||
|
.cornerRadius(12)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,9 +335,11 @@ struct SettingRow: View {
|
|||||||
.font(.system(size: 16))
|
.font(.system(size: 16))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Text(subtitle)
|
if !subtitle.isEmpty {
|
||||||
.font(.system(size: 14))
|
Text(subtitle)
|
||||||
.foregroundColor(.white.opacity(0.7))
|
.font(.system(size: 14))
|
||||||
|
.foregroundColor(.white.opacity(0.7))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
@@ -23,10 +23,10 @@ public struct ImagePickerWithPreviewView: View {
|
|||||||
Color.clear
|
Color.clear
|
||||||
}
|
}
|
||||||
.background(.clear)
|
.background(.clear)
|
||||||
.modifier(ActionSheetModifier(viewStore: viewStore, onCancel: onCancel))
|
.ignoresSafeArea()
|
||||||
.modifier(CameraSheetModifier(viewStore: viewStore))
|
.modifier(CameraSheetModifier(viewStore: viewStore, onCancel: onCancel))
|
||||||
.modifier(PhotosPickerModifier(viewStore: viewStore, loadedImages: $loadedImages, isLoadingImages: $isLoadingImages, loadingId: $loadingId))
|
.modifier(PhotosPickerModifier(viewStore: viewStore, loadedImages: $loadedImages, isLoadingImages: $isLoadingImages, loadingId: $loadingId, onCancel: onCancel))
|
||||||
.modifier(PreviewCoverModifier(viewStore: viewStore, loadedImages: loadedImages, onUpload: onUpload, loadingId: $loadingId))
|
.modifier(PreviewCoverModifier(viewStore: viewStore, loadedImages: loadedImages, onUpload: onUpload, loadingId: $loadingId, onCancel: onCancel))
|
||||||
.modifier(ErrorToastModifier(viewStore: viewStore))
|
.modifier(ErrorToastModifier(viewStore: viewStore))
|
||||||
.onChange(of: viewStore.inner.isLoading) { isLoading in
|
.onChange(of: viewStore.inner.isLoading) { isLoading in
|
||||||
if isLoading && loadingId == nil {
|
if isLoading && loadingId == nil {
|
||||||
@@ -40,34 +40,21 @@ public struct ImagePickerWithPreviewView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct ActionSheetModifier: ViewModifier {
|
|
||||||
let viewStore: ViewStoreOf<ImagePickerWithPreviewReducer>
|
|
||||||
let onCancel: () -> Void
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
content.confirmationDialog(
|
|
||||||
"请选择图片来源",
|
|
||||||
isPresented: .init(
|
|
||||||
get: { viewStore.inner.showActionSheet },
|
|
||||||
set: { viewStore.send(.inner(.showActionSheet($0))) }
|
|
||||||
),
|
|
||||||
titleVisibility: .visible
|
|
||||||
) {
|
|
||||||
Button(LocalizedString("app_settings.take_photo", comment: "")) { viewStore.send(.inner(.selectSource(.camera))) }
|
|
||||||
Button(LocalizedString("app_settings.select_from_album", comment: "")) { viewStore.send(.inner(.selectSource(.photoLibrary))) }
|
|
||||||
Button("取消", role: .cancel) { onCancel() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct CameraSheetModifier: ViewModifier {
|
private struct CameraSheetModifier: ViewModifier {
|
||||||
let viewStore: ViewStoreOf<ImagePickerWithPreviewReducer>
|
let viewStore: ViewStoreOf<ImagePickerWithPreviewReducer>
|
||||||
|
let onCancel: () -> Void
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content.sheet(isPresented: .init(
|
content.sheet(isPresented: .init(
|
||||||
get: { viewStore.inner.showCamera },
|
get: { viewStore.inner.showCamera },
|
||||||
set: { viewStore.send(.inner(.setShowCamera($0))) }
|
set: { viewStore.send(.inner(.setShowCamera($0))) }
|
||||||
)) {
|
)) {
|
||||||
CameraPicker { image in
|
CameraPicker { image in
|
||||||
viewStore.send(.inner(.cameraImagePicked(image)))
|
if let image = image {
|
||||||
|
viewStore.send(.inner(.cameraImagePicked(image)))
|
||||||
|
} else {
|
||||||
|
// 相机取消,关闭整个视图
|
||||||
|
onCancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,12 +65,20 @@ private struct PhotosPickerModifier: ViewModifier {
|
|||||||
@Binding var loadedImages: [UIImage]
|
@Binding var loadedImages: [UIImage]
|
||||||
@Binding var isLoadingImages: Bool
|
@Binding var isLoadingImages: Bool
|
||||||
@Binding var loadingId: UUID?
|
@Binding var loadingId: UUID?
|
||||||
|
let onCancel: () -> Void
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
.photosPicker(
|
.photosPicker(
|
||||||
isPresented: .init(
|
isPresented: .init(
|
||||||
get: { viewStore.inner.showPhotoPicker },
|
get: { viewStore.inner.showPhotoPicker },
|
||||||
set: { viewStore.send(.inner(.setShowPhotoPicker($0))) }
|
set: { show in
|
||||||
|
viewStore.send(.inner(.setShowPhotoPicker(show)))
|
||||||
|
// 如果相册选择器被关闭且没有选择图片,则关闭整个视图
|
||||||
|
if !show && viewStore.inner.selectedPhotoItems.isEmpty {
|
||||||
|
onCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
),
|
),
|
||||||
selection: .init(
|
selection: .init(
|
||||||
get: { viewStore.inner.selectedPhotoItems },
|
get: { viewStore.inner.selectedPhotoItems },
|
||||||
@@ -131,6 +126,7 @@ private struct PreviewCoverModifier: ViewModifier {
|
|||||||
let loadedImages: [UIImage]
|
let loadedImages: [UIImage]
|
||||||
let onUpload: ([UIImage]) -> Void
|
let onUpload: ([UIImage]) -> Void
|
||||||
@Binding var loadingId: UUID?
|
@Binding var loadingId: UUID?
|
||||||
|
let onCancel: () -> Void
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content.fullScreenCover(isPresented: .init(
|
content.fullScreenCover(isPresented: .init(
|
||||||
get: { viewStore.inner.showPreview },
|
get: { viewStore.inner.showPreview },
|
||||||
@@ -148,6 +144,7 @@ private struct PreviewCoverModifier: ViewModifier {
|
|||||||
},
|
},
|
||||||
onCancel: {
|
onCancel: {
|
||||||
viewStore.send(.inner(.previewCancel))
|
viewStore.send(.inner(.previewCancel))
|
||||||
|
onCancel()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user