chourobin
6/20/2017 - 4:23 AM

Awesome optimized AVPlayer for smooth scrolling AVPlayerLayer inside UICollectionView/UITableView (tested on iOS10+)

Awesome optimized AVPlayer for smooth scrolling AVPlayerLayer inside UICollectionView/UITableView (tested on iOS10+)

#import <JRSwizzle/JRSwizzle.h>

#import "MLWAsyncAVPlayer.h"

@implementation MLWAsyncAVPlayer

- (instancetype)init {
    self = [super init];
    if (self) {
        [self setValue:dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL) forKeyPath:@"_player.stateDispatchQueue"];
    }
    return self;
}

// Move completions from following methods to main thread

- (void)prerollAtRate:(float)rate completionHandler:(void (^)(BOOL))completionHandler {
    [super prerollAtRate:rate completionHandler:^(BOOL finished) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            completionHandler(finished);
        });
    }];
}

- (id)addBoundaryTimeObserverForTimes:(NSArray<NSValue *> *)times queue:(dispatch_queue_t)queue usingBlock:(void (^)(void))block {
    return [super addBoundaryTimeObserverForTimes:times queue:queue usingBlock:^{
        dispatch_sync(dispatch_get_main_queue(), ^{
            block();
        });
    }];
}

- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime))block {
    return [super addPeriodicTimeObserverForInterval:interval queue:queue usingBlock:^(CMTime time) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            block(time);
        });
    }];
}

// Move KVO of player properties to main thread

- (void)willChangeValueForKey:(NSString *)key {
    if ([NSThread isMainThread]) {
        [super willChangeValueForKey:key];
        return;
    }

    dispatch_sync(dispatch_get_main_queue(), ^{
        [super willChangeValueForKey:key];
    });
}

- (void)didChangeValueForKey:(NSString *)key {
    if ([NSThread isMainThread]) {
        [super didChangeValueForKey:key];
        return;
    }

    dispatch_sync(dispatch_get_main_queue(), ^{
        [super didChangeValueForKey:key];
    });
}

@end
    
//
    
@implementation AVPlayerItem (MLWAsync)

+ (void)load {
    [self jr_swizzleMethod:@selector(willChangeValueForKey:) withMethod:@selector(mlw_willChangeValueForKey:) error:nil];
    [self jr_swizzleMethod:@selector(didChangeValueForKey:) withMethod:@selector(mlw_didChangeValueForKey:) error:nil];
}

- (void)mlw_willChangeValueForKey:(NSString *)key {
    if ([NSThread isMainThread]) {
        [self mlw_willChangeValueForKey:key];
        return;
    }

    dispatch_sync(dispatch_get_main_queue(), ^{
        [self mlw_willChangeValueForKey:key];
    });
}

- (void)mlw_didChangeValueForKey:(NSString *)key {
    if ([NSThread isMainThread]) {
        [self mlw_didChangeValueForKey:key];
        return;
    }

    dispatch_sync(dispatch_get_main_queue(), ^{
        [self mlw_didChangeValueForKey:key];
    });
}

@end
#import <AVFoundation/AVFoundation.h>

@interface MLWAsyncAVPlayer : AVPlayer

@end