【问题标题】:iOS10 UIImageWriteToSavedPhotosAlbum TCC__CRASHING_DUE_TO_PRIVACY_VIOLATIONiOS10 UIImageWriteToSavedPhotosAlbum TCC__CRASHING_DUE_TO_PRIVACY_VIOLATION
【发布时间】:2018-06-11 01:28:20
【问题描述】:

我想在 iOS 10 中创建并保存UIImage。我可以通过拍摄UIView 快照或使用UIGraphicsImageRenderer 来创建它(如下所示)。

- (UIView *)createIcon
{
    UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake(200, 200)];

    UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull context) {
        [[UIColor darkGrayColor] setStroke];
        [context strokeRect:renderer.format.bounds];
        [[UIColor colorWithRed:158/255.0 green:215/255.0 blue:245/255.0 alpha:0.5] setFill];
        [context fillRect:CGRectMake(30, 30, 140, 140)];
        [[UIColor colorWithRed:243/255.0 green:122/255.0 blue:216/255.0 alpha:0.3] setFill];
        CGContextFillEllipseInRect(context.CGContext, CGRectMake(30, 30, 120, 120));
    }];

    UIImageView *showImage = [[UIImageView alloc] initWithImage:image];

    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);

    return showImage;
}

但是,当我尝试使用 UIImageWriteToSavedPhotosAlbum 通过包含倒数第二个语句来保存时,Xcode 进入调试并显示以下错误报告

(lldb) bt
* thread #5, queue = 'com.apple.root.default-qos', stop reason = signal SIGABRT
  * frame #0: 0x00000001101611a6 libsystem_kernel.dylib`__abort_with_payload + 10
    frame #1: 0x000000011015b86e libsystem_kernel.dylib`abort_with_payload_wrapper_internal + 89
    frame #2: 0x000000011015b89b libsystem_kernel.dylib`abort_with_payload + 9
    frame #3: 0x0000000108ab3af7 TCC`__CRASHING_DUE_TO_PRIVACY_VIOLATION__ + 182
    frame #4: 0x0000000108ab3a41 TCC`__TCCAccessRequest_block_invoke.77 + 665
    frame #5: 0x0000000108ab7273 TCC`__tccd_send_block_invoke + 274

所以即使我没有使用相机创建图像,将其保存到相册也是侵犯隐私(嘶嘶,嘘)!我知道可以设置privacy keys in the plist,但有更明智的选择吗?

澄清

我希望在应用程序中使用渲染图像(或视图快照)作为资产。那么除了还有更明智的选择吗?

也许我应该问

是否有其他地方可以保存图像(即保存为 PNG 或 JPG 文件),因此隐私不是问题?

【问题讨论】:

    标签: ios objective-c iphone uiimage plist


    【解决方案1】:

    您可以将任何 UIImage 保存到相册,但首先您必须征得用户的许可,因为这确实是一个隐私问题。如果他们不授予您访问权限,那么您根本无法保存图像。

    最明智的做法是将所需的隐私密钥添加到 info.plist。

    这是 info.plist xml 定义,尽管在属性列表中添加键更容易:

    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>Message requesting the ability to add to the photo library</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>Message requestion the ability to access the photo library</string>
    

    如果您添加这些,那么当您第一次尝试访问或添加到照片库时,将显示一个弹出窗口,其中包含您的消息,允许用户决定他们是否希望您的应用具有访问权限。

    将它放在 info.plist 文件中的一个很好的理由是,所有访问请求都位于一个易于查看的位置,而不是项目中的某个随机位置。

    编辑

    以下是如何将图像保存在不会引发隐私问题的文档中:

    NSData *imageData = UIImagePNGRepresentation(image);
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.png"]; //Add the file name
    [imageData writeToFile:filePath atomically:YES]; //Write the file
    

    如果您想要 jpg 而不是 png,请改用:

    NSData *imageData = UIImageJPEGRepresentation(image, 0.9); // Use whatever compression ratio you want instead of 0.9
    

    【讨论】:

    • 添加NSPhotoLibraryAddUsageDescription 不是“最明智的方法”,而是唯一的方法。如果您想使用UIImageWriteToSavedPhotosAlbum,则必须添加该密钥。
    • 当我说最明智的方法时,我应该说得更清楚,我的意思是你应该采用的方法。我知道对于某些访问(例如访问图像库),您可以在代码中请求它而不是更新 info.plist,但我不确定添加到图像库是否如此。
    • @rmaddy 和 UpholderOfTruth,澄清一下。我希望将渲染的图像(或视图快照)用作应用程序中的资产。这就是为什么我质疑为什么存在隐私问题。
    • @Greg iOS 一直要求应用程序获得从用户照片库中读取的权限。现在,从 iOS 11 开始,您还需要(不同的)权限才能添加到用户的照片库。
    • @Greg 如果只是将图像保存在照片库以外的地方,我将更新我的答案以显示如何将图像保存在应用程序的文档文件夹中。
    【解决方案2】:

    遵循此代码:

    #import <Photos/Photos.h>
    

    pragma mark - 保存图片到相册

    - (void)addImageToCameraRoll:(UIImage *)image {
    NSString *albumName = @"Your app name";
    
    void (^saveBlock)(PHAssetCollection *assetCollection) = ^void(PHAssetCollection *assetCollection) {
        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
            PHAssetChangeRequest *assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
            PHAssetCollectionChangeRequest *assetCollectionChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
            [assetCollectionChangeRequest addAssets:@[[assetChangeRequest placeholderForCreatedAsset]]];
    
        } completionHandler:^(BOOL success, NSError *error) {
            if (!success) {
                NSLog(@"Error creating asset: %@", error);
            }
        }];
    };
    
    PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
    fetchOptions.predicate = [NSPredicate predicateWithFormat:@"localizedTitle = %@", albumName];
    PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:fetchOptions];
    if (fetchResult.count > 0) {
        saveBlock(fetchResult.firstObject);
    } else {
        __block PHObjectPlaceholder *albumPlaceholder;
        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
            PHAssetCollectionChangeRequest *changeRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:albumName];
            albumPlaceholder = changeRequest.placeholderForCreatedAssetCollection;
    
        } completionHandler:^(BOOL success, NSError *error) {
            if (success) {
                PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[albumPlaceholder.localIdentifier] options:nil];
                if (fetchResult.count > 0) {
                    saveBlock(fetchResult.firstObject);
                }
            } else {
                NSLog(@"Error creating album: %@", error);
            }
        }];
    }
    }
    

    【讨论】:

    • 卡尔佩什,谢谢。可以将图像资源添加到共享照片库以外的文件夹中吗? (请参阅我对上述 cmets 的说明)
    • 是的,您共享了照片库。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2015-02-07
    相关资源
    最近更新 更多