Exporting the Composition and Saving it to Camera Roll

// Create a static date formatter so we only have to initialize it once.
static NSDateFormatter *kDateFormatter;
if (!kDateFormatter) {
    kDateFormatter = [[NSDateFormatter alloc] init];
    kDateFormatter.dateStyle = NSDateFormatterMediumStyle;
    kDateFormatter.timeStyle = NSDateFormatterShortStyle;
}
// Create the export session with the composition and set the preset to the highest quality.
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition presetName:AVAssetExportPresetHighestQuality];
// Set the desired output URL for the file created by the export process.
exporter.outputURL = [[[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:@YES error:nil] URLByAppendingPathComponent:[kDateFormatter stringFromDate:[NSDate date]]] URLByAppendingPathExtension:CFBridgingRelease(UTTypeCopyPreferredTagWithClass((CFStringRef)AVFileTypeQuickTimeMovie, kUTTagClassFilenameExtension))];
// Set the output file type to be a QuickTime movie.
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
exporter.videoComposition = mutableVideoComposition;
// Asynchronously export the composition to a video file and save this file to the camera roll once export completes.
[exporter exportAsynchronouslyWithCompletionHandler:^{
    dispatch_async(dispatch_get_main_queue(), ^{
        if (exporter.status == AVAssetExportSessionStatusCompleted) {
            ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
            if ([assetsLibrary videoAtPathIsCompatibleWithSavedPhotosAlbum:exporter.outputURL]) {
                [assetsLibrary writeVideoAtPathToSavedPhotosAlbum:exporter.outputURL completionBlock:NULL];
            }
        }
    });
}];

ref: Link Here

MarkDown 页内跳转

定义锚点

<span id="anchor1">Hello World</span>

使用锚点跳转

[It will jump to anchor1](#anchor1)

Xcode 头文件搜索路径设置

User Header Search Paths = $(SRCROOT)    // recursive
Always Search User Paths = YES

NSNotification 的同步运行

相同线程

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"viewWillAppear");
    [[NSNotificationCenter defaultCenter] addObserverForName:@"Test_NOTIFY" object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"In Observer: Thread = %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:5];
    }];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"Test_NOTIFY" object:self];
    
    NSLog(@"viewDidLoad Finished");
}

不同线程

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"viewWillAppear");
    [[NSNotificationCenter defaultCenter] addObserverForName:@"Test_NOTIFY" object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"In Observer: Thread = %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:5];
    }];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"post: Thread = %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"Test_NOTIFY" object:self];
        NSLog(@"Post thread Finished");
    });
    
    NSLog(@"viewDidLoad finished");
}

异步还是用NSNotificationQueue吧

iOS 滑动调节音量和亮度

typedef NS_ENUM(NSInteger, MyPanType) {
    MyPanTypeNone,
    MyPanTypeBrightness,
    MyPanTypeVolume,
};

- (void)initGestureProcessor {
    _currentPanType = MyPanTypeNone;
    
    _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognized:)];
    [_panGestureRecognizer setMinimumNumberOfTouches:1];
    [_panGestureRecognizer setMaximumNumberOfTouches:1];
    _panGestureRecognizer.delegate = self;
    [self.view addGestureRecognizer:_panGestureRecognizer];
}


- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if (touch.view != self.view)
        return NO;
    return YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

- (MyPanType)detectPanTypeForPan:(UIPanGestureRecognizer *)panRecognizer {
    CGPoint location = [panRecognizer locationInView:self.view];
    CGFloat position = location.x;
    CGFloat viewWidth = 0.0;
    viewWidth = self.view.frame.size.width;
    
    MyPanType panType = MyPanTypeVolume;
    if (position < viewWidth / 2)
        panType = MyPanTypeBrightness;
    
    return panType;
}

- (void)panGestureRecognized:(UIPanGestureRecognizer *)panGestureRecognizer {
    CGFloat panDirectionY = [panGestureRecognizer velocityInView:self.view].y;
    
    if (panGestureRecognizer.state == UIGestureRecognizerStateBegan)
        _currentPanType = [self detectPanTypeForPan:panGestureRecognizer];
    
    if (_currentPanType == MyPanTypeVolume) {
        MPMusicPlayerController *musicPlayer = [MPMusicPlayerController applicationMusicPlayer];
        if (panDirectionY > 0)
            musicPlayer.volume -= 0.01;
        else
            musicPlayer.volume += 0.01;
        
    } else if (_currentPanType == MyPanTypeBrightness) {
        CGFloat brightness = [UIScreen mainScreen].brightness;
        if (panDirectionY > 0)
            brightness -= 0.01;
        else
            brightness += 0.01;
        
        if (brightness > 1.0)
            brightness += 1.0;
        else if (brightness < 0.0)
            brightness = 0.0;
        [[UIScreen mainScreen] setBrightness:brightness];
    }
    
    if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
        _currentPanType = MyPanTypeNone;
    }
}