feat: 拍照使用头像

This commit is contained in:
jinyaqiu 2025-08-22 14:25:37 +08:00
parent e539bb37d2
commit 534c38cd8b
6 changed files with 138 additions and 18 deletions

View File

@ -20,6 +20,10 @@
</array>
<key>NSAppleIDUsageDescription</key>
<string>Sign in with Apple is used to authenticate your account</string>
<key>NSCameraUsageDescription</key>
<string>We need access to your camera to take photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to select photos</string>
<key>UIAppFonts</key>
<array>
<string>Inter.ttf</string>

View File

@ -0,0 +1,70 @@
import AVFoundation
import UIKit
class ImageCaptureManager: NSObject {
static let shared = ImageCaptureManager()
private var completion: ((UIImage?) -> Void)?
private weak var presentingViewController: UIViewController?
func captureImage(from viewController: UIViewController, completion: @escaping (UIImage?) -> Void) {
self.completion = completion
self.presentingViewController = viewController
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .authorized:
presentImagePicker()
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
DispatchQueue.main.async {
if granted {
self?.presentImagePicker()
} else {
self?.completion?(nil)
}
}
}
default:
completion(nil)
}
}
private func presentImagePicker() {
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
completion?(nil)
return
}
let picker = UIImagePickerController()
picker.sourceType = .camera
picker.allowsEditing = true
picker.delegate = self
// Present from the topmost view controller
var topController = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController
while let presentedVC = topController?.presentedViewController {
topController = presentedVC
}
topController?.present(picker, animated: true)
}
}
extension ImageCaptureManager: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage
picker.dismiss(animated: true) { [weak self] in
self?.completion?(image)
self?.completion = nil
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true) { [weak self] in
self?.completion?(nil)
self?.completion = nil
}
}
}

View File

@ -3,6 +3,7 @@ import SwiftUI
public struct AvatarPicker: View {
@StateObject private var uploadManager = MediaUploadManager()
@State private var showMediaPicker = false
@State private var showImageCapture = false
@State private var isUploading = false
@Binding var selectedImage: UIImage?
@Binding var showUsername: Bool
@ -12,7 +13,10 @@ public struct AvatarPicker: View {
// Animation state
@State private var isAnimating = false
public init(selectedImage: Binding<UIImage?>, showUsername: Binding<Bool>, isKeyboardVisible: Binding<Bool>, uploadedFileId: Binding<String?>) {
public init(selectedImage: Binding<UIImage?>,
showUsername: Binding<Bool>,
isKeyboardVisible: Binding<Bool>,
uploadedFileId: Binding<String?>) {
self._selectedImage = selectedImage
self._showUsername = showUsername
self._isKeyboardVisible = isKeyboardVisible
@ -139,6 +143,35 @@ public struct AvatarPicker: View {
}
}
}
if !showUsername {
Button(action: {
withAnimation {
showImageCapture = true
}
}) {
Text("Take a Photo")
.font(Typography.font(for: .subtitle, family: .inter))
.fontWeight(.regular)
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.themePrimaryLight)
)
}
.frame(maxWidth: .infinity)
.sheet(isPresented: $showImageCapture) {
CameraView(isPresented: $showImageCapture) { image in
selectedImage = image
uploadManager.selectedMedia = [.image(image)]
withAnimation {
isUploading = true
}
uploadManager.startUpload()
}
}
}
}
}

View File

@ -0,0 +1,30 @@
import SwiftUI
import UIKit
struct CameraView: UIViewControllerRepresentable {
@Binding var isPresented: Bool
let onImageSelected: (UIImage) -> Void
@Environment(\.presentationMode) private var presentationMode
func makeUIViewController(context: Context) -> UIViewController {
let viewController = UIViewController()
viewController.view.backgroundColor = .clear
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
if isPresented {
DispatchQueue.main.async {
if let rootVC = UIApplication.shared.windows.first?.rootViewController {
ImageCaptureManager.shared.captureImage(from: rootVC) { image in
if let image = image {
onImageSelected(image)
}
isPresented = false
presentationMode.wrappedValue.dismiss()
}
}
}
}
}
}

View File

@ -108,23 +108,6 @@ struct UserInfo: View {
uploadedFileId: $uploadedFileId
)
.padding(.top, isKeyboardVisible ? 0 : 20)
if !showUsername {
Button(action: {
// Action for second button
}) {
Text("Take a Photo")
.font(Typography.font(for: .subtitle, family: .inter))
.fontWeight(.regular)
.frame(maxWidth: .infinity)
.padding()
.foregroundColor(.black)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.themePrimaryLight)
)
}
}
if showUsername {
TextField("Username", text: $userName)