Created
September 17, 2021 13:55
-
-
Save Hackmodford/9060579ccab4fd0917061ced474cdb05 to your computer and use it in GitHub Desktop.
MediaPicker CapturePhotoAsync Rotation Fix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IPhotoService | |
{ | |
public Task<Stream> TakePhotoAsync(MediaPickerOptions options = null); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PlatformPhotoService : IPhotoService | |
{ | |
private static UIImagePickerController _picker; | |
public async Task<Stream> TakePhotoAsync(MediaPickerOptions options) | |
{ | |
const UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceType.Camera; | |
var mediaType = UTType.Image; | |
if (!UIImagePickerController.IsSourceTypeAvailable(sourceType)) | |
throw new FeatureNotSupportedException(); | |
if (!UIImagePickerController.AvailableMediaTypes(sourceType).Contains(mediaType)) | |
throw new FeatureNotSupportedException(); | |
await EnsureGrantedAsync<Permissions.Camera>(); | |
var vc = GetCurrentViewController(); | |
_picker = new UIImagePickerController | |
{ | |
SourceType = sourceType, | |
MediaTypes = new string[] {mediaType}, | |
AllowsEditing = false | |
}; | |
if (!string.IsNullOrWhiteSpace(options?.Title)) | |
_picker.Title = options.Title; | |
if (DeviceInfo.Idiom == DeviceIdiom.Tablet && _picker.PopoverPresentationController != null && vc.View != null) | |
_picker.PopoverPresentationController.SourceRect = vc.View.Bounds; | |
var tcs = new TaskCompletionSource<Stream>(_picker); | |
_picker.Delegate = new PhotoPickerDelegate | |
{ | |
CompletedHandler = info => GetImageStream(info, tcs) | |
}; | |
if (_picker.PresentationController != null) | |
{ | |
_picker.PresentationController.Delegate = new PhotoPickerPresentationControllerDelegate | |
{ | |
CompletedHandler = info => GetImageStream(info, tcs) | |
}; | |
} | |
await vc.PresentViewControllerAsync(_picker, true); | |
var result = await tcs.Task; | |
await vc.DismissViewControllerAsync(true); | |
_picker?.Dispose(); | |
_picker = null; | |
return result; | |
} | |
static void GetImageStream(NSDictionary info, TaskCompletionSource<Stream> tcs) | |
{ | |
try | |
{ | |
tcs.TrySetResult(DictionaryToStream(info)); | |
} | |
catch (Exception ex) | |
{ | |
tcs.TrySetException(ex); | |
} | |
} | |
static Stream DictionaryToStream(NSDictionary info) | |
{ | |
var img = info.ValueForKey(UIImagePickerController.OriginalImage) as UIImage; | |
img = img.NormalizeOrientation(); | |
var data = img?.AsPNG(); | |
return data?.AsStream(); | |
} | |
class PhotoPickerDelegate : UIImagePickerControllerDelegate | |
{ | |
public Action<NSDictionary> CompletedHandler { get; set; } | |
public override void FinishedPickingMedia(UIImagePickerController picker, NSDictionary info) => | |
CompletedHandler?.Invoke(info); | |
public override void Canceled(UIImagePickerController picker) => | |
CompletedHandler?.Invoke(null); | |
} | |
class PhotoPickerPresentationControllerDelegate : UIAdaptivePresentationControllerDelegate | |
{ | |
public Action<NSDictionary> CompletedHandler { get; set; } | |
public override void DidDismiss(UIPresentationController presentationController) => | |
CompletedHandler?.Invoke(null); | |
} | |
internal static UIViewController GetCurrentViewController(bool throwIfNull = true) | |
{ | |
UIViewController viewController = null; | |
var window = UIApplication.SharedApplication.KeyWindow; | |
if (window != null && window.WindowLevel == UIWindowLevel.Normal) | |
viewController = window.RootViewController; | |
if (viewController == null) | |
{ | |
window = UIApplication.SharedApplication | |
.Windows | |
.OrderByDescending(w => w.WindowLevel) | |
.FirstOrDefault(w => w.RootViewController != null && w.WindowLevel == UIWindowLevel.Normal); | |
if (window == null && throwIfNull) | |
throw new InvalidOperationException("Could not find current view controller."); | |
else | |
viewController = window?.RootViewController; | |
} | |
while (viewController?.PresentedViewController != null) | |
viewController = viewController.PresentedViewController; | |
if (throwIfNull && viewController == null) | |
throw new InvalidOperationException("Could not find current view controller."); | |
return viewController; | |
} | |
internal static bool HasOSVersion(int major, int minor) => | |
UIDevice.CurrentDevice.CheckSystemVersion(major, minor); | |
internal static async Task EnsureGrantedAsync<TPermission>() | |
where TPermission : Permissions.BasePermission, new() | |
{ | |
var status = await Permissions.RequestAsync<TPermission>(); | |
if (status != PermissionStatus.Granted) | |
throw new PermissionException($"{typeof(TPermission).Name} permission was not granted: {status}"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
NormalizeOrientation
xamarin/Essentials#1562 (comment)