You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS11后UIImagePickerController获取图片文件名真机崩溃求助

iOS11后真机图片上传崩溃问题排查与解决

这种iOS版本差异导致的真机/模拟器不一致问题确实挺闹心的,尤其是TestFlight测试看不到错误日志的话,定位起来更费劲。我帮你梳理下可能的原因和对应的解决办法,一步步来:

第一步:先拿到真机崩溃日志

要解决问题,首先得知道到底在哪崩的,你可以通过这两种方式获取崩溃日志:

  • 把iPhone连到Mac上,打开Xcode的Window > Devices and Simulators,选中你的设备,在Device Logs里找到对应APP的崩溃记录,导出后就能看到具体的崩溃栈信息(比如是空指针、权限问题还是文件访问失败)
  • 如果没法连Mac,让测试的用户在手机的设置 > 隐私与安全性 > 分析与改进 > 分析数据里找,文件名一般是[你的APP名称]-[日期].ips,找到后发给你就行

可能的崩溃原因及解决办法

1. 隐私权限缺失

iOS11之后苹果对照片访问权限的校验更严格了,要是Info.plist里没加对应的权限描述,真机上直接就会崩溃:

  • 如果是从相册选图,要添加NSPhotoLibraryUsageDescription,描述里得写清楚为啥要访问相册(比如“需要访问相册上传图片”)
  • 如果用到拍照功能,还要加NSCameraUsageDescription
  • 要是允许用户保存图片到相册,得加NSPhotoLibraryAddUsageDescription

注意:这些描述不能是空字符串,否则权限申请会直接被拒,甚至触发崩溃

2. 文件名获取的API兼容性问题

你提到尝试了多种获取文件名的方式,iOS11之后PHAsset的一些旧API可能失效了,推荐用这个靠谱的方式获取文件名:

// 针对PHAsset的文件名获取方法
func fetchFileName(from asset: PHAsset) -> String? {
    let resources = PHAssetResource.assetResources(for: asset)
    return resources.first?.originalFilename
}

之前的旧方法可能在真机上因为API变更返回nil,后续代码没做非空判断就直接用,导致空指针崩溃。记得在使用文件名前一定要加非空校验:

guard let fileName = fetchFileName(from: selectedAsset), !fileName.isEmpty else {
    // 这里弹个提示告诉用户无法获取文件名,别直接崩溃
    let alert = UIAlertController(title: "提示", message: "无法获取图片名称,请重试", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "确定", style: .default))
    present(alert, animated: true)
    return
}
// 接下来再处理文件名和图片的传递

3. 文件访问权限问题

iOS11之后沙盒的文件访问权限更严格,如果你是通过文件URL传递图片,要注意临时获取权限:

// 假设你拿到了安全作用域的URL
if url.startAccessingSecurityScopedResource() {
    defer {
        url.stopAccessingSecurityScopedResource() // 用完一定要释放权限
    }
    // 在这里读取文件内容,比如转成Data或者UIImage
    if let imageData = try? Data(contentsOf: url) {
        // 传递imageData和文件名到下一个控制器
    }
} else {
    // 权限获取失败,弹提示
}

模拟器因为沙盒限制没那么严,所以不会出问题,但真机上没做这一步就会崩溃。

4. 内存溢出问题

模拟器的内存比真机大很多,要是你直接传递高清原图,真机可能因为内存不足崩溃。可以先压缩图片再传递:

func compressImage(_ image: UIImage, maxFileSizeKB: CGFloat) -> Data? {
    var compressionQuality: CGFloat = 1.0
    guard var imageData = image.jpegData(compressionQuality: compressionQuality) else { return nil }
    
    while imageData.count > Int(maxFileSizeKB * 1024) && compressionQuality > 0.1 {
        compressionQuality -= 0.1
        if let compressedData = image.jpegData(compressionQuality: compressionQuality) {
            imageData = compressedData
        } else {
            break
        }
    }
    return imageData
}

比如把图片压缩到200KB以内,再把压缩后的Data和文件名一起传到下一个控制器,能有效避免内存崩溃。

先按这些步骤排查,尤其是先拿到崩溃日志,能精准定位问题。如果还是不行,把崩溃栈信息发出来再细化分析~

内容的提问来源于stack exchange,提问作者Aria

火山引擎 最新活动