SwiftUI中PhotosPicker相机图片倒置及导航标题不显示问题
问题1:PhotosPicker选取的图片显示倒置
原因
PhotosPicker返回的图片携带EXIF方向元数据,但SwiftUI的Image组件不会自动依据该元数据调整显示方向,导致图片按原始像素方向渲染,出现倒置。
解决方案
借助UIKit的UIImage修正图片方向,再转换为SwiftUI可用的图片:
- 给
UIImage添加自动修正方向的扩展:
extension UIImage { func fixedOrientation() -> UIImage { guard imageOrientation != .up else { return self } var transform = CGAffineTransform.identity switch imageOrientation { case .down, .downMirrored: transform = transform.translatedBy(x: size.width, y: size.height) transform = transform.rotated(by: .pi) case .left, .leftMirrored: transform = transform.translatedBy(x: size.width, y: 0) transform = transform.rotated(by: .pi / 2) case .right, .rightMirrored: transform = transform.translatedBy(x: 0, y: size.height) transform = transform.rotated(by: -.pi / 2) case .up, .upMirrored: break @unknown default: break } switch imageOrientation { case .upMirrored, .downMirrored: transform = transform.translatedBy(x: size.width, y: 0) transform = transform.scaledBy(x: -1, y: 1) case .leftMirrored, .rightMirrored: transform = transform.translatedBy(x: size.height, y: 0) transform = transform.scaledBy(x: -1, y: 1) case .up, .down, .left, .right: break @unknown default: break } if let cgImage = cgImage, let colorSpace = cgImage.colorSpace { let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: cgImage.bitmapInfo.rawValue) context?.concatenate(transform) switch imageOrientation { case .left, .leftMirrored, .right, .rightMirrored: context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.height, height: size.width)) default: context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) } if let correctedCGImage = context?.makeImage() { return UIImage(cgImage: correctedCGImage) } } return self } }
- 处理PhotosPicker返回的图片时应用该扩展:
假设你使用@State private var selectedItem: PhotosPickerItem?和@State private var selectedImage: Image?,修改加载逻辑:
if let item = selectedItem { Task { if let data = try? await item.loadTransferable(type: Data.self), let uiImage = UIImage(data: data)?.fixedOrientation() { selectedImage = Image(uiImage: uiImage) } } }
问题2:导航标题在模拟器/真机不显示(预览正常)
原因
通常是导航视图层级错误导致,常见场景:
- 未将需要显示标题的视图包裹在
NavigationStack(iOS 16+)或NavigationView(iOS 15及以下)内部 navigationTitle修饰符被添加到错误层级(比如直接加在NavigationStack上,而非其子视图)- 使用
TabView时,错误地将TabView放在NavigationStack内部,而非每个Tab子视图单独嵌套NavigationStack
解决方案
确保导航层级结构正确:
iOS 16+(使用NavigationStack)示例
struct ContentView: View { var body: some View { NavigationStack { VStack { PhotosPicker("选择图片", selection: $selectedItem, matching: .images) if let image = selectedImage { image .resizable() .scaledToFit() } } .navigationTitle("图片选择器") // 必须添加在NavigationStack的直接子视图上 .navigationBarTitleDisplayMode(.inline) // 可选,控制标题显示模式 } } @State private var selectedItem: PhotosPickerItem? @State private var selectedImage: Image? }
iOS 15及以下(使用NavigationView)示例
struct ContentView: View { var body: some View { NavigationView { VStack { PhotosPicker("选择图片", selection: $selectedItem, matching: .images) if let image = selectedImage { image .resizable() .scaledToFit() } } .navigationTitle("图片选择器") } } @State private var selectedItem: PhotosPickerItem? @State private var selectedImage: Image? }
TabView场景注意事项
每个Tab的内容需单独嵌套NavigationStack:
struct ContentView: View { var body: some View { TabView { NavigationStack { FirstTabView() .navigationTitle("首页") } .tabItem { Label("首页", systemImage: "house") } NavigationStack { SecondTabView() .navigationTitle("图片") } .tabItem { Label("图片", systemImage: "photo") } } } }
内容的提问来源于stack exchange,提问作者NodlesView
相关产品推荐
相关产品推荐

