You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中PhotosPicker相机图片倒置及导航标题不显示问题

问题1:PhotosPicker选取的图片显示倒置

原因

PhotosPicker返回的图片携带EXIF方向元数据,但SwiftUI的Image组件不会自动依据该元数据调整显示方向,导致图片按原始像素方向渲染,出现倒置。

解决方案

借助UIKit的UIImage修正图片方向,再转换为SwiftUI可用的图片:

  1. 给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
    }
}
  1. 处理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 06:40:21