diff --git a/wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate b/wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate index f5a68fe..2a4b61b 100644 Binary files a/wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate and b/wake.xcodeproj/project.xcworkspace/xcuserdata/elliwood.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/wake/View/Components/Upload/Avatar.swift b/wake/View/Components/Upload/Avatar.swift index e69de29..d5e6deb 100644 --- a/wake/View/Components/Upload/Avatar.swift +++ b/wake/View/Components/Upload/Avatar.swift @@ -0,0 +1,122 @@ +import SwiftUI +import PhotosUI + +struct PhotoPicker: UIViewControllerRepresentable { + @Binding var selectedImages: [UIImage] + let selectionLimit: Int + let filter: PHPickerFilter + + init(selectedImages: Binding<[UIImage]>, selectionLimit: Int = 1, filter: PHPickerFilter = .images) { + self._selectedImages = selectedImages + self.selectionLimit = selectionLimit + self.filter = filter + } + + func makeUIViewController(context: Context) -> PHPickerViewController { + var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) + configuration.filter = filter + configuration.selectionLimit = selectionLimit + configuration.preferredAssetRepresentationMode = .current + + let picker = PHPickerViewController(configuration: configuration) + picker.delegate = context.coordinator + return picker + } + + func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {} + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: NSObject, PHPickerViewControllerDelegate { + let parent: PhotoPicker + + init(_ parent: PhotoPicker) { + self.parent = parent + } + + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + parent.selectedImages.removeAll() + + let group = DispatchGroup() + var loadedImages: [Int: UIImage] = [:] + + for (index, result) in results.enumerated() { + group.enter() + + if result.itemProvider.canLoadObject(ofClass: UIImage.self) { + result.itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in + if let image = image as? UIImage { + loadedImages[index] = image + } + group.leave() + } + } else { + group.leave() + } + } + + group.notify(queue: .main) { + // Sort the images by their original index to maintain selection order + let sortedImages = loadedImages.sorted { $0.key < $1.key }.map { $0.value } + self.parent.selectedImages.append(contentsOf: sortedImages) + + // Dismiss the picker + picker.dismiss(animated: true) + } + } + } +} + +// MARK: - Avatar Uploader Component +struct AvatarUploader: View { + @Binding var selectedImage: UIImage? + let size: CGFloat + + @State private var isImagePickerPresented = false + + var body: some View { + Button(action: { + isImagePickerPresented = true + }) { + ZStack { + // Avatar Image or Placeholder + if let selectedImage = selectedImage { + Image(uiImage: selectedImage) + .resizable() + .scaledToFill() + .frame(width: size, height: size) + .clipShape(RoundedRectangle(cornerRadius: size * 0.1)) + } else { + // Default avatar container + Color.gray.opacity(0.1) + .frame(width: size, height: size) + .overlay( + SVGImage(svgName: "Avatar") + .frame(width: size * 0.8, height: size * 0.8) + ) + .clipShape(RoundedRectangle(cornerRadius: size * 0.1)) + .overlay( + RoundedRectangle(cornerRadius: size * 0.1) + .stroke(Color.gray.opacity(0.3), lineWidth: 1) + ) + } + } + .frame(width: size, height: size) + .contentShape(Rectangle()) // Make the entire area tappable + } + .buttonStyle(PlainButtonStyle()) // Remove button highlight effect + .sheet(isPresented: $isImagePickerPresented) { + PhotoPicker( + selectedImages: Binding( + get: { [selectedImage].compactMap { $0 } }, + set: { images in + selectedImage = images.first + } + ), + selectionLimit: 1 + ) + } + } +} \ No newline at end of file diff --git a/wake/View/Owner/UserInfo/UserInfo.swift b/wake/View/Owner/UserInfo/UserInfo.swift index 74c7f9a..7a49929 100644 --- a/wake/View/Owner/UserInfo/UserInfo.swift +++ b/wake/View/Owner/UserInfo/UserInfo.swift @@ -9,6 +9,7 @@ struct UserInfo: View { @State private var notificationsEnabled = true @State private var darkModeEnabled = false @State private var showLogoutAlert = false + @State private var avatarImage: UIImage? // Add this line var body: some View { VStack(spacing: 0) { @@ -40,17 +41,23 @@ struct UserInfo: View { // Avatar ZStack { - SVGImage(svgName: "Avatar") - .frame(width: 225, height: 225) + // Show either the SVG or the uploaded image + if let avatarImage = avatarImage { + Image(uiImage: avatarImage) + .resizable() + .scaledToFill() + .frame(width: 200, height: 200) + .clipShape(Circle()) + } else { + SVGImage(svgName: "Avatar") + .frame(width: 200, height: 200) + } - // Fallback in case SVG fails to load - Image(systemName: "person.circle.fill") - .resizable() - .scaledToFit() - .frame(width: 225, height: 225) - .foregroundColor(.gray) - .opacity(0.3) + // Make sure the AvatarUploader is on top and covers the entire area + AvatarUploader(selectedImage: $avatarImage, size: 200) + .contentShape(Rectangle()) // This makes the entire area tappable } + .frame(width: 200, height: 200) .padding(.vertical, 20) // Buttons diff --git a/wake/Views/Utils/SVGImage.swift b/wake/Views/Utils/SVGImage.swift index ca680cd..8604cfd 100644 --- a/wake/Views/Utils/SVGImage.swift +++ b/wake/Views/Utils/SVGImage.swift @@ -9,20 +9,38 @@ struct SVGImage: UIViewRepresentable { webView.isOpaque = false webView.backgroundColor = .clear webView.scrollView.isScrollEnabled = false + webView.scrollView.contentInsetAdjustmentBehavior = .never - // 尝试从根目录加载SVG文件 if let url = Bundle.main.url(forResource: svgName, withExtension: "svg") { - print("SVG URL found: \(url.path)") - let request = URLRequest(url: url) - webView.load(request) - } else { - print("Error: Failed to find SVG file with name: \(svgName).svg in main bundle") - // 打印所有可用的SVG文件 - if let resourceURL = Bundle.main.resourceURL, - let files = try? FileManager.default.contentsOfDirectory(at: resourceURL, includingPropertiesForKeys: nil) { - let svgFiles = files.filter { $0.pathExtension.lowercased() == "svg" } - print("Available SVG files: \(svgFiles)") - } + let htmlString = """ + + +
+ + + + +