diff --git a/Modules/Sources/WPMediaPicker/WPImageExporter.m b/Modules/Sources/WPMediaPicker/WPImageExporter.m index e635c57aa78..546c2657226 100644 --- a/Modules/Sources/WPMediaPicker/WPImageExporter.m +++ b/Modules/Sources/WPMediaPicker/WPImageExporter.m @@ -2,6 +2,7 @@ @import MobileCoreServices; @import ImageIO; +@import UniformTypeIdentifiers; @implementation WPImageExporter @@ -33,7 +34,7 @@ + (BOOL)writeImage:(UIImage *)image withMetadata:(NSDictionary *)metadata toURL: adjustedMetadata[(NSString *)kCGImagePropertyIPTCDictionary] = adjustedIPTC; } - CGImageDestinationRef destination = CGImageDestinationCreateWithURL((CFURLRef)fileURL, kUTTypeJPEG, 1, nil); + CGImageDestinationRef destination = CGImageDestinationCreateWithURL((CFURLRef)fileURL, (CFStringRef)UTTypeJPEG.identifier, 1, nil); if (destination == NULL) { return NO; } diff --git a/Modules/Sources/WPMediaPicker/WPInputMediaPickerViewController.m b/Modules/Sources/WPMediaPicker/WPInputMediaPickerViewController.m deleted file mode 100644 index b660fe4b270..00000000000 --- a/Modules/Sources/WPMediaPicker/WPInputMediaPickerViewController.m +++ /dev/null @@ -1,134 +0,0 @@ -#import "WPInputMediaPickerViewController.h" -#import "WPPHAssetDataSource.h" - -@interface WPInputMediaPickerViewController() - -@property (nonatomic, strong) WPMediaPickerViewController *mediaPicker; -@property (nonatomic, strong) UIToolbar *mediaToolbar; -@property (nonatomic, strong) id privateDataSource; - -@end - -@implementation WPInputMediaPickerViewController - -- (instancetype _Nonnull )initWithOptions:(WPMediaPickerOptions *_Nonnull)options { - self = [super initWithNibName:nil bundle:nil]; - if (self) { - _mediaPicker = [[WPMediaPickerViewController alloc] initWithOptions:[options copy]]; - } - return self; -} - -- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (self) { - _mediaPicker = [[WPMediaPickerViewController alloc] initWithOptions:[WPMediaPickerOptions new]]; - } - return self; -} - -- (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - _mediaPicker = [[WPMediaPickerViewController alloc] initWithOptions:[WPMediaPickerOptions new]]; - } - return self; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - [self setupMediaPickerViewController]; -} - -- (void)setupMediaPickerViewController { - self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - - self.privateDataSource = [[WPPHAssetDataSource alloc] init]; - self.mediaPicker.dataSource = self.privateDataSource; - - [self addChildViewController:self.mediaPicker]; - [self overridePickerTraits]; - - self.mediaPicker.view.frame = self.view.bounds; - self.mediaPicker.view.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:self.mediaPicker.view]; - - NSLayoutAnchor *leadingAnchor = self.view.safeAreaLayoutGuide.leadingAnchor; - NSLayoutAnchor *trailingAnchor = self.view.safeAreaLayoutGuide.trailingAnchor; - - [NSLayoutConstraint activateConstraints: - @[ - [self.mediaPicker.view.leadingAnchor constraintEqualToAnchor:leadingAnchor], - [self.mediaPicker.view.trailingAnchor constraintEqualToAnchor:trailingAnchor], - [self.mediaPicker.view.topAnchor constraintEqualToAnchor:self.view.topAnchor], - [self.mediaPicker.view.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], - ] - ]; - - [self.mediaPicker didMoveToParentViewController:self]; - self.view.backgroundColor = [UIColor whiteColor]; - self.mediaToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)]; - self.mediaToolbar.items = @[ - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(mediaCanceled:)], - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(mediaSelected:)] - ]; -} - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection -{ - [super traitCollectionDidChange:previousTraitCollection]; - [self overridePickerTraits]; -} - -- (void)overridePickerTraits -{ - // Due to an inputView being displayed in its own window, the force touch peek transition - // doesn't display correctly. Because of this, we'll disable it for the input picker thus forcing - // long touch to be used instead. - UITraitCollection *traits = [UITraitCollection traitCollectionWithForceTouchCapability:UIForceTouchCapabilityUnavailable]; - [self setOverrideTraitCollection:[UITraitCollection traitCollectionWithTraitsFromCollections:@[self.traitCollection, traits]] forChildViewController:self.mediaPicker]; -} - -#pragma mark - WPMediaCollectionDataSource - -- (void)setDataSource:(id)dataSource { - self.mediaPicker.dataSource = dataSource; -} - -- (id)dataSource { - return self.mediaPicker.dataSource; -} - -#pragma mark - WPMediaPickerViewControllerDelegate - -- (void)setMediaPickerDelegate:(id)mediaPickerDelegate { - self.mediaPicker.mediaPickerDelegate = mediaPickerDelegate; -} - -- (id)mediaPickerDelegate { - return self.mediaPicker.mediaPickerDelegate; -} - -- (void)mediaSelected:(UIBarButtonItem *)sender { - if ([self.mediaPickerDelegate respondsToSelector:@selector(mediaPickerController:didFinishPickingAssets:)]) { - [self.mediaPickerDelegate mediaPickerController:self.mediaPicker didFinishPickingAssets:self.mediaPicker.selectedAssets]; - [self.mediaPicker resetState:NO]; - } - -} - -- (void)mediaCanceled:(UIBarButtonItem *)sender { - if ([self.mediaPickerDelegate respondsToSelector:@selector(mediaPickerControllerDidCancel:)]) { - [self.mediaPickerDelegate mediaPickerControllerDidCancel:self.mediaPicker]; - [self.mediaPicker resetState:NO]; - } -} - -- (void)showCapture -{ - [self.mediaPicker showCapture]; -} - -@end diff --git a/Modules/Sources/WPMediaPicker/WPMediaCapturePresenter.m b/Modules/Sources/WPMediaPicker/WPMediaCapturePresenter.m index 035a00616f3..07b6d78f32c 100644 --- a/Modules/Sources/WPMediaPicker/WPMediaCapturePresenter.m +++ b/Modules/Sources/WPMediaPicker/WPMediaCapturePresenter.m @@ -3,6 +3,7 @@ @import MobileCoreServices; @import AVFoundation; +@import UniformTypeIdentifiers; @interface WPMediaCapturePresenter () @property (nonatomic, strong, nullable) UIViewController *presentingViewController; @@ -79,11 +80,10 @@ - (void)presentCaptureViewController UIImagePickerControllerSourceTypeCamera]]; NSMutableSet *mediaDesired = [NSMutableSet new]; if (self.mediaType & WPMediaTypeImage) { - [mediaDesired addObject:(__bridge NSString *)kUTTypeImage]; + [mediaDesired addObject:UTTypeImage.identifier]; } if (self.mediaType & WPMediaTypeVideo) { - [mediaDesired addObject:(__bridge NSString *)kUTTypeMovie]; - + [mediaDesired addObject:UTTypeMovie.identifier]; } if (mediaDesired.count > 0){ [mediaTypes intersectSet:mediaDesired]; diff --git a/Modules/Sources/WPMediaPicker/WPMediaCapturePreviewCollectionView.m b/Modules/Sources/WPMediaPicker/WPMediaCapturePreviewCollectionView.m index 2e3616133de..6f41ba5797f 100644 --- a/Modules/Sources/WPMediaPicker/WPMediaCapturePreviewCollectionView.m +++ b/Modules/Sources/WPMediaPicker/WPMediaCapturePreviewCollectionView.m @@ -110,8 +110,8 @@ - (void)startCapture CALayer *viewLayer = self.previewView.layer; self.captureVideoPreviewLayer.frame = viewLayer.bounds; self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; - UIWindowScene *currentScene = [[[[UIApplication sharedApplication] windows] lastObject] windowScene]; - self.captureVideoPreviewLayer.connection.videoOrientation = [self videoOrientationForInterfaceOrientation:[currentScene interfaceOrientation]]; + UIWindowScene *currentScene = [self activeWindowScene]; + self.captureVideoPreviewLayer.connection.videoRotationAngle = [self videoRotationAngleForInterfaceOrientation:[currentScene interfaceOrientation]]; [viewLayer addSublayer:self.captureVideoPreviewLayer]; }); } @@ -121,24 +121,39 @@ - (void)startCapture - (void)deviceOrientationDidChange:(NSNotification *)notification { - if (self.captureVideoPreviewLayer.connection.supportsVideoOrientation) { - UIWindowScene *currentScene = [[[[UIApplication sharedApplication] windows] lastObject] windowScene]; - self.captureVideoPreviewLayer.connection.videoOrientation = [self videoOrientationForInterfaceOrientation:[currentScene interfaceOrientation]]; + UIWindowScene *currentScene = [self activeWindowScene]; + self.captureVideoPreviewLayer.connection.videoRotationAngle = [self videoRotationAngleForInterfaceOrientation:[currentScene interfaceOrientation]]; +} + +- (UIWindowScene *)activeWindowScene +{ + for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) { + if ([scene isKindOfClass:[UIWindowScene class]] && scene.activationState == UISceneActivationStateForegroundActive) { + return (UIWindowScene *)scene; + } + } + // Fallback to first window scene if no active one found + for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) { + if ([scene isKindOfClass:[UIWindowScene class]]) { + return (UIWindowScene *)scene; + } } + return nil; } -- (AVCaptureVideoOrientation)videoOrientationForInterfaceOrientation:(UIInterfaceOrientation)orientation +- (CGFloat)videoRotationAngleForInterfaceOrientation:(UIInterfaceOrientation)orientation { switch (orientation) { case UIInterfaceOrientationPortrait: - return AVCaptureVideoOrientationPortrait; + return 90.0; case UIInterfaceOrientationPortraitUpsideDown: - return AVCaptureVideoOrientationPortraitUpsideDown; + return 270.0; case UIInterfaceOrientationLandscapeLeft: - return AVCaptureVideoOrientationLandscapeLeft; + return 180.0; case UIInterfaceOrientationLandscapeRight: - return AVCaptureVideoOrientationLandscapeRight; - default:return AVCaptureVideoOrientationPortrait; + return 0.0; + default: + return 90.0; } } diff --git a/Modules/Sources/WPMediaPicker/WPMediaPickerViewController.m b/Modules/Sources/WPMediaPicker/WPMediaPickerViewController.m index c8c05744638..7bdb1517af7 100644 --- a/Modules/Sources/WPMediaPicker/WPMediaPickerViewController.m +++ b/Modules/Sources/WPMediaPicker/WPMediaPickerViewController.m @@ -5,12 +5,12 @@ #import "WPMediaGroupPickerViewController.h" #import "WPPHAssetDataSource.h" #import "WPMediaCapturePresenter.h" -#import "WPInputMediaPickerViewController.h" #import "WPCarouselAssetsViewController.h" #import "UIViewController+MediaAdditions.h" @import MobileCoreServices; @import AVFoundation; +@import UniformTypeIdentifiers; static CGFloat const IPhoneSELandscapeWidth = 568.0f; static CGFloat const IPhone7PortraitWidth = 375.0f; @@ -114,7 +114,8 @@ - (void)viewDidLoad [self.view addGestureRecognizer:self.longPressGestureRecognizer]; self.layout.sectionInsetReference = UICollectionViewFlowLayoutSectionInsetFromSafeArea; - + [self registerForTraitObservation]; + [self refreshDataAnimated:NO]; } @@ -317,18 +318,6 @@ - (void)viewDidDisappear:(BOOL)animated [self unregisterForKeyboardNotifications]; } -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection -{ - [super traitCollectionDidChange:previousTraitCollection]; - - if ([self shouldShowCustomHeaderView]) { - // If there's a custom header, we'll invalidate it so that it can adapt itself to dynamic type changes. - UICollectionViewFlowLayoutInvalidationContext *context = [UICollectionViewFlowLayoutInvalidationContext new]; - [context invalidateSupplementaryElementsOfKind:UICollectionElementKindSectionHeader atIndexPaths:@[ [NSIndexPath indexPathForRow:0 inSection:0] ]]; - [self.collectionView.collectionViewLayout invalidateLayout]; - } -} - - (UIViewController *)viewControllerToUseToPresent { // viewControllerToUseToPresent defaults to self but could be set to nil. Reset to self if needed. @@ -381,7 +370,6 @@ - (void)addCollectionViewToView - (void)setupSearchBar { BOOL shouldShowSearchBar = self.options.showSearchBar && - ![self.parentViewController isKindOfClass:[WPInputMediaPickerViewController class]] && //Disable search bar on WPInputMediaPicker [self.dataSource respondsToSelector:@selector(searchFor:)]; if (shouldShowSearchBar && self.searchBar == nil) { @@ -999,7 +987,8 @@ - (void)configureBadgeViewForCell:(WPMediaCollectionViewCell *)cell withAsset:(i NSString *uttype = [asset UTTypeIdentifier]; if ([self.options.badgedUTTypes containsObject:uttype]) { - NSString *tagName = (__bridge_transfer NSString *)(UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)uttype, kUTTagClassFilenameExtension)); + UTType *type = [UTType typeWithIdentifier:uttype]; + NSString *tagName = type.preferredFilenameExtension; cell.badgeView.label.text = [tagName uppercaseString]; cell.badgeView.hidden = NO; return; @@ -1274,12 +1263,12 @@ - (void)processMediaCaptured:(NSDictionary *)info } [self addMedia:media animated:YES]; }; - if ([info[UIImagePickerControllerMediaType] isEqual:(NSString *)kUTTypeImage]) { + if ([info[UIImagePickerControllerMediaType] isEqual:UTTypeImage.identifier]) { UIImage *image = (UIImage *)info[UIImagePickerControllerOriginalImage]; [self.dataSource addImage:image metadata:info[UIImagePickerControllerMediaMetadata] completionBlock:completionBlock]; - } else if ([info[UIImagePickerControllerMediaType] isEqual:(NSString *)kUTTypeMovie]) { + } else if ([info[UIImagePickerControllerMediaType] isEqual:UTTypeMovie.identifier]) { [self.dataSource addVideoFromURL:info[UIImagePickerControllerMediaURL] completionBlock:completionBlock]; } } @@ -1466,20 +1455,30 @@ - (BOOL)isPresentedAsPopover return NO; } +- (void)registerForTraitObservation +{ + __weak typeof(self) weakSelf = self; + [self registerForTraitChanges:@[UITraitPreferredContentSizeCategory.class] + withHandler:^(id traitEnvironment, UITraitCollection *previousCollection) { + if ([weakSelf shouldShowCustomHeaderView]) { + // If there's a custom header, invalidate it so that it can adapt itself to dynamic type changes. + UICollectionViewFlowLayoutInvalidationContext *context = [UICollectionViewFlowLayoutInvalidationContext new]; + [context invalidateSupplementaryElementsOfKind:UICollectionElementKindSectionHeader atIndexPaths:@[ [NSIndexPath indexPathForRow:0 inSection:0] ]]; + [weakSelf.collectionView.collectionViewLayout invalidateLayout]; + } + }]; +} + - (void)registerForKeyboardNotifications { - if (![self.parentViewController isKindOfClass:[WPInputMediaPickerViewController class]]) { - [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardWillShowNotification:) name:UIKeyboardWillShowNotification object:nil]; - [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardWillHideNotification:) name:UIKeyboardWillHideNotification object:nil]; - } + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardWillShowNotification:) name:UIKeyboardWillShowNotification object:nil]; + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardWillHideNotification:) name:UIKeyboardWillHideNotification object:nil]; } - (void)unregisterForKeyboardNotifications { - if (![self.parentViewController isKindOfClass:[WPInputMediaPickerViewController class]]) { - [NSNotificationCenter.defaultCenter removeObserver:self name:UIKeyboardWillShowNotification object:nil]; - [NSNotificationCenter.defaultCenter removeObserver:self name:UIKeyboardWillHideNotification object:nil]; - } + [NSNotificationCenter.defaultCenter removeObserver:self name:UIKeyboardWillShowNotification object:nil]; + [NSNotificationCenter.defaultCenter removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } - (void)keyboardWillShowNotification:(NSNotification *)notification diff --git a/Modules/Sources/WPMediaPicker/include/WPInputMediaPickerViewController.h b/Modules/Sources/WPMediaPicker/include/WPInputMediaPickerViewController.h deleted file mode 100644 index 9693f637dc7..00000000000 --- a/Modules/Sources/WPMediaPicker/include/WPInputMediaPickerViewController.h +++ /dev/null @@ -1,48 +0,0 @@ -#import -#import "WPMediaPickerViewController.h" - - -/** - A class to be used as an input view for an UITextView or UITextField. - - The mediaToolbar property provides a toolbar that can be used as the inputAccessoryView for this inputView. - */ -@interface WPInputMediaPickerViewController : UIViewController - -/** - Init a WPInputMediaPickerViewController with the selection options - - @param options an WPMediaPickerOption object - @return an initiated WPInputMediaPickerViewController with the designated options - */ -- (instancetype _Nonnull )initWithOptions:(nonnull WPMediaPickerOptions *)options; - -/** -The delegate for the WPMediaPickerViewController events -*/ -@property (nonatomic, weak, nullable) id mediaPickerDelegate; - -/** - The object that acts as the data source of the media picker. - - @Discussion - If no object is defined before the picker is show then the picker will use a shared data source that access the user media library. - */ -@property (nonatomic, weak, nullable) id dataSource; - -/** - The internal WPMediaPickerViewController that is used to display the media. - */ -@property (nonatomic, readonly, nonnull) WPMediaPickerViewController *mediaPicker; - -/** - A toolbar that can be used as the inputAccessoryView for this inputView. - */ -@property (nonatomic, readonly, nonnull) UIToolbar *mediaToolbar; - -/** - * Presents the system image / video capture view controller, presented from `viewControllerToUseToPresent`. - */ -- (void)showCapture; - -@end