partial void RemoveAsset(NSObject sender) { Action <bool, NSError> completion = (success, error) => { if (success) { PHPhotoLibrary.SharedPhotoLibrary.UnregisterChangeObserver(this); DispatchQueue.MainQueue.DispatchSync(() => NavigationController.PopViewController(true)); } else { Console.WriteLine($"can't remove asset: {error.LocalizedDescription}"); } }; if (AssetCollection != null) { // Remove asset from album PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetCollectionChangeRequest.ChangeRequest(AssetCollection); request.RemoveAssets(new PHObject [] { Asset }); }, completion); } else { // Delete asset from library PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => PHAssetChangeRequest.DeleteAssets(new [] { Asset }), completion); } }
partial void TrashButtonClickHandler(NSObject sender) { Action <bool, NSError> completionHandler = (success, error) => { if (success) { DispatchQueue.MainQueue.DispatchAsync(() => NavigationController.PopViewController(true) ); } else { Console.WriteLine(error.LocalizedDescription); } }; if (AssetCollection != null) { // Remove asset from album PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var changeRequest = PHAssetCollectionChangeRequest.ChangeRequest(AssetCollection); changeRequest.RemoveAssets(new PHObject[] { Asset }); }, completionHandler); } else { // Delete asset from library PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => PHAssetChangeRequest.DeleteAssets(new [] { Asset }), completionHandler); } }
void SaveToAlbum(string watermarkedPath) { var lib = PHPhotoLibrary.SharedPhotoLibrary; lib.PerformChanges(() => { var album = PHAssetCollection.FetchAssetCollections(new[] { Xamarin.Essentials.Preferences.Get("iOSAlbumIdentifier", string.Empty) }, null)?.firstObject as PHAssetCollection; var collectionRequest = PHAssetCollectionChangeRequest.ChangeRequest(album); if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0)) { var options = new PHAssetResourceCreationOptions { ShouldMoveFile = true }; var changeRequest = PHAssetCreationRequest.CreationRequestForAsset(); changeRequest.AddResource(PHAssetResourceType.Video, NSUrl.FromString(watermarkedPath), options); collectionRequest.AddAssets(new[] { changeRequest.PlaceholderForCreatedAsset }); } else { var changeRequest2 = PHAssetChangeRequest.FromVideo(NSUrl.FromString(watermarkedPath)); collectionRequest.AddAssets(new[] { changeRequest2.PlaceholderForCreatedAsset }); } RetrieveLastAssetSaved(); }, (success, err) => { }); }
void OnApplyFilter(object sender, EventArgs e) { Asset.RequestContentEditingInput(new PHContentEditingInputRequestOptions(), (input, options) => { var image = CIImage.FromUrl(input.FullSizeImageUrl); image = image.CreateWithOrientation(input.FullSizeImageOrientation); var updatedPhoto = new CIPhotoEffectNoir { Image = image }; var ciContext = CIContext.FromOptions(null); var output = updatedPhoto.OutputImage; // Get the upated image var uiImage = UIImage.FromImage(ciContext.CreateCGImage(output, output.Extent)); TheImage.Image = uiImage; // Save the image data to a PHContentEditingOutput instance var editingOutput = new PHContentEditingOutput(input); NSError error; var data = uiImage.AsJPEG(); data.Save(editingOutput.RenderedContentUrl, false, out error); editingOutput.AdjustmentData = new PHAdjustmentData();; // Request to publish the changes form the editing output back to the photo library PHPhotoLibrary.SharedPhotoLibrary.PerformChanges( () => { PHAssetChangeRequest request = PHAssetChangeRequest.ChangeRequest(Asset); request.ContentEditingOutput = editingOutput; }, (ok, err) => Console.WriteLine("Photo updated : {0}, {1}", ok, err)); }); }
public Task <OperationResult <string> > SaveVideo(string videoPath) { var tcs = new TaskCompletionSource <OperationResult <string> >(); if (string.IsNullOrEmpty(videoPath) || !File.Exists(videoPath)) { tcs.SetResult(OperationResult <string> .AsFailure("Invalid video file path specified")); return(tcs.Task); } var url = NSUrl.CreateFileUrl(videoPath, false, null); string localId = string.Empty; PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.FromVideo(url); localId = request?.PlaceholderForCreatedAsset?.LocalIdentifier; }, (bool success, NSError error) => { if (!success && error != null) { tcs.SetResult(OperationResult <string> .AsFailure(error.LocalizedDescription)); } else { tcs.SetResult(OperationResult <string> .AsSuccess(localId)); } }); return(tcs.Task); }
partial void AddAsset(NSObject sender) { var rnd = new Random(); // Create a random dummy image. var size = (rnd.Next(0, 2) == 0) ? new CGSize(400f, 300f) : new CGSize(300f, 400f); var renderer = new UIGraphicsImageRenderer(size); var image = renderer.CreateImage(context => { UIColor.FromHSBA((float)rnd.NextDouble(), 1, 1, 1).SetFill(); context.FillRect(context.Format.Bounds); }); // Add it to the photo library PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { PHAssetChangeRequest creationRequest = PHAssetChangeRequest.FromImage(image); if (AssetCollection != null) { var addAssetRequest = PHAssetCollectionChangeRequest.ChangeRequest(AssetCollection); addAssetRequest.AddAssets(new PHObject [] { creationRequest.PlaceholderForCreatedAsset }); } }, (success, error) => { if (!success) { Console.WriteLine(error.LocalizedDescription); } }); }
partial void AddButtonClickHandler(NSObject sender) { // Create a random dummy image. var rect = new Random().Next(0, 2) == 0 ? new CGRect(0f, 0f, 400f, 300f) : new CGRect(0f, 0f, 300f, 400f); UIGraphics.BeginImageContextWithOptions(rect.Size, false, 1f); UIColor.FromHSBA(new Random().Next(0, 100) / 100f, 1f, 1f, 1f).SetFill(); UIGraphics.RectFillUsingBlendMode(rect, CGBlendMode.Normal); UIImage image = UIGraphics.GetImageFromCurrentImageContext(); UIGraphics.EndImageContext(); // Add it to the photo library PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { PHAssetChangeRequest assetChangeRequest = PHAssetChangeRequest.FromImage(image); if (AssetCollection != null) { PHAssetCollectionChangeRequest assetCollectionChangeRequest = PHAssetCollectionChangeRequest.ChangeRequest(AssetCollection); assetCollectionChangeRequest.AddAssets(new PHObject[] { assetChangeRequest.PlaceholderForCreatedAsset }); } }, (success, error) => { if (!success) { Console.WriteLine(error.LocalizedDescription); } }); }
public static bool SaveImageToGalery(string imagePath, string albumName) { var saved = true; PHAssetCollection customAlbum = null; if (!string.IsNullOrEmpty(albumName)) { customAlbum = FindOrCreateAlbum(albumName); if (customAlbum == null) { return(false); } } PHPhotoLibrary.SharedPhotoLibrary.PerformChanges( () => { var assetRequest = PHAssetChangeRequest.FromImage(NSUrl.FromFilename(imagePath)); if (customAlbum != null) { var albumRequest = PHAssetCollectionChangeRequest.ChangeRequest(customAlbum); albumRequest?.AddAssets(new[] { assetRequest.PlaceholderForCreatedAsset }); } }, (success, error) => { if (!success) { Console.WriteLine(error); saved = success; } } ); return(saved); }
void ApplyNoirFilter(object sender, EventArgs e) { Asset.RequestContentEditingInput(new PHContentEditingInputRequestOptions(), (input, options) => { // perform the editing operation, which applies a noir filter in this case var image = CIImage.FromUrl(input.FullSizeImageUrl); image = image.CreateWithOrientation((CIImageOrientation)input.FullSizeImageOrientation); var noir = new CIPhotoEffectNoir { Image = image }; var ciContext = CIContext.FromOptions(null); var output = noir.OutputImage; var uiImage = UIImage.FromImage(ciContext.CreateCGImage(output, output.Extent)); imageView.Image = uiImage; // save the filtered image data to a PHContentEditingOutput instance var editingOutput = new PHContentEditingOutput(input); var adjustmentData = new PHAdjustmentData(); var data = uiImage.AsJPEG(); NSError error; data.Save(editingOutput.RenderedContentUrl, false, out error); editingOutput.AdjustmentData = adjustmentData; // make a change request to publish the changes form the editing output PHPhotoLibrary.SharedPhotoLibrary.PerformChanges( () => { PHAssetChangeRequest request = PHAssetChangeRequest.ChangeRequest(Asset); request.ContentEditingOutput = editingOutput; }, (ok, err) => Console.WriteLine("photo updated successfully: {0}", ok)); }); }
static async Task PlatformSaveAsync(MediaFileType type, string filePath) { using var fileUri = new NSUrl(filePath); await PhotoLibraryPerformChanges(() => { using var request = type == MediaFileType.Video ? PHAssetChangeRequest.FromVideo(fileUri) : PHAssetChangeRequest.FromImage(fileUri); }); }
void RevertAsset(UIAlertAction action) { PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.ChangeRequest(Asset); request.RevertAssetContentToOriginal(); }, (success, error) => { if (!success) { Console.WriteLine($"can't revert asset: {error.LocalizedDescription}"); } }); }
void RevertToOriginal() { PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.ChangeRequest(Asset); request.RevertAssetContentToOriginal(); }, (success, error) => { if (!success) { Console.WriteLine("Error: {0}", error.LocalizedDescription); } }); }
void ToggleFavoriteState() { PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.ChangeRequest(Asset); request.Favorite = !Asset.Favorite; }, (success, error) => { if (!success) { Console.WriteLine("Error: {0}", error.LocalizedDescription); } }); }
void ApplyFilter(CIFilter filter) { // Prepare the options to pass when requesting to edit the image. var options = new PHContentEditingInputRequestOptions(); options.SetCanHandleAdjustmentDataHandler(adjustmentData => { bool result = false; InvokeOnMainThread(() => { result = adjustmentData.FormatIdentifier == AdjustmentFormatIdentifier && adjustmentData.FormatVersion == "1.0"; }); return(result); }); Asset.RequestContentEditingInput(options, (contentEditingInput, requestStatusInfo) => { // Create a CIImage from the full image representation. var url = contentEditingInput.FullSizeImageUrl; int orientation = (int)contentEditingInput.FullSizeImageOrientation; var inputImage = CIImage.FromUrl(url); inputImage = inputImage.CreateWithOrientation((CIImageOrientation)orientation); // Create the filter to apply. filter.SetDefaults(); filter.Image = inputImage; // Apply the filter. CIImage outputImage = filter.OutputImage; // Create a PHAdjustmentData object that describes the filter that was applied. var adjustmentData = new PHAdjustmentData( AdjustmentFormatIdentifier, "1.0", NSData.FromString(filter.Name, NSStringEncoding.UTF8) ); var contentEditingOutput = new PHContentEditingOutput(contentEditingInput); NSData jpegData = outputImage.GetJpegRepresentation(0.9f); jpegData.Save(contentEditingOutput.RenderedContentUrl, true); contentEditingOutput.AdjustmentData = adjustmentData; // Ask the shared PHPhotoLinrary to perform the changes. PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.ChangeRequest(Asset); request.ContentEditingOutput = contentEditingOutput; }, (success, error) => { if (!success) { Console.WriteLine("Error: {0}", error.LocalizedDescription); } }); }); }
partial void ToggleFavorite(UIBarButtonItem sender) { PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.ChangeRequest(Asset); request.Favorite = !Asset.Favorite; }, (success, error) => { // Original sample updates FavoriteButton.Title here // but this doesn't work, because you have to wait PhotoLibraryDidChange notification. // At this point you just know is there any issue or everthing is ok. if (!success) { Console.WriteLine($"can't set favorite: {error.LocalizedDescription}"); } }); }
private void OnExportCompleted(AVAssetExportSession session) { this.exportProgressView.Hidden = true; this.currentTimeLabel.Hidden = false; var outputURL = session.OutputUrl; this.progressTimer.Invalidate(); this.progressTimer.Dispose(); this.progressTimer = null; if (session.Status != AVAssetExportSessionStatus.Completed) { Console.WriteLine($"exportSession error:{session.Error}"); this.ReportError(session.Error); } else { this.exportProgressView.Progress = 1f; // Save the exported movie to the camera roll PHPhotoLibrary.RequestAuthorization((status) => { if (status == PHAuthorizationStatus.Authorized) { PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => PHAssetChangeRequest.FromVideo(outputURL), (successfully, error) => { if (error != null) { Console.WriteLine($"writeVideoToAssestsLibrary failed: {error}"); this.ReportError(error); } base.InvokeOnMainThread(() => { this.playPauseButton.Enabled = true; this.transitionButton.Enabled = true; this.scrubber.Enabled = true; this.exportButton.Enabled = true; }); }); } }); } }
public static bool DeleteImagesFromGallery(string[] localIds) { var images = PHAsset.FetchAssetsUsingLocalIdentifiers(localIds, new PHFetchOptions()); var assets = images.Select(a => (PHAsset)a).ToArray(); var success = PHPhotoLibrary.SharedPhotoLibrary.PerformChangesAndWait ( () => { PHAssetChangeRequest.DeleteAssets(assets); }, out var error ); if (error != null) { Console.WriteLine(error); } return(success); }
public static async Task <bool> SaveToLibrary(string path, string albumName) { var source = new TaskCompletionSource <bool>(); PHAssetCollection album = await GetAlbum(albumName); PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var assetReq = PHAssetChangeRequest.FromImage(NSUrl.CreateFileUrl(path, null)); var albumReq = PHAssetCollectionChangeRequest.ChangeRequest(album); var placeholder = assetReq.PlaceholderForCreatedAsset; albumReq.AddAssets(new[] { (PHObject)placeholder }); }, (success, err) => { Debug.WriteLine($"SaveToLibrary: {success}"); source.SetResult(success); }); return(await source.Task); }
void DeleteOriginal(NSUrl url) { var assets = PHAsset.FetchAssets(new NSUrl[] { url }, null); var asset = (PHAsset)assets.firstObject; var sharedLib = PHPhotoLibrary.SharedPhotoLibrary; sharedLib.PerformChanges(() => { var req = asset.CanPerformEditOperation(PHAssetEditOperation.Delete); if (req) { PHAssetChangeRequest.DeleteAssets(new PHAsset[] { asset }); } }, (success, err) => { if (success) { Debug.WriteLine("successfully deleted original"); } }); }
private async void SnapStillImage() { // if ((m_videoDevice != null) && (m_stillImageOutput != null)) { if (m_videoDevice.HasFlash && m_videoDevice.IsFlashModeSupported(AVCaptureFlashMode.Auto)) { NSError error; if (m_videoDevice.LockForConfiguration(out error)) { m_videoDevice.FlashMode = AVCaptureFlashMode.Auto; m_videoDevice.UnlockForConfiguration(); } } AVCaptureConnection connection = m_stillImageOutput.ConnectionFromMediaType(AVMediaType.Video); var imageDataSampleBuffer = await m_stillImageOutput.CaptureStillImageTaskAsync(connection); //获得当前帧的压缩图像 var imageData = AVCaptureStillImageOutput.JpegStillToNSData(imageDataSampleBuffer); //得到当前帧压缩图像的图像数据... //RequestAuthorization(handler), handler是用户与权限对话框交互后,执行的动作。 PHPhotoLibrary.RequestAuthorization(status => { if (status == PHAuthorizationStatus.Authorized) { // 若用户授权了 // To preserve the metadata, we create an asset from the JPEG NSData representation. // Note that creating an asset from a UIImage discards the metadata. // In iOS 9, we can use AddResource method on PHAssetCreationRequest class. // In iOS 8, we save the image to a temporary file and use +[PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:]. if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0)) { //PHPhotoLibrary.SharedPhotoLibrary 返回的是一个(共享)图片库对象 //PerformChanges (changeHandler, completionHandler) changeHandler 以及 completionHandler 是一个lambda PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetCreationRequest.CreationRequestForAsset(); request.AddResource(PHAssetResourceType.Photo, imageData, null); //保存当前照片 }, (success, err) => { if (!success) { Console.WriteLine("Error occurred while saving image to photo library: {0}", err); } }); } else { //用户没有授权 string outputFileName = NSProcessInfo.ProcessInfo.GloballyUniqueString; string tmpDir = Path.GetTempPath(); string outputFilePath = Path.Combine(tmpDir, outputFileName); string outputFilePath2 = Path.ChangeExtension(outputFilePath, "jpg"); NSUrl temporaryFileUrl = new NSUrl(outputFilePath2, false); PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { NSError error = null; if (imageData.Save(temporaryFileUrl, NSDataWritingOptions.Atomic, out error)) { PHAssetChangeRequest.FromImage(temporaryFileUrl); } else { Console.WriteLine("Error occured while writing image data to a temporary file: {0}", error); } }, (success, error) => { if (!success) { Console.WriteLine("Error occurred while saving image to photo library: {0}", error); } // Delete the temporary file. NSError deleteError; NSFileManager.DefaultManager.Remove(temporaryFileUrl, out deleteError); }); } } }); } }
void ApplyFilter(CIFilter filter) { // Set up a handler to make sure we can handle prior edits. var options = new PHContentEditingInputRequestOptions(); options.CanHandleAdjustmentData = (adjustmentData => { return(adjustmentData.FormatIdentifier == formatIdentifier && adjustmentData.FormatVersion == formatVersion); }); // Prepare for editing. Asset.RequestContentEditingInput(options, (input, requestStatusInfo) => { if (input == null) { throw new InvalidProgramException($"can't get content editing input: {requestStatusInfo}"); } // This handler gets called on the main thread; dispatch to a background queue for processing. DispatchQueue.GetGlobalQueue(DispatchQueuePriority.Default).DispatchAsync(() => { // Create a PHAdjustmentData object that describes the filter that was applied. var adjustmentData = new PHAdjustmentData( formatIdentifier, formatVersion, NSData.FromString(filter.Name, NSStringEncoding.UTF8)); // NOTE: // This app's filter UI is fire-and-forget. That is, the user picks a filter, // and the app applies it and outputs the saved asset immediately. There's // no UI state for having chosen but not yet committed an edit. This means // there's no role for reading adjustment data -- you do that to resume // in-progress edits, and this sample app has no notion of "in-progress". // // However, it's still good to write adjustment data so that potential future // versions of the app (or other apps that understand our adjustement data // format) could make use of it. // Create content editing output, write the adjustment data. var output = new PHContentEditingOutput(input) { AdjustmentData = adjustmentData }; // Select a filtering function for the asset's media type. Action <CIFilter, PHContentEditingInput, PHContentEditingOutput, Action> applyFunc; if (Asset.MediaSubtypes.HasFlag(PHAssetMediaSubtype.PhotoLive)) { applyFunc = ApplyLivePhotoFilter; } else if (Asset.MediaType == PHAssetMediaType.Image) { applyFunc = ApplyPhotoFilter; } else { applyFunc = ApplyVideoFilter; } // Apply the filter. applyFunc(filter, input, output, () => { // When rendering is done, commit the edit to the Photos library. PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.ChangeRequest(Asset); request.ContentEditingOutput = output; }, (success, error) => { if (!success) { Console.WriteLine($"can't edit asset: {error.LocalizedDescription}"); } }); }); }); }); }
void SnapStillImage(CameraViewController sender) { SessionQueue.DispatchAsync(async() => { AVCaptureConnection connection = StillImageOutput.ConnectionFromMediaType(AVMediaType.Video); var previewLayer = (AVCaptureVideoPreviewLayer)PreviewView.Layer; // Update the orientation on the still image output video connection before capturing. connection.VideoOrientation = previewLayer.Connection.VideoOrientation; // Flash set to Auto for Still Capture. SetFlashModeForDevice(AVCaptureFlashMode.Auto, VideoDeviceInput.Device); // Capture a still image. try { var imageDataSampleBuffer = await StillImageOutput.CaptureStillImageTaskAsync(connection); // The sample buffer is not retained. Create image data before saving the still image to the photo library asynchronously. NSData imageData = AVCaptureStillImageOutput.JpegStillToNSData(imageDataSampleBuffer); PHPhotoLibrary.RequestAuthorization(status => { if (status == PHAuthorizationStatus.Authorized) { // To preserve the metadata, we create an asset from the JPEG NSData representation. // Note that creating an asset from a UIImage discards the metadata. // In iOS 9, we can use AddResource method on PHAssetCreationRequest class. // In iOS 8, we save the image to a temporary file and use +[PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:]. if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0)) { PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetCreationRequest.CreationRequestForAsset(); request.AddResource(PHAssetResourceType.Photo, imageData, null); }, (success, err) => { if (!success) { Console.WriteLine("Error occurred while saving image to photo library: {0}", err); } }); } else { var temporaryFileUrl = new NSUrl(GetTmpFilePath("jpg"), false); PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { NSError error = null; if (imageData.Save(temporaryFileUrl, NSDataWritingOptions.Atomic, out error)) { PHAssetChangeRequest.FromImage(temporaryFileUrl); } else { Console.WriteLine("Error occured while writing image data to a temporary file: {0}", error); } }, (success, error) => { if (!success) { Console.WriteLine("Error occurred while saving image to photo library: {0}", error); } // Delete the temporary file. NSError deleteError; NSFileManager.DefaultManager.Remove(temporaryFileUrl, out deleteError); }); } } }); } catch (NSErrorException ex) { Console.WriteLine("Could not capture still image: {0}", ex.Error); } }); }
public void FinishedRecording(AVCaptureFileOutput captureOutput, NSUrl outputFileUrl, NSObject[] connections, NSError error) { // Note that currentBackgroundRecordingID is used to end the background task associated with this recording. // This allows a new recording to be started, associated with a new UIBackgroundTaskIdentifier, once the movie file output's isRecording property // is back to NO — which happens sometime after this method returns. // Note: Since we use a unique file path for each recording, a new recording will not overwrite a recording currently being saved. var currentBackgroundRecordingID = backgroundRecordingID; backgroundRecordingID = -1; Action cleanup = () => { NSError err; NSFileManager.DefaultManager.Remove(outputFileUrl, out err); if (currentBackgroundRecordingID != -1) { UIApplication.SharedApplication.EndBackgroundTask(currentBackgroundRecordingID); } }; bool success = true; if (error != null) { Console.WriteLine("Movie file finishing error: {0}", error); success = ((NSNumber)error.UserInfo [AVErrorKeys.RecordingSuccessfullyFinished]).BoolValue; } if (!success) { cleanup(); return; } // Check authorization status. PHPhotoLibrary.RequestAuthorization(status => { if (status == PHAuthorizationStatus.Authorized) { // Save the movie file to the photo library and cleanup. PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { // In iOS 9 and later, it's possible to move the file into the photo library without duplicating the file data. // This avoids using double the disk space during save, which can make a difference on devices with limited free disk space. if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0)) { var options = new PHAssetResourceCreationOptions { ShouldMoveFile = true }; var changeRequest = PHAssetCreationRequest.CreationRequestForAsset(); changeRequest.AddResource(PHAssetResourceType.Video, outputFileUrl, options); } else { PHAssetChangeRequest.FromVideo(outputFileUrl); } }, (success2, error2) => { if (!success2) { Console.WriteLine("Could not save movie to photo library: {0}", error2); } cleanup(); }); } else { cleanup(); } }); }
public async Task <string> ImageSave(MemoryStream stream, bool compatibleMode, string fileName = null) { NSError error = null; //虽然对于iOS没有这两个权限,但要保证方法异步,所以还是保留下来了 await Permissions.RequestAsync <Permissions.StorageWrite>(); await Permissions.RequestAsync <Permissions.StorageRead>(); //判断相册是否存在,不存在就创建 PHAssetCollection appAlbum = null; PHFetchResult albums = PHAssetCollection.FetchAssetCollections(PHAssetCollectionType.Album, PHAssetCollectionSubtype.Any, null); foreach (PHAssetCollection album in albums) { if (album.LocalizedTitle == albumName) { appAlbum = album; } } if (appAlbum == null) //相册不存在,新建 { string[] albumID = new string[1]; PHPhotoLibrary.SharedPhotoLibrary.PerformChangesAndWait(() => { albumID[0] = PHAssetCollectionChangeRequest.CreateAssetCollection(albumName).PlaceholderForCreatedAssetCollection.LocalIdentifier; }, out error); appAlbum = PHAssetCollection.FetchAssetCollections(albumID, null)[0] as PHAssetCollection; } //获取路径及名称 string documentsPath; documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); if (fileName == null || fileName == "") { fileName = "Tank_" + DateTime.Now.ToLocalTime().ToString("yyyyMMdd_HHmmss") + ".png"; } string path = Path.Combine(documentsPath, fileName); //保存 FileStream photoTankFile = new FileStream(path, FileMode.Create); byte[] photoTank = stream.ToArray(); photoTankFile.Write(photoTank, 0, photoTank.Length); photoTankFile.Flush(); photoTankFile.Close(); //如果是图片或视频,就添加到相册里 string MimeType = MimeUtility.GetMimeMapping(path); if (MimeType.IndexOf("image") != -1 || MimeType.IndexOf("video") != -1) { string[] assetID = new string[1]; PHPhotoLibrary.SharedPhotoLibrary.PerformChangesAndWait(() => { if (MimeType.IndexOf("image") != -1) { assetID[0] = PHAssetChangeRequest.FromImage(new NSUrl(path, true)).PlaceholderForCreatedAsset.LocalIdentifier; } if (MimeType.IndexOf("video") != -1) { assetID[0] = PHAssetChangeRequest.FromVideo(new NSUrl(path, true)).PlaceholderForCreatedAsset.LocalIdentifier; } }, out error); PHAsset asset = PHAsset.FetchAssetsUsingLocalIdentifiers(assetID, null)[0] as PHAsset; PHObject[] objs = { asset }; PHPhotoLibrary.SharedPhotoLibrary.PerformChangesAndWait(() => { PHAssetCollectionChangeRequest collectionChangeRequest = PHAssetCollectionChangeRequest.ChangeRequest(appAlbum); collectionChangeRequest.InsertAssets(objs, new NSIndexSet(0)); }, out error); } return(Path.Combine(fileName)); }
void UpdateToolbars() { // Enable editing buttons if the asset can be edited. EditButton.Enabled = Asset.CanPerformEditOperation(PHAssetEditOperation.Content); FavoriteButton.Enabled = Asset.CanPerformEditOperation(PHAssetEditOperation.Properties); FavoriteButton.Title = Asset.Favorite ? "♥︎" : "♡"; // Enable the trash button if the asset can be deleted. if (AssetCollection != null) { TrashButton.Enabled = AssetCollection.CanPerformEditOperation(PHCollectionEditOperation.RemoveContent); } else { TrashButton.Enabled = Asset.CanPerformEditOperation(PHAssetEditOperation.Delete); } // Set the appropriate toolbarItems based on the mediaType of the asset. if (Asset.MediaType == PHAssetMediaType.Video) { #if __TVOS__ NavigationItem.LeftBarButtonItems = new UIBarButtonItem[] { PlayButton, FavoriteButton, TrashButton }; #elif __IOS__ ToolbarItems = new UIBarButtonItem[] { FavoriteButton, Space, PlayButton, Space, TrashButton }; if (NavigationController != null) { NavigationController.ToolbarHidden = false; } #endif } else { #if __TVOS__ // In tvOS, PHLivePhotoView doesn't do playback gestures, // so add a play button for Live Photos. if (Asset.PlaybackStyle == PHAssetPlaybackStyle.LivePhoto) { NavigationItem.LeftBarButtonItems = new UIBarButtonItem[] { LivePhotoPlayButton, FavoriteButton, TrashButton } } ; else { NavigationItem.LeftBarButtonItems = new UIBarButtonItem[] { FavoriteButton, TrashButton } }; #elif __IOS__ // In iOS, present both stills and Live Photos the same way, because // PHLivePhotoView provides the same gesture-based UI as in Photos app. ToolbarItems = new UIBarButtonItem[] { FavoriteButton, Space, TrashButton }; if (NavigationController != null) { NavigationController.ToolbarHidden = false; } #endif } } void UpdateStillImage() { // Prepare the options to pass when fetching the (photo, or video preview) image. var options = new PHImageRequestOptions { DeliveryMode = PHImageRequestOptionsDeliveryMode.HighQualityFormat, NetworkAccessAllowed = true, ProgressHandler = (double progress, NSError error, out bool stop, NSDictionary info) => { stop = false; // Handler might not be called on the main queue, so re-dispatch for UI work. DispatchQueue.MainQueue.DispatchSync(() => { ProgressView.Progress = (float)progress; }); } }; ProgressView.Hidden = false; PHImageManager.DefaultManager.RequestImageForAsset(Asset, GetTargetSize(), PHImageContentMode.AspectFit, options, (image, info) => { // Hide the progress view now the request has completed. ProgressView.Hidden = true; // If successful, show the image view and display the image. if (image == null) { return; } // Now that we have the image, show it. ImageView.Hidden = false; ImageView.Image = image; }); } void UpdateLivePhoto() { // Prepare the options to pass when fetching the live photo. var options = new PHLivePhotoRequestOptions { DeliveryMode = PHImageRequestOptionsDeliveryMode.HighQualityFormat, NetworkAccessAllowed = true, ProgressHandler = (double progress, NSError error, out bool stop, NSDictionary dictionary) => { stop = false; // Handler might not be called on the main queue, so re-dispatch for UI work. DispatchQueue.MainQueue.DispatchSync(() => ProgressView.Progress = (float)progress); } }; ProgressView.Hidden = false; // Request the live photo for the asset from the default PHImageManager. PHImageManager.DefaultManager.RequestLivePhoto(Asset, GetTargetSize(), PHImageContentMode.AspectFit, options, (livePhoto, info) => { // Hide the progress view now the request has completed. ProgressView.Hidden = true; // If successful, show the live photo view and display the live photo. if (livePhoto == null) { return; } // Now that we have the Live Photo, show it. ImageView.Hidden = true; AnimatedImageView.Hidden = true; LivePhotoView.Hidden = false; LivePhotoView.LivePhoto = livePhoto; // Playback a short section of the live photo; similar to the Photos share sheet. if (!isPlayingHint) { isPlayingHint = true; LivePhotoView.StartPlayback(PHLivePhotoViewPlaybackStyle.Hint); } }); } void UpdateAnimatedImage() { // Prepare the options to pass when fetching the (photo, or video preview) image. var options = new PHImageRequestOptions { DeliveryMode = PHImageRequestOptionsDeliveryMode.HighQualityFormat, Version = PHImageRequestOptionsVersion.Original, NetworkAccessAllowed = true, ProgressHandler = (double progress, NSError error, out bool stop, NSDictionary info) => { stop = false; // Handler might not be called on the main queue, so re-dispatch for UI work. DispatchQueue.MainQueue.DispatchSync(() => { ProgressView.Progress = (float)progress; }); } }; ProgressView.Hidden = false; PHImageManager.DefaultManager.RequestImageData(Asset, options, (data, dataUti, orientation, info) => { // Hide the progress view now the request has completed. ProgressView.Hidden = true; // If successful, show the image view and display the image. if (data == null) { return; } var animatedImage = new AnimatedImage(data); LivePhotoView.Hidden = true; ImageView.Hidden = true; AnimatedImageView.Hidden = false; AnimatedImageView.AnimatedImage = animatedImage; AnimatedImageView.IsPlaying = true; }); } #endregion #region Asset editing void RevertAsset(UIAlertAction action) { PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.ChangeRequest(Asset); request.RevertAssetContentToOriginal(); }, (success, error) => { if (!success) { Console.WriteLine($"can't revert asset: {error.LocalizedDescription}"); } }); } void ApplyFilter(CIFilter filter) { // Set up a handler to make sure we can handle prior edits. var options = new PHContentEditingInputRequestOptions(); options.CanHandleAdjustmentData = (adjustmentData => { return(adjustmentData.FormatIdentifier == formatIdentifier && adjustmentData.FormatVersion == formatVersion); }); // Prepare for editing. Asset.RequestContentEditingInput(options, (input, requestStatusInfo) => { if (input == null) { throw new InvalidProgramException($"can't get content editing input: {requestStatusInfo}"); } // This handler gets called on the main thread; dispatch to a background queue for processing. DispatchQueue.GetGlobalQueue(DispatchQueuePriority.Default).DispatchAsync(() => { // Create a PHAdjustmentData object that describes the filter that was applied. var adjustmentData = new PHAdjustmentData( formatIdentifier, formatVersion, NSData.FromString(filter.Name, NSStringEncoding.UTF8)); // NOTE: // This app's filter UI is fire-and-forget. That is, the user picks a filter, // and the app applies it and outputs the saved asset immediately. There's // no UI state for having chosen but not yet committed an edit. This means // there's no role for reading adjustment data -- you do that to resume // in-progress edits, and this sample app has no notion of "in-progress". // // However, it's still good to write adjustment data so that potential future // versions of the app (or other apps that understand our adjustement data // format) could make use of it. // Create content editing output, write the adjustment data. var output = new PHContentEditingOutput(input) { AdjustmentData = adjustmentData }; // Select a filtering function for the asset's media type. Action <CIFilter, PHContentEditingInput, PHContentEditingOutput, Action> applyFunc; if (Asset.MediaSubtypes.HasFlag(PHAssetMediaSubtype.PhotoLive)) { applyFunc = ApplyLivePhotoFilter; } else if (Asset.MediaType == PHAssetMediaType.Image) { applyFunc = ApplyPhotoFilter; } else { applyFunc = ApplyVideoFilter; } // Apply the filter. applyFunc(filter, input, output, () => { // When rendering is done, commit the edit to the Photos library. PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { var request = PHAssetChangeRequest.ChangeRequest(Asset); request.ContentEditingOutput = output; }, (success, error) => { if (!success) { Console.WriteLine($"can't edit asset: {error.LocalizedDescription}"); } }); }); }); }); } void ApplyPhotoFilter(CIFilter filter, PHContentEditingInput input, PHContentEditingOutput output, Action completion) { // Load the full size image. var inputImage = new CIImage(input.FullSizeImageUrl); if (inputImage == null) { throw new InvalidProgramException("can't load input image to edit"); } // Apply the filter. filter.Image = inputImage.CreateWithOrientation(input.FullSizeImageOrientation); var outputImage = filter.OutputImage; // Write the edited image as a JPEG. // TODO: https://bugzilla.xamarin.com/show_bug.cgi?id=44503 NSError error; if (!ciContext.WriteJpegRepresentation(outputImage, output.RenderedContentUrl, inputImage.ColorSpace, new NSDictionary(), out error)) { throw new InvalidProgramException($"can't apply filter to image: {error.LocalizedDescription}"); } completion(); } void ApplyLivePhotoFilter(CIFilter filter, PHContentEditingInput input, PHContentEditingOutput output, Action completion) { // This app filters assets only for output. In an app that previews // filters while editing, create a livePhotoContext early and reuse it // to render both for previewing and for final output. var livePhotoContext = new PHLivePhotoEditingContext(input); livePhotoContext.FrameProcessor2 = (IPHLivePhotoFrame frame, ref NSError _) => { filter.Image = frame.Image; return(filter.OutputImage); }; livePhotoContext.SaveLivePhoto(output, (PHLivePhotoEditingOption)null, (success, error) => { if (success) { completion(); } else { Console.WriteLine("can't output live photo"); } }); // Applying edits to a Live Photo currently crashes // TODO: https://bugzilla.xamarin.com/show_bug.cgi?id=58227 } void ApplyVideoFilter(CIFilter filter, PHContentEditingInput input, PHContentEditingOutput output, Action completion) { // Load AVAsset to process from input. var avAsset = input.AudiovisualAsset; if (avAsset == null) { throw new InvalidProgramException("can't get AV asset to edit"); } // Set up a video composition to apply the filter. var composition = AVVideoComposition.CreateVideoComposition(avAsset, request => { filter.Image = request.SourceImage; var filtered = filter.OutputImage; request.Finish(filtered, null); }); // Export the video composition to the output URL. var export = new AVAssetExportSession(avAsset, AVAssetExportSessionPreset.HighestQuality) { OutputFileType = AVFileType.QuickTimeMovie, OutputUrl = output.RenderedContentUrl, VideoComposition = composition }; export.ExportAsynchronously(completion); }