iOS 2.5.1 - Fixed Camera Rotation

This commit is contained in:
2025-12-12 00:38:24 -07:00
parent 8154cd24bb
commit abb76e454b
5 changed files with 127 additions and 69 deletions

View File

@@ -465,7 +465,7 @@
CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 37;
CURRENT_PROJECT_VERSION = 38;
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
DRIVERKIT_DEPLOYMENT_TARGET = 24.6;
ENABLE_PREVIEWS = YES;
@@ -487,7 +487,7 @@
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.6;
MARKETING_VERSION = 2.5.0;
MARKETING_VERSION = 2.5.1;
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -513,7 +513,7 @@
CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 37;
CURRENT_PROJECT_VERSION = 38;
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
DRIVERKIT_DEPLOYMENT_TARGET = 24.6;
ENABLE_PREVIEWS = YES;
@@ -535,7 +535,7 @@
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.6;
MARKETING_VERSION = 2.5.0;
MARKETING_VERSION = 2.5.1;
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -602,7 +602,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 37;
CURRENT_PROJECT_VERSION = 38;
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SessionStatusLive/Info.plist;
@@ -613,7 +613,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.5.0;
MARKETING_VERSION = 2.5.1;
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.SessionStatusLive;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -632,7 +632,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 37;
CURRENT_PROJECT_VERSION = 38;
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SessionStatusLive/Info.plist;
@@ -643,7 +643,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.5.0;
MARKETING_VERSION = 2.5.1;
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.SessionStatusLive;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;

View File

@@ -1,52 +1,104 @@
import SwiftUI
import UIKit
/// A native iOS camera picker presented from a hosting controller so it can
/// respect all supported interface orientations. Present with `.fullScreenCover()`.
struct CameraImagePicker: UIViewControllerRepresentable {
@Binding var isPresented: Bool
@Environment(\.dismiss) private var dismiss
let onImageCaptured: (UIImage) -> Void
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.sourceType = .camera
picker.cameraCaptureMode = .photo
picker.cameraDevice = .rear
picker.allowsEditing = false
return picker
func makeUIViewController(context: Context) -> CameraHostViewController {
let host = CameraHostViewController()
host.onImageCaptured = { image in
onImageCaptured(image)
dismiss()
}
host.onCancel = {
dismiss()
}
host.pickerDelegate = context.coordinator
context.coordinator.onImageCaptured = { image in
onImageCaptured(image)
dismiss()
}
context.coordinator.onCancel = {
dismiss()
}
return host
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
// Nothing here actually... Q_Q
func updateUIViewController(_ uiViewController: CameraHostViewController, context: Context) {
// No dynamic updates needed
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
Coordinator()
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let parent: CameraImagePicker
init(_ parent: CameraImagePicker) {
self.parent = parent
}
var onImageCaptured: ((UIImage) -> Void)?
var onCancel: (() -> Void)?
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
) {
picker.dismiss(animated: true) {
if let image = info[.originalImage] as? UIImage {
parent.onImageCaptured(image)
self.onImageCaptured?(image)
} else {
self.onCancel?()
}
}
parent.isPresented = false
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
parent.isPresented = false
picker.dismiss(animated: true) {
self.onCancel?()
}
}
}
}
// Extension to check camera availability
// MARK: - Hosting VC to own presentation/orientation
final class CameraHostViewController: UIViewController {
var onImageCaptured: ((UIImage) -> Void)?
var onCancel: (() -> Void)?
weak var pickerDelegate: (UIImagePickerControllerDelegate & UINavigationControllerDelegate)?
private var didPresent = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
presentIfNeeded()
}
private func presentIfNeeded() {
guard !didPresent, UIImagePickerController.isSourceTypeAvailable(.camera) else { return }
didPresent = true
let picker = UIImagePickerController()
picker.delegate = pickerDelegate
picker.sourceType = .camera
picker.cameraCaptureMode = .photo
picker.cameraDevice = .rear
picker.allowsEditing = false
picker.modalPresentationStyle = .fullScreen
present(picker, animated: true)
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
// Defer to app-supported orientations; returning .all allows rotation when permitted by the app
return .all
}
override var shouldAutorotate: Bool {
true
}
}
// MARK: - Camera Availability Check
extension CameraImagePicker {
static var isCameraAvailable: Bool {
UIImagePickerController.isSourceTypeAvailable(.camera)

View File

@@ -26,19 +26,19 @@ struct AddAttemptView: View {
enum SheetType: Identifiable {
case photoOptions
case camera
var id: Int {
switch self {
case .photoOptions: return 0
case .camera: return 1
}
}
}
@State private var activeSheet: SheetType?
@State private var showCamera = false
@State private var showPhotoPicker = false
@State private var isPhotoPickerActionPending = false
@State private var isCameraActionPending = false
private var activeProblems: [Problem] {
dataManager.activeProblems(forGym: gym.id)
@@ -110,6 +110,11 @@ struct AddAttemptView: View {
.sheet(
item: $activeSheet,
onDismiss: {
if isCameraActionPending {
showCamera = true
isCameraActionPending = false
return
}
if isPhotoPickerActionPending {
showPhotoPicker = true
isPhotoPickerActionPending = false
@@ -123,7 +128,8 @@ struct AddAttemptView: View {
imageData: $imageData,
maxImages: 5,
onCameraSelected: {
activeSheet = .camera
isCameraActionPending = true
activeSheet = nil
},
onPhotoLibrarySelected: {
isPhotoPickerActionPending = true
@@ -132,20 +138,16 @@ struct AddAttemptView: View {
activeSheet = nil
}
)
case .camera:
CameraImagePicker(
isPresented: Binding(
get: { activeSheet == .camera },
set: { if !$0 { activeSheet = nil } }
)
) { capturedImage in
}
}
.fullScreenCover(isPresented: $showCamera) {
CameraImagePicker { capturedImage in
if let jpegData = capturedImage.jpegData(compressionQuality: 0.8) {
imageData.append(jpegData)
}
}
}
}
}
@ViewBuilder
private func ProblemSelectionSection() -> some View {
@@ -778,19 +780,19 @@ struct EditAttemptView: View {
enum SheetType: Identifiable {
case photoOptions
case camera
var id: Int {
switch self {
case .photoOptions: return 0
case .camera: return 1
}
}
}
@State private var activeSheet: SheetType?
@State private var showCamera = false
@State private var showPhotoPicker = false
@State private var isPhotoPickerActionPending = false
@State private var isCameraActionPending = false
private var availableProblems: [Problem] {
guard let session = dataManager.session(withId: attempt.sessionId) else {
@@ -883,6 +885,11 @@ struct EditAttemptView: View {
.sheet(
item: $activeSheet,
onDismiss: {
if isCameraActionPending {
showCamera = true
isCameraActionPending = false
return
}
if isPhotoPickerActionPending {
showPhotoPicker = true
isPhotoPickerActionPending = false
@@ -896,7 +903,8 @@ struct EditAttemptView: View {
imageData: $imageData,
maxImages: 5,
onCameraSelected: {
activeSheet = .camera
isCameraActionPending = true
activeSheet = nil
},
onPhotoLibrarySelected: {
isPhotoPickerActionPending = true
@@ -905,20 +913,16 @@ struct EditAttemptView: View {
activeSheet = nil
}
)
case .camera:
CameraImagePicker(
isPresented: Binding(
get: { activeSheet == .camera },
set: { if !$0 { activeSheet = nil } }
)
) { capturedImage in
}
}
.fullScreenCover(isPresented: $showCamera) {
CameraImagePicker { capturedImage in
if let jpegData = capturedImage.jpegData(compressionQuality: 0.8) {
imageData.append(jpegData)
}
}
}
}
}
@ViewBuilder
private func ProblemSelectionSection() -> some View {

View File

@@ -25,19 +25,19 @@ struct AddEditProblemView: View {
@State private var isEditing = false
enum SheetType: Identifiable {
case photoOptions
case camera
var id: Int {
switch self {
case .photoOptions: return 0
case .camera: return 1
}
}
}
@State private var activeSheet: SheetType?
@State private var showCamera = false
@State private var showPhotoPicker = false
@State private var isPhotoPickerActionPending = false
@State private var isCameraActionPending = false
private var existingProblem: Problem? {
guard let problemId = problemId else { return nil }
@@ -120,6 +120,11 @@ struct AddEditProblemView: View {
.sheet(
item: $activeSheet,
onDismiss: {
if isCameraActionPending {
showCamera = true
isCameraActionPending = false
return
}
if isPhotoPickerActionPending {
showPhotoPicker = true
isPhotoPickerActionPending = false
@@ -133,7 +138,8 @@ struct AddEditProblemView: View {
imageData: $imageData,
maxImages: 5,
onCameraSelected: {
activeSheet = .camera
isCameraActionPending = true
activeSheet = nil
},
onPhotoLibrarySelected: {
isPhotoPickerActionPending = true
@@ -142,19 +148,15 @@ struct AddEditProblemView: View {
activeSheet = nil
}
)
case .camera:
CameraImagePicker(
isPresented: Binding(
get: { activeSheet == .camera },
set: { if !$0 { activeSheet = nil } }
)
) { capturedImage in
}
}
.fullScreenCover(isPresented: $showCamera) {
CameraImagePicker { capturedImage in
if let jpegData = capturedImage.jpegData(compressionQuality: 0.8) {
imageData.append(jpegData)
}
}
}
}
.photosPicker(
isPresented: $showPhotoPicker,
selection: $selectedPhotos,