How do I filter by media subtype using NSPredicate
with PHFetchOptions
?
I'm trying to exclude slow mo (high frame rate) and time lapse videos. I keep getting strange results when I try to use the predicate
field of PHFetchOptions
.
My phone has a bunch (120+) regular videos, and one slow mo video. When I run the example from Apple's docs, I get the correct result back: 1 slow mo video.
PHFetchOptions *options = [PHFetchOptions new];
options.predicate = [NSPredicate predicateWithFormat:@"(mediaSubtype & %d) != 0 || (mediaSubtype & %d) != 0", PHAssetMediaSubtypeVideoTimelapse, PHAssetMediaSubtypeVideoHighFrameRate];
But I'm trying to exclude slow mo, rather than select it. However if I negate the filter condition, I get zero results back:
options.predicate = [NSPredicate predicateWithFormat:@"(mediaSubtype & %d) == 0", PHAssetMediaSubtypeVideoHighFrameRate];
<PHFetchResult: 0x1702a6660> count=0
Confusingly, the Apple docs list the name of the field as mediaSubtypes
(with an "s"), while their sample predicate is filtering on mediaSubtype
(without an "s").
Trying to filter on mediaSubtypes
produces an error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't do bit operators on non-numbers'
Has anyone been able to make heads or tails of this predicate?
First of all, let me say about the s
in mediaSubtypes
available on PHAsset
and only Photos team at Apple knows why and how mediaSubtype
works without an s
.
@property (nonatomic, assign, readonly) PHAssetMediaType mediaType;
@property (nonatomic, assign, readonly) PHAssetMediaSubtype mediaSubtypes;
Now let me try to explain why it doesn't work as expected. Here's how you compare using NS_OPTIONS or any other bitwise operation.
PHAsset* asset = // my asset;
if((asset.mediaSubtypes & PHAssetMediaSubtypePhotoScreenshot) == PHAssetMediaSubtypePhotoScreenshot) {
// This is a screenshot
}
Here's how you would compare to see if asset is not a screenshot-
PHAsset* asset = // my asset;
if(!((asset.mediaSubtypes & PHAssetMediaSubtypePhotoScreenshot) == PHAssetMediaSubtypePhotoScreenshot)) {
// This is not a screenshot
}
Now in your case the predicate should be-
Fetch all videos with high frame rate
[NSPredicate predicateWithFormat:@"((mediaSubtype & %d) == %d)", PHAssetMediaSubtypeVideoHighFrameRate, PHAssetMediaSubtypeVideoHighFrameRate];
Fetch all videos without high frame rate
[NSPredicate predicateWithFormat:@"!((mediaSubtype & %d) == %d)", PHAssetMediaSubtypeVideoHighFrameRate, PHAssetMediaSubtypeVideoHighFrameRate];
This works beautifully in my case. Hope it helps somebody else.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With